Deploying Xcode – The Trick With Accepting License Agreements

If you’ve ever gone through the process of automating Xcode installations, you’ve no doubt run across the issue of making sure that the license for Xcode and included SDKs has been accepted. An unlicensed Xcode looks like this on first launch, and asks for admin privileges:

Screen Shot 2015-11-26 at 3.06.42 PM

Or, try and run a command line utility and get:

➜  ~  strings

Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo.

For a number of years the Munki wiki has been maintaining a list of actions to “finalize” an Xcode installation. See the script posted here on the Munki wiki, notably this part:

# accept Xcode license
/Applications/ -license accept

This useful trick with xcodebuild works if you have only a single Xcode app to deploy, but the situation becomes less clear if you maintain several on a single machine. And, you may have seen from time to time that you install a different version of Xcode (or a Beta version) on your own machine, that you need to re-accept the license again. What exactly is going on here?

Read More »

Tagged | Leave a comment

Easy Version Comparisons with Python

This is just a little taste of why sysadmins find Python so approachable. If you’ve managed systems for long enough, you’ve probably had a need to compare two versions of something. For example, you want to do one thing if a given application or package is less than 2.0, and another thing if it’s greater. (For example, upgrade the application or package, or configure it differently in either case.)

If you’ve ever tried to do this in Bash, it’s terrible. And you may have seen various installer scripts that attempt to do this. Or even doing this within Installer distribution scripts, despite there being more robust mechanisms already provided by the OS that require no scripting.

Here’s an example from a support script that runs as part of installing Blackmagic DaVinci Resolve, where it does a version check to make sure it already has at least their minimum supported version of CUDA:

if [ -d /Library/Frameworks/CUDA.framework ]
    INSTALLED_CUDA_VER=`defaults read /Library/Frameworks/CUDA.framework/Versions/Current/Resources/Info.plist | \grep CFBundleVersion | sed -e 's/"//g' | sed -e 's/;//g' | awk '{print $3}'`
    if [ "${INSTALLED_CUDA_VER_PATCH}" == "" ]


CUDA_VER_MAJOR=`echo ${CUDA_VER} | cut -d\. -f1`
CUDA_VER_MINOR=`echo ${CUDA_VER} | cut -d\. -f2`
CUDA_VER_PATCH=`echo ${CUDA_VER} | cut -d\. -f3`
CUDA_VER_NUM=`echo "${CUDA_VER_MAJOR} * 10000 + ${CUDA_VER_MINOR} * 100 + ${CUDA_VER_PATCH}" | bc`

    echo "    --- CUDA is already installed - skipping step"

This is converting each “component” of the version into some multiple of 10, by putting together an arithmetic expression, and then piping it to the bc command (which was new to me), and finally using Bash’s -ge (“greater or equal than”) operator. This might be safer and more portable than doing arithmetic within Bash, I don’t know.

Is this readable? Sort of (not really). This one of the more elaborate but perhaps also more “correct” examples I’ve seen from installer packages in the wild.

If you use the JAMF Casper suite to install software, and would like to create a Smart Group that contains a criteria where some version of something is “less than” a given version, you may have found that there’s no built-in way to do this, despite it being an oft-requested feature. You can do SQL-like comparisons on the versions as strings, but this does not equate to an actual logical comparison of the “a.b.c” format that is often used for versions. In fact, this doesn’t even compare a single integer, it’s just doing simple string operations, one of “equals,” “not equals,” or “LIKE” wildcard comparisons.

Getting data like “is the Java plugin installed on a client at least version X.Y” actually requires writing a purpose-built script that can return a value on the client (known as an Extension Attribute in Casper parlance). Casper admins who use it to manage software tend to do this a lot, and have many such nearly-identical scripts. So either for cases like this, or for some other ad-hoc usage like I described earlier, it is sometimes very handy to have a lightweight, readable way of comparing versions of things, using tools that are available on every shipping version of OS X.

Python’s distutils package contains a “version” module containing some basic classes for doing version comparisons, like LooseVersion and StrictVersion. These contain enough logic to know that, for example, “1.0” is less than “1.5”, but that “1.10” is greater than “1.9” (even though if you were comparing these as floats or decimals, the latter example would be evaluated differently).

Here’s a very simple example. This will simply print the value which is evaluated as the highest version according to LooseVersion, or “equal” if they’re the same. It’s pretty readable, no?


import sys
from distutils.version import LooseVersion

# Let's create LooseVersion objects out of the 1st and 2nd arguments
# (sys.argv[0] is our script itself)
a, b = LooseVersion(sys.argv[1]), LooseVersion(sys.argv[2])

if a > b:
    print a
elif b > a:
    print b
elif a == b:
    print 'equal'

Save this script to some file, make it executable, and give it two arguments:

➜ ./ 1.10 1.9

One thing to note about this distutils.version module is that it may not be present in all Python distributions. And going forward, this module seems to have been deprecated in favour of another approach. However, if you’re reading this because you need to manage or automate tasks on OS X machines, you can safely rely on this module being part of every system distribution (thus me specifying #!/usr/bin/python above) as of at least 10.6, as long as you trust that your systems’ Apple-provided Python distributions are safely intact – which from is harder to screw up thanks to System Integrity Protection.

I highly recommend anyone wrangling shell scripts look at Python (or Ruby, or Swift, or Go, or..) as an empowering tool to help you perform sysadmin tasks more safely and effectively. But if your Bash script in question is working just fine and you just want a better way to compare versions, you can even hack in an example like the one above into its own self-contained Bash function:


greater_than_or_equal() {
    python - "$1" "$2" << EOF
import sys
from distutils.version import LooseVersion as LV
print LV(sys.argv[1]) >= LV(sys.argv[2])


echo $(greater_than_or_equal 1.2.1 1.2.0)

I’m not very proficient at Bash, but in this simple example I’ve made a function called greater_than_or_equal which assumes it gets two arguments, and sends a tiny Python script to the python interpreter binary via stdin, and should simply print “True” because 1.2.1 is greater-than-or-equal-to 1.2.0 (and “False” if otherwise).

This is just one example where Python is a great tool for performing actions that may otherwise be painful, and potentially dangerous to your systems, to do using shell scripting and built-in command-line tools.

Tagged | 2 Responses

The Office for Mac 2016 Volume License Installer, Two Months Later

pkg_officeIt is now over two months since Microsoft has made the Office for Mac 2016 Volume License installer available for customers in the VLSC (Volume Licensing Service Center) portal. I have previously documented a couple major issues with the installer that impact those who deploy Office 2016 using automated means (meaning anything that doesn’t involve a user manually running the GUI installer).

In this post I’ll summarize two of the major issues and talk a bit about a conference session that was presented just this past week at MacSysAdmin 2015 by Duncan McCracken.

Running at the loginwindow: fixed, sort of

The Office for Mac team has made some progress with one of the major issues with this installer, which was its inability to run the license activation process while at the loginwindow. The latest release in the VL portal at this time of writing is 15.13.4, and it fixes the issue where the license activation (run by Microsoft Setup Assistant) assumed it could connect to a GUI session, which at the loginwindow it cannot.

Unfortunately, they have not yet met what I’d consider the minimum requirement for a deployable installer: that it should be possible to deploy it with Apple Remote Desktop (ARD). While ARD has a (deserved) reputation of being unreliable and is not suitable for ongoing management of Macs at a larger-than-small scale, it’s still an easy-to-set-up tool that you can point a software vendor to as a way to test how well their installers stand up to a typical mass deployment scenario.

The reason the Office VL installer fails at the loginwindow with ARD was already explained in the afore-linked post: ARD seems to set a USER environment value of nobody, and when their licensing tool runs it is run using sudo -u $USER, which seems to fail when the command is run as nobody. I don’t see any reason why sudo -u $USER should be used at all in this case.

Confusing security prompt for the auto-update daemon: still there

The other major issue with the installer is that when it detects COMMAND_LINE_INSTALL, it skips the process of registering the Microsoft AU Daemon application (using an undocumented -trusted option) using lsregister, because this should be done as the user launching the app. The end result is that installing this package without other additional steps will result in a confusing “you are running this for the first time” prompt shown to users, triggered by the auto-update daemon, which is triggered automatically on the first launch of any Office 2016 application.

Working around this issue requires some fancy footwork: setting preferences for to prevent it from launching automatically, or using an installer choice changes XML to selectively disable Microsoft Auto Update (MAU) from installing at all. The latter won’t help much if Office 2011 has already been installed, because Office 2011 includes the same Auto Update application, and the 2016 applications will attempt to register themselves with it on first launch. Another option, which requires no modification to the installation configuration, is to instead create a custom script to run the same lsregister command, and run this script by every user at login time, deployed using a tool such as outset.

Admins have also gone the route of simply deploying the standalone “update” packages instead of the base application, as these don’t include the MAU components at all. This is also all documented thoroughly in my earlier post.

These advanced workarounds – repackaging, recombining, reconfiguring and “augmenting” with additional LaunchAgents – are all excellent examples of things that should never be required by an IT administrator for mainstream software. These techniques are typically only needed for niche applications made by software vendors whose release engineers have little interest in understanding the conventions and tools available for the OS platform. Adobe is obviously the one glaring exception here.

The audit by Duncan McCracken at MacSysAdmin 2015

Last week the MacSysAdmin 2015 conference took place in Göteborg, Sweden. Duncan McCracken, whose company Mondada offers a paid Mac packaging service, spent the latter half of his presentation deconstructing the Office 2016 installer.

A video recording of Duncan’s presentation, as well as some his resources used in the demo, can be found at the MacSysAdmin 2015 documentation page (or here for a direct link of the video).

Because Mondada specializes in packaging as a service, Duncan is an expert at doing packages properly, and is experienced with fixing the mistakes made by commercial vendors who don’t properly implement the tools made available by the Installer framework and packaging tools on OS X. Somewhat of a perfectionist, Duncan is used to completely disassembling and re-assembling a flawed package (or one that uses a custom packaging engine – see his 2010 MacSysAdmin Installer Packages session for an example) to make it as “correct” as possible, and using the appropriate mechanisms available in the Installer framework to perform whatever custom logic may be necessary.

The Office 2016 package deconstruction begins roughly halfway into the video. As someone who’s all-too-familiar with problematic installer packages (and Office 2016’s in particular), I found the session extremely entertaining. The parts of Duncan’s demos that didn’t go so well were supposedly caused by a misconfigured (or broken?) shell binary in his OS X VM he was using in the demonstration, and that the process he went through to re-assemble the installer package should otherwise have resulted in a successful installation.

Given that Mac IT admins are still in this awkward phase where OS X El Capitan is now shipping on all new Mac hardware, Outlook 2011 effectively cannot run on El Capitan, and organizations are feeling pressure to deploy Office 2016 as soon as possible, it’s unfortunate that the Office 2016 installer still requires so much “fixing.” I’m willing to go out on a limb and say that Office is the single most commonly deployed commercial software in organizations.

That Duncan dedicated nearly half of his session to this installer package is a testament to how far IT admins need to go simply to deploy software in a manner that provides a trouble-free experience for users. Software vendors do not have a clue that we do this – so don’t think that they are “out to get you” – but when software becomes this hard to deliver to users, it’s time to push back and give real-world examples of the contexts in which we install software and details of the workarounds we implement. You may well better understand the implications of sudo -u $USER in postinstall scripts than the release engineers do, so educate them!

There’s even contact info in a comment from my previous post. If you don’t have an expensive enough agreement with Microsoft (we don’t), it can otherwise be challenging to get a fruitful contact with the engineering team, so this is an opportunity to provide direct feedback.

Tagged , | 1 Response