This is the second post in my multi-part series on modern bootstrapping with Workspace ONE UEM. If you haven’t read the first one, you can find it here.

At Saint Joseph’s University, we have two types of devices: Single-User Devices and Multi-User Devices.

  • Single-User Devices are devices that are meant to be used by the end user. An example is a computer given to a faculty or staff member.
  • Multi-User Devices are meant to be used by multiple people over their lifespan and thus, have different requirements. An example of a multi-user device would be a lab computer.

Where the bootstrapping process differs, I’ll cover both.

Requirements

  • An MDM. I’ll be detailing how we fit everything together with Workspace ONE UEM. You’ll need to have it connected to Apple School Manager or Apple Business Manager.
  • An Identity Provider for your MDM. We’re using Active Directory with the AirWatch Cloud Connector installed on a VM that can reach our AD domain controllers.
  • A root agent for installing apps. I’ll be using Munki in this tutorial.
  • Python 3. Install the latest version on your Mac, and make sure it’s in your Munki repository, too (I like these AutoPkg recipes from Armin Briegel).
  • Outset. Make sure the latest version is in your Munki repository.
  • The latest version of InstallApplications (IAs), as well as my modern_bootstrapping GitHub repo.
  • If you want to name your Macs while bootstrapping, download and install my fork of Munki Enroll. Your Macs will also need to be running macOS 10.15 or above.
  • A web-accessible file share for your IAs scripts and pkgs. In our case, I made a new SMB share on our on-prem Munki server named ‘bootstrap’ and enabled basic authentication.
  • The Xcode command line tools. The easiest way to obtain these is to install Xcode from the App Store.
  • Munkipkg. Be sure to install it somewhere in your PATH (I put it in /usr/local/munki-pkg).
  • Download the latest version of NoMAD Login, but don’t install it. You can obtain the latest version from the NoMAD Login website or from the #nomad-login channel in the MacAdmins Slack.
  • Finally, generate Developer ID Application and Developer ID Installer identities in your Apple Developer account and add that to your keychain. You’ll need to ask the Team Agent to do this, as the latter is hidden for all other users.

DEP Profile

First, create an Organization Group in Workspace ONE for your Single-User Devices or Multi-User Devices. In the left sidebar, click Groups & Settings > Groups > Organization Groups > Details, then click the Add Child Organization Group tab. Be sure to give it a Group ID.

Next, you’ll create a DEP profile and connect your new Organization Group to that. In the left sidebar, click Groups & Settings > All Settings, then navigate to Devices & Users > Apple > Device Enrollment Program. Create a new profile.

The important settings to change are:

  • Custom Enrollment: Off
  • Authentication: On
  • Device Ownership Type: Corporate – Dedicated
  • Device Organization Group: select the one you created above for Single-User Devices or Multi-User Devices
  • Await Configuration: On

Most of the Setup Assistant options are shared with iOS, iPadOS, and tvOS. You can select Skip for any macOS-specific ones.

For Single-User Devices:

Towards the bottom, set Primary Account Setup to Don’t Skip. Fill out the next section as follows:

  • Account Type: Standard
  • Autofill: Enabled
  • Username: {EnrollmentUser}
  • Full Name: {FirstName} {LastName}
  • Allow Editing: Disabled

For Multi-User Devices, set Primary Account Setup to Skip.

In the next section, for Admin Account Creation, fill it out however you’d like. In our case, we’re creating a temporary account that is deleted later, when we deliver our local admin account (created with pycreateuserpkg). For Single-User Devices, the primary user of the device will be the one with the Secure Token.

Click the Save button. In our case, we also made the Single-User Devices DEP profile our default. For Multi-User Devices, we manually assign it to serial numbers. This can be done by visiting Devices > Lifecycle > Enrollment Status, checking the appropriate serial numbers, and clicking More Actions and Assign Profile.

Bootstrap Package

Now that we’ve laid that groundwork, we can focus on the bootstrap package itself. As mentioned above, we’re using Erik Gomez’s InstallApplications. Our workflow started off similar to Erik’s demo repo, but diverged a bit when our needs changed.

In order to allow a custom bootstrap package to install, you’ll need to disable Intelligent Hub’s installation in Groups & Settings > All Settings, then navigate to Devices & Users > Apple > Apple macOS > Intelligent Hub Settings. Ensure that Install Hub After Enrollment is disabled.

In a nutshell, here’s how bootstrapping with IAs works: you build your bootstrap package, then upload that to your MDM server. When a user clicks through the Setup Assistant and proceeds past the Remote Management screen, your MDM server pushes your bootstrap package to the device (as well as any relevant configuration profiles for settings management). From there, your bootstrap package downloads a bootstrap.json file that contains everything necessary to get things started: usually a few configuration scripts, your root agent (in our case, Munki) for installing additional packages, etc. The idea is to keep bootstrapping as lightweight as possible and let your root agent handle the rest.

To allow for testing, I actually keep four copies of IAs available:

  • bootstrap_single_release
  • bootstrap_single_testing
  • bootstrap_multi_release
  • bootstrap_multi_testing

I’ve renamed each IAs folder for easy differentiation. We’ll be working with the first and third entries in that list: bootstrap_single_release and bootstrap_multi_release. If you’d like to set up separate bootstrap packages for testing, just follow this guide again, creating separate Organization Groups, DEP profiles, IAs folders, etc.

Start by merging the contents of the latest IAs release into the bootstrap_single_release or bootstrap_multi_release folders from my GitHub.

Decide if you’d prefer to use a plist or a json file for building with munkipkg. Erik includes a build-info.json file, but since I find it easier to work with plist files, I’ve included a build-info.plist for each package. Trash the one you don’t want to use. Edit your build-info file to include your package’s name, bundle identifier, version number, and Apple Developer account information. I add another octet to Erik’s version number, so I can tell which copy of his code I’ve based mine from (for example: 2.0.3 becomes 2.0.3.1, 2.0.3.2, etc.).

Edit the LaunchDaemon in payload/Library/LaunchDaemons to include your server information, including authentication. Be sure to change the reference to bootstrap.json to bootstrap_single_release.json or bootstrap_multi_release.json. You should be able to leave everything else in payload alone.

At the root of each bootstrap folder, change lines 19 and 28 in make_pkg.sh as appropriate for your environment. Open the Terminal, cd to the root of the bootstrap folder, then run make_pkg.sh. A Finder window will open and highlight your package. Upload your bootstrap package to Apps & Books > Applications > Native and select Expedited Delivery. The script makes an assignment suggestion for Workspace ONE users, though I’d recommend starting with a group of test devices.

Bootstrap JSON

Next, we’ll build the script that generates the bootstrap.json file that IAs downloads after installation. Everything in this file is downloaded and installed immediately afterwards. Since this file isn’t contained in the bootstrap package, you can make as many changes as necessary without needing to regenerate it.

IAs supports three phases:

  1. preflight
  2. setupassistant
  3. userland

In this tutorial, we’ll only be using the first two. Preflight will prevent your bootstrap package from installing repeatedly on the Macs in your fleet, which would likely result in undesired configurations. The setupassistant phase is where all of the scripts and packages will be located. The userland phase is generally for performing actions like changing the dock icons, the desktop background, etc. We’ll be using Outset for that instead.

If you’re already using NoMAD Login in your environment, please add the “notify” keys to your custom profile. If not, I’ve included two profiles you can customize and use. The Single-User Devices profile lets the user know what’s happening while IAs is working through your bootstrap.json file. The Multi-User Devices profile does this, too, but includes a user input prompt for the computer name and device type (at SJU, we have several types of Multi-User Devices).

Rather than build these bootstrap.json files manually, we can run a script that calls Erik’s generatejson.py script. To get you started, I’ve included sanitized versions of the Single-User Devices (make_bootstrap_single_release.sh) and Multi-User Devices (make_bootstrap_multi_release.sh) scripts. Edit the URLs and SMB file share variables at the top of the script to match the ones you created at the beginning of this post. Here’s everything included in the file, and why you might want to use it:

  • preflight.py: This is from the installapplicationsdemo repo, slightly customized for our environment. Please obtain this from Erik’s repo and edit it to contain something in your environment that can be used to determine if a Mac has been set up or not. In our case, we’re using logo.pkg (see the next line).
  • background_grey.pkg, logo.pkg: This is the background and logo we use for NoMAD Login.
  • NoMADLogin-1.4.0-multiplatform.pkg: This is a Universal copy of NoMAD Login, obtained from the #nomad-login channel on the MacAdmins Slack.
  • bootstrap_sud.pkg or bootstrap_mud.pkg: This will officially kick off the user notification portion of the bootstrapping process. I’m including the code for these in my GitHub repo. They contain an empty depnotify.log and ManagedSoftwareUpdate.log, since NoMAD Login and Munki will fail to run if they don’t exist. The Single-User Devices version includes a script for displaying a notification using hubcli (part of Workspace ONE’s Intelligent Hub) and Outset. Thanks to Dennis Moffett for the login window detection code in the postinstall script!
  • install_rosetta_on_apple_silicon.sh: This is from Rich Trouton’s GitHub repo and is necessary for ensuring the bootstrapping is successful on Apple’s latest hardware.
  • munkitools_launchd-3.0.3265.pkg, munkitools_python-3.9.4.4346.pkg, munkitools_core-5.4.0.4348.pkg: These are the most important components of Munki. Once these are installed, we use Munki to install the others.
  • set_time_zone_eastern.sh: Since SJU is located on the east coast, I can cheat a little and manually set the time zone. I’d love to use Location Services instead, but just couldn’t get it to work.
  • bless_vm.py: This is from Erik’s installapplicationsdemo repo.
  • munki_bootstrap_sud.py or munki_bootstrap_mud.py: This is also from Erik’s installapplicationsdemo repo, but modified for our needs. Each script is referencing a specific manifest in our Munki repo that contains munkitools, munkitools_admin, MunkiReport, Outset, and Intelligent Hub. The Single-User Devices manifest also contains our dock and desktop background scripts (which are run with Outset).
  • delete_ilife_iwork.sh: We use this to remove the preinstalled copies of iLife and iWork. Most of our users don’t actually need these apps. We have them available in Managed Software Center if they decide later that they’d like to use them.
  • munki_launchd_loader.py: Also from Erik’s installapplicationsdemo repo.
  • mac_auto_name.sh (Single-User Devices only): Our naming scheme for our Single-User Macs is username-computertype (example: msolin-mbp). This script automates the naming process and creates a manifest using my fork of Munki Enroll. Functions best with the lookup-value-profile.xml configuration profile from my GitHub repo (thanks to mrkidd in #workspaceone for this!).
  • munki_auto_trigger.py (Single-User Devices only): Also from Erik’s installapplicationsdemo repo.
  • finish_registration_mud.sh (Multi-User Devices only): This takes the user input from NoMAD Login and applies it to the Mac, then tells Munki to create a manifest with the same name using my fork of Munki Enroll.
  • finish_depnotify_sud.sh or finish_depnotify_mud.sh: This is the final script in our bootstrap.json file. In this script, we tell IAs that it’s OK for it to perform its own cleanup.

Once you’ve assembled all of that, open Terminal, cd to your bootstrap_single_release or bootstrap_multi_release directory, and run make_bootstrap_single_release.sh or make_bootstrap_multi_release.sh.

Wrapping Up

If you followed this post to build a bootstrap package for your Single-User Devices, your setup process should look like this:

  • After selecting a country and a language, your users will be prompted for authentication at the Remote Management screen.
  • After proceeding to the end of the Setup Assistant, the bootstrap package will download from Workspace ONE and install.
  • The bootstrap package will download your bootstrap_single_release.json file and proceed with installing/running everything in there.
  • The user will see NoMAD Login take over the entire screen while displaying the bootstrapping progress. The Mac will be named automatically, and a manifest will be created in Munki.
  • When it’s finished, NoMAD Login will display its customized login window. Although your Identity Provider might be on-premises (even if the computer is not), the user will be permitted to login, since their local account was created after the Remote Management authentication.
  • Once at the desktop, the user will see a notification that their Mac is finishing up in the background. IAs will remove itself.
  • Finally, Munki will run in the background and download/install all of the software listed in the device’s manifest, prompting the user in Managed Software Center for a reboot, if necessary.

If you followed this post to build a bootstrap package for your Multi-User Devices, your setup process should look like this:

  • You’ll be prompted at the Remote Management screen to authenticate.
  • After proceeding to the end of the Setup Assistant, the bootstrap package will download from Workspace ONE and install.
  • The bootstrap package will download your bootstrap_multi_release.json file and proceed with installing/running everything in there.
  • You will see NoMAD Login take over the entire screen while displaying the bootstrapping progress. You’ll be prompted for the computer name and device type in NoMAD Login, which will be used for naming the Mac and creating a manifest in Munki.
  • Apple’s login window will be restored immediately before IAs removes itself.
  • Finally, Munki will appear in an overlay window and will install all software in the manifest, rebooting as necessary.

I hope this was useful for anyone getting started with bootstrapping or looking to improve their existing practices. If you have any questions or feedback, please leave a comment below and I’ll do my best to help. If there’s interest, I’ll follow up with another post, addressing common pitfalls and FAQs. Thanks!