Monday 11 November 2019

macOS Catalina - How to uses imaging even though Apple don't want you to

Apple have with each new version of macOS tightened the security and in general this is clearly a good thing.

Apple have also removed a number of historically available functions - including ones used in the past by many Mac administrators. This arguably is a mixture of good and bad.

The latest casualty in macOS Catalina is the loss of the --volume option in startosinstall.

Losing the --volume option means you cannot boot from an external drive and automate the installation on to the internal drive along with (optionally) flags to erase the internal drive and install packages. Now you can only do this by booting from the internal drive itself and then running the startosinstall command which in turn means going through the Apple Setup Assistant at least once. This could be workable for wiping and reusing an existing Mac but only if you have a valid login when the Mac is returned by the previous user.

This seems an extremely petty change since the GUI macOS installer still does let you boot from an external drive, run the installer and specify a different drive to install on to. Clearly there cannot be any technical reasons for this change. 😕

Ironically the 'solution' to the loss of the --volume option is to go back in time and return to using AutoDMG and an image restoration process e.g. like DeployStudio (run locally).

It should be noted that due to the now extremely aggressive secure implementation of Security & Privacy in Catalina one can no longer run normal DeployStudio workflows to configure a Mac unless you also install DeployStudio Runtime on the target Mac, give it and Terminal/bash/scripts full disk access permission. Clearly you would not do this on a Mac you are configuring.

It is however possible to do the following.

  1. Use Mager Valp's AutoDMG (currently a beta version for Catalina compatibility) to build a Catalina image
    1. The source macOS Installer must be inside a disk image, I happen to use Greg Neagle's installinstallmacos.py script to download the macOS Installer and this automatically puts it in a disk image
    2. Make sure you have no other volumes called 'Macintosh HD' mounted as otherwise AutoDMG gets 'confused' as which to use
    3. This includes the normally invisible 'Macintosh HD - Data' now included with Catalina, I therefore have my USB boot drive named differently
  2. Use Richard Troughton's old first-boot-package tool to run scripts and installers during the first boot of the restored image
  3. Use a DeployStudio server to host the AutoDMG image
  4. Use a USB boot stick with a full install of Catalina and use Disk Utility to erase the target (internal) drive if needed
  5. Use DeployStudio Runtime to restore the AutoDMG created image
  6. On first boot the restored Mac will then run the scripts/installers provided by Richard Troughton's tool, in my case I run an installer created using Greg Neagle's pycreateuserpkg to create an initial local admin account, Mager Valp's SkipAppleSetupAssistant pkg, my own script to set initial preferences, and then Greg Neagle's munkitools installer. I also run another of my own scripts to replace the DeployStudio function to automatically name restored computers.

I could have included an installer to enrol in to our MDM e.g. a Jamf QuickAdd.pkg however I intend to use DEP for Catalina.

The above therefore pretty much restores past 'imaging' capabilities.

Thursday 17 October 2019

Auto-naming Mac computers using values from a database

Long time Mac admins may have used a tool like DeployStudio to 'build' Macs before issuing them to users.

DeployStudio can install the operating system, set various settings and install files and programs. One of the tasks it can perform as part of its workflow is to automatically set the name of the computer based on a 'database' stored within DeployStudio.

I always found this auto-naming of computers useful as it allowed using a name format that was completely under the control of the Mac administrator and therefore could be for example based on asset numbers rather than the computer serial number.

Unfortunately it seems with macOS Catalina Apple have finally put the last nail in the coffin for using DeployStudio as a tool. (I had been able to devise a way to use it for macOS Mojave even with T2 chip equipped Macs.)

Equally unfortunately it seems most other Mac management tools e.g. Jamf do not have a similar facility and at best leave you to write a script which typically names the computer based on its serial number.

Whilst using a serial number is a possibility and achieves the main goal of being a unique value and one that could be used to track computers on a network it is not the format I prefer and whilst macOS is perfectly happy with that as a computer name that format would not work as well in other operating systems especially Windows which would lead to a loss of consistency in naming computers.

I have therefore devised a script of my own to auto-name Macs using a database sourced value i.e. the way DeployStudio works. This script could be run via Jamf after enrolment. The example script listed below is using the database from DeployStudio. Clearly it would be massive overkill to setup and run a DeployStudio server solely for the purpose of running the database of computer names but if you have an existing DeployStudio server you can continue to use it for just this purpose. In theory this approach could be relatively easily modified to use an alternate database although directly using something like MySQL would then require having the MySQL client installed. In my case I am also considering using the free open source IT asset management system 'Snipe-IT' which like DeployStudio also has a REST API.

Note: In order to make this script as robust as possible and in particular more suitable for Jamf I went to considerable effort to process the XML returned by DeployStudio in a way that avoided having to write the results to a file, that is I have managed to do all the processing using pipes and stdin. This precluded using the defaults command for example. I also was careful to only use tools built-in to macOS.

#!/bin/sh

# DeployStudio connection settings
host='https://deploystudio.domain.com:60443'
adminuser='deploystudiouser'
adminpass='deploystudiopass'

# Get Mac serial number
# Your choice to use ioreg or system_profiler
# MAC_SERIAL_NUMBER=`/usr/sbin/ioreg -l | /usr/bin/grep IOPlatformSerialNumber | /usr/bin/awk '{print $4}' | /usr/bin/cut -d \" -f 2`
MAC_SERIAL_NUMBER=`/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/grep 'Serial Number (system)' | /usr/bin/awk '{print $NF}'`

# Get Mac hostname from DeployStudio
# This is done using DeployStudio's REST API which returns a binary XML record, this then has to be converted to text XML and the host name key obtained from it
# It is assumed that your DeployStudio is using the default option to use a Mac serial number as the index, if you used MAC addresses this will not work
result=`/usr/libexec/PlistBuddy -c "Print $MAC_SERIAL_NUMBER:cn" /dev/stdin 2> /dev/null <<< $(/usr/bin/curl -s -k -u $adminuser:$adminpass "$host/computers/get/entry?id=$MAC_SERIAL_NUMBER" | /usr/bin/plutil  -convert xml1 -r -o - -- - )`

# If a result is returned from DeployStudio then use it, else use a generic name
if [ $? -eq 0 ]; then
echo "$result"
else
result=`/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/grep "Model Name" | /usr/bin/awk '{for(i=3;i<=NF;++i)printf $i""FS ; print ""}'`
echo "$result"
fi

# Set Bonjour and Computer names
/usr/sbin/scutil --set LocalHostName "$result"
/usr/sbin/scutil --set ComputerName "$result"
/usr/bin/dscacheutil -flushcache

exit