Mavericks tool time - SIU and imagetool

Recently I’ve had to rebuild our customized NBI NetBoot images a number of times due to special OS builds (yay) and needing to test Mavericks DPs. In that process it became obvious that it’s easy to make a mistake adding certain resources, deleting others and making sure that the resulting DMG is resized afterwards. I don’t know about you, but if I have to repeatedly and manually run a bunch of error-prone steps my mind quickly turns towards automating the heck out of it to regain sanity and remove error.

Wanting to cut my teeth on some more Python I figured I would seize this particular itch to do that. Having been generally happy with System Image Utility (SIU) my first approach was to create an SIU workflow and then figure out a way to execute it from Python. This, I surmised, required tapping the power of the Automator framework with PyObjc through things like AMWorkflow.runWorkflowAtURL_withInput_error_(). Not the easiest for a first PyObjc project, admittedly. After some scary times wandering in the PyObjc desert kicking the Automator Framework around and not making a lot of headway I decided to see if perhaps System Image Utility had a CLI mode I could subvert for my needs. Thus I stumbled upon ‘/System/Library/CoreServices/System Image Utility.app/Contents/MacOS/imagetool’ which I didn’t remember seeing before and indeed appears to be new in Mavericks.

Long story short: imagetool is a CLI tool that can generate NetBoot/Install/Restore NBI bundles ready for deployment as well as regular bootable installer media. Since we already have the “createinstallmedia” tool contained in the Install OS X Mavericks.app bundle (written up so very nicely by Rusty Meyers) I will not go into the -createmedia (-c) function here as it does the same thing.

The usage info for imagetool is as follows:

/System/Library/CoreServices/System\ Image\ Utility.app/Contents/MacOS/imagetool

Usage: imagetool <options>
  where <options> are:
   -c | --createmedia -s <path_to_install_app> -d <path_to_volume>
  or:
   -p | --plist <path> path to a property list containing the build specifications
 and/or:
   [--netboot | --netinstall | --netrestore] image kind (required)
   -d | --destination <path> path to save the image (required)
   -s | --source <mountpoint> mountpoint of source volume or path to Install Mac OS X
        application (required)
   -n | --name <string> name of image (required)
   -i | --index <integer> image index (0-65535) (required)

   -h | --help this help

The help text goes on to say:

To create a property list, create a custom workflow in System Image Utility.
Add any customized steps you wish the workflow to perform.
Hold down the option key when clicking the run button in the workflow.
This will save the workflow detail to the specified location instead of executing it.

While it would be perfectly fine to feed imagetool some CLI parameters the -plist option intrigued me. I tested the “option-click Run to save” (so intuitive, Apple) in SIU and it does indeed save a plist file, different in structure from the usual .wflow document.

SIU_custom
SIU_saveplist
Upon opening the file we see a pretty straightforward set of configuration items:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>imageDescription</key>
	<string>NetInstall of OS X 10.9 (13A603) Install (7.14 GB)</string>
	<key>imageIndex</key>
	<integer>1621</integer>
	<key>imageName</key>
	<string>NetInstall of Install OS X Mavericks</string>
	<key>installType</key>
	<string>netinstall</string>
	<key>nbiLocation</key>
	<string>/Path/To/NetInstall of Install OS X Mavericks</string>
	<key>sourcesList</key>
	<array>
		<dict>
			<key>dmgPath</key>
			<string>/Path/To/InstallESD.dmg</string>
			<key>isInstallMedia</key>
			<true/>
			<key>kindOfSource</key>
			<integer>1</integer>
			<key>sourceType</key>
			<string>ESDApplication</string>
			<key>volumePath</key>
			<string>/Path/To//Install OS X Mavericks.app</string>
		</dict>
	</array>
	----SNIP----

These are all as one would expect to see - image description, name, index as well as the location it will be written out to and source(s) of the InstallESD.dmg to use. If the “Add Packages and Post-Install Scripts” workflow item is included its settings are recorded as follows:

<key>packageList</key>
	<array>
		<string>/Users/admin/munkitools-0.9.0.1803.0.mpkg</string>
		<string>/Users/admin/CleanImage-1.0.pkg</string>
	</array>
	<key>scriptList</key>
	<array/>

In addition to the above options the .plist contains a lengthy “userConfigurationOptions” dict containing a “groupID” and “userID” key which are set to that of the user running SIU. A third key named “siuPrefs” (containing a dict of other strings, integers, arrays and dicts) appears to contain a few SIU-specific keys but also many settings originating from the user creating the NetBoot image. In my testing these keys contained ColorSync profile settings, Text Replacement configs and even configurations for third party applications.

The SIU-related keys are:

<key>addlNetBootMbytes</key>
<integer>400</integer>
<key>asr_blockCopyVolume</key>
<true/>
<key>asr_displayCountdown</key>
<false/>
<key>asr_dontReorderForMulticast</key>
<false/>
<key>asr_retainOriginalVolumeName</key>
<true/>
<key>consumeSuppliedImage</key>
<false/>
<key>createEnabledImages</key>
<true/>
<key>createSparseImages</key>
<false/>
<key>enableInternalLogging</key>
<false/>
<key>installToImageDiskFormat</key>
<string>HFS+J</string>
<key>kSIUWorkflowDirectoryKey</key>
<string>/Users/admin/Documents</string>
<key>lastImageLocation</key>
<string>/Users/admin/Desktop</string>
<key>liveUpdateInstallerSearch</key>
<true/>
<key>restoreImageFormat</key>
<string>UDZO</string>

Since I didn’t want to retain most of the user settings I removed the entire “userConfigurationOptions” dict as a test and subsequent images built and booted without a problem. Taking all of the above into account it is therefore fairly straightforward to put together one or more .plist files that can be fed to imagetool with an invocation like this:

sudo '/System/Library/CoreServices/System Image Utility.app/Contents/MacOS/imagetool' --plist '/Users/admin/Documents/NetRestore Template.plist' --index 3000

This will tell imagetool to use the configuration in “NetRestore Template.plist”, overriding the “imageIndex” key in the plist and substituting it with “3000”. Note that while my example uses Mavericks as an installer source I have been able to verify that substituting a Mountain Lion (10.8) or Lion (10.7) installer app and InstallESD.dmg also works without a hitch and the resulting NBIs were all bootable.

As you can see, some interesting things are possible with this entirely CLI-driven process. I will be posting a tool written in Python in the next few days that leverages imagetool to automate the creation and processing of plists to generate and modify NetBoot images for use in mass deployments of OS X.