Extracting Adobe CC icons for Munki

Generally, Munki has no problems extracting icons from .app or .pkg items it imports. Adobe makes its installers a bit annoying to get icons out of, so when you import an Adobe CC item into Munki and try to get the icon out, you'll get this instead:

Attempt to create a product icon? [y/n] y
Attempting to extract and upload icon...
Can't generate icons from installer_type: AdobeCCPInstaller.

With some idea help from some people on the MacAdmins Slack team, I cobbled together a script that extracts the Adobe CC icons. The only bummer is that you have to actually install the Creative Cloud items on to a client machine first and then run the script on the client machine to get the icons.

Using Plistbuddy to delete a string from an array

I have two previous posts about using Plistbuddy to add to an array in a .plist file (specifically Munki's SelfServeManifest file):
Bash script to add optional installs for Munki
Terminal command to mark a Munki optional install for installation

What if you want to remove an item from the array? It's not a simple thing to do. One option you have is to read the entire array, remove the offending entry, and then write back the modified array.

The most straightforward (but still roundabout) way I could think to do it is to find the index of the offending entry and then remove the entry by index number. Here's an example:

# See if Acrobat DC is set to be installed in the self-serve manifest
acrobatDC=$(/usr/libexec/Plistbuddy -c "Print managed_installs" /Library/Managed\ Installs/manifests/SelfServeManifest | grep -n "AdobeAcrobatDC" | awk -F ":" '{print $1}')

if [ ! -z "$acrobatDC" ]; then
# Item to delete is the number minus two
itemToDelete=$(($acrobatDC-2))

sudo /usr/libexec/Plistbuddy -c "Delete :managed_installs:$itemToDelete" /Library/Managed\ Installs/manifests/SelfServeManifest

fi
The first command puts into a variable the output of reading the managed_installs after finding out what line number the offending entry is on. Since it prints the word "Array {" on the first line and since the indexing starts at zero, the third line would be index 1, the fourth line would be index 2, etc.

So, assuming the variable is not empty, the item to delete is the line number minus 2, and then we can remove it by that index number.

Bash script to add optional installs for Munki

In Terminal command to mark a Munki optional install for installation, I showed a one-off command to mark a Munki optional install item for installation.

What if you want to do it in bulk, though? For example, if you're scripting a thin image to install some software by default, but you want the option for your users to uninstall the software later (i.e., you don't want the items to be managed install items).

I wrote a little script that will add various items of your choice to a the client machine's SelfServeManifest.

The syntax gets a bit tricky with bash variable expansion and single quotes, so I did a bit of a hack to get it working (created a temporary variable to store the source variable surrounded by single quotes).

Scripting enabling root or disabling root on Mac OS X

If you want to script enabling or disabling the root account in Mac OS X (instead of having to go to Directory Utility to do it), you'll want to use the dsenableroot command.

Enable the root user

dsenableroot -u adminaccount -p adminaccountpassword -r rootpasswordyouwant

Disable the root user

dsenableroot -d -u adminaccount -p adminaccountpassword

Important Notes

Just keep in mind that you have to use the syntax as given. You cannot bypass giving an actual username and password of an admin account by being logged into that admin account and prefixing the command with sudo.

Any commands you put into the terminal on a computer, by default, will go into that user's .bash_history file stored as plain text. So if it's one-off on a local computer, be sure to edit that file later and delete that line. If you're using some other remote way to deploy the command, make sure it's being sent over a secure (and protected) connection of some sort.

Managing Mac admin user accounts with CreateUserPkg

A lot of Mac admins are ditching "the golden image" (or "monolithic image") for more thin-imaging + scripting, managing accounts with scripts and packages. Frankly, though, even if you aren't using AutoDMG (here at SI, we use a combination of "silver image" or "bronze image" that gets subsequently updated with Munki), you may still want to manage accounts through scripts and packages.

A helpful tool for creating and updating user accounts is CreateUserPkg. If you download the latest .dmg (not .zip of the GitHub source code), it has an .app that's basically a one-window wizard to create a user account. Most of it is straightforward.

Some things to keep in mind, though:

  1. If you want to create multiple user accounts, close out CreateUserPkg and re-launch it for the subsequent user accounts. Otherwise, the same UUID will be assigned to each user. Alternatively, if you're too lazy to re-launch the app and yet still have enough energy to launch up a Terminal.app instance, then you can use the /usr/bin/uuidgen command to generate new UUIDs and then paste them in.
  2. By default, CreateUserPkg will make the User ID 499. If you want to use the Hide500Users option (scroll to the bottom of the link page) to hide users with User IDs under 500, that may be handy (and then you can also use 498, 497, etc.). Just keep in mind that if you're using a Guest account, that (by default) also has a User ID under 500, so that also would be hidden if you choose to hide accounts that way instead of using the IsHidden option (same page but at the top).
  3. If you want to check if User ID already exist before creating them, you may want to run something like dscl . -list /Users UniqueID | grep -e '499' -e '498' to check first.
  4. If you want to update a password, create a new .pkg with CreateUserPkg, but make sure the User ID remains the same.
  5. If you want to deploy this with Munki on computers that had accounts set up not with CreateUserPkg, you may want to use this preinstall_script with your Munki item.
  6. CreateUserPkg comes with a warning about encryption (it uses a SHA1 hash, which later gets "converted to PBKDF2 upon first login"), so you may want to make sure those .pkg files you create a safely guarded when not being deployed.

Using duti to script default applications for Macs

Back story

It was surprisingly difficult for me to find a straightforward tutorial on how to script changing the default applications for Macs. There are great screenshot-laden tutorials on how to change default applications the point-and-click way for one user (right-click the file, Get Info, Open With, select application, Change All, Continue). And then there are vague mentions of how complicated lsregister is. Eventually I found a semi-consensus in the Mac Admin communities about using duti to change default applications for users, but I still couldn't find a simple tutorial on how to get it up and running.

I thought the logical thing to do would be to go to the downloads page for duti and then follow the instructions (which involve compiling it from source). For some reason, I couldn't get the ./configure to work.

In case anyone else is frustrated in a similar way, the tutorial below goes out to you.

Get duti

Make sure you have Homebrew set up:

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
and then use Homebrew to install duti:
brew install duti
This will install a binary of duti to /usr/local/Cellar/duti/#.#.#/bin/duti (#.#.# as of this writing is actually 1.5.3). Once you have this binary, you can copy it to somewhere else, and it'll run just fine there—it's a self-contained executable.

For the sake of simplicity, I would recommend moving it to /usr/local/bin instead, so the path to it would be /usr/local/bin/duti instead of that long Cellar path.

Creating a duti configuration file

Apparently, there are three forms duti configuration can take (including a specially-formatted XML file), but for starters I found the straight tab-delimited text file to be the easiest.

Here's an example of a text file (which I called AudioVideo.duti) to make VLC the default media player for a bunch of audio and video types:

# bundle id UTI role
org.videolan.vlc avi viewer
org.videolan.vlc flac viewer
org.videolan.vlc flv viewer
org.videolan.vlc m4a viewer
org.videolan.vlc mkv viewer
org.videolan.vlc mov viewer
org.videolan.vlc mp3 viewer
org.videolan.vlc mp4 viewer
org.videolan.vlc mpg viewer
org.videolan.vlc wav viewer
org.videolan.vlc wmv viewer
Please remember to include a carriage return at the end of the last line of the file; otherwise, duti will complain that the last line is too long.

You can find the bundle identifier by right-clicking the .app, selecting Show Package Contents and then going to Contents/Info.plist. Look for CFBundleIdentifier.

Here's an example, too, of how to use duti to Set Outlook 2016 as default without having to open Mail.app.

Create a script that scans the configuration file(s)

As far as I can tell, duti changes defaults on a per-user basis and not system-wide. So the best thing I've found is to use a login script (which, for this example, is called RescanDuti.sh) to scan the settings to be applied:

#!/bin/bash

# Delay start for a few seconds. If it runs too early, the change won't register.
sleep 5

# Run the command to register the changes
/usr/local/bin/duti /Library/Application\ Support/duti
The first command (to pause for 5 seconds) is optional. The change actually does register... if the user already exists. But we run a lot of our computers bound to the domain, so if a user logs into a random computer, she may not already have an existing user profile on that Mac. Based on a few tests I've done with user profiles being created, it seems about 5 seconds is a good delay to make sure the user is logged in before scanning for duti configuration files.

The next command just runs duti and tells it to scan a certain directory for any configuration files. I put the AudioVideo.duti file in the /Library/Application Support/duti folder. If I had .duti files for text files and web browser files, I'd also put those in the same /Library/Application Support/duti folder to be scanned.

Create a Launch Agent to invoke the script at login

Don't bother making your own Launch Agent. Just make the duti rescan a login-every script for outset.

Distribute the executable, Launch Agent, and script

I'm assuming if you want to script this that you have ways you're already familiar with to distribute these things (Munki, Casper, Puppet, ARD). My preferred method in a case like this is to use Packages to distribute the payloads to their respective places (dockutil to /usr/local/bin, AudioVideo.duti to /Library/Application Support/duti). Then I use Munki to distribute it to client machines.

Further reading

Read duti's official documentation for more details about different commands you can use and the XML format if you prefer that to a plain old text file.