Waiting for the macOS Big Sur installer to launch

As part of Gatekeeper, macOS runs a code signature validation scan on all apps when they are run the first time. This results in some really large apps taking 2-3 minutes before they can run. Some apps include: Xcode, Matlab, Mathematica, etc.

The macOS installer app for Big Sur suffers from this too and as a result if you try to run “Install macOS Big Sur.app” it will take at least 2 minutes while macOS scans it. If you launch the app installer through the GUI, the app will simply bounce in the dock until the scan completes. If you run the app through the CLI using startosinstall, it will show no activity until the scan completes.

There’s no way to force macOS to scan a particular app for its code signature validation other than actually trying to run it. This would obviously lead to a bad user experience for the end user.

As a workaround, you can trigger the code signing validation by using startosinstall --usage. This will not actually run an OS upgrade and macOS will start to scan “Install macOS Big Sur.app” silently in the background.

I’ve written a simple script that is designed to run immediately after “Install macOS Big Sur.app” has been installed. I’ve designed it for use in Jamf Pro, but you could easily modify it for other uses if you wanted. The idea is to make the user experience better so that when the user launches an OS upgrade whether through a GUI install (as Apple intends) or a CLI install relying on “startosinstall” there’s no a 2-3 minute period of silence. You can run the script immediately after deploying the installer app to the computer.

File feedback with Apple so that they can improve this user experience and macOS installer apps can be scanned immediately after a download has taken place. This would also have benefits to other large apps as well. Jamf has a good article that discusses the various options for providing Apple with feedback.

Additional research

XProtectService and syspolicyd

If you try to see what’s going on in the when you launch the installer macOS app, you will see XProtectService mentioned in the Console logging when dealing with one of these apps and syspolicyd. Some research on syspolicyd that helped a bit in my testing and research: https://knight.sc/reverse%20engineering/2019/02/20/syspolicyd-internals.html

There is a Launch Daemon at /System/Library/LaunchDaemons/com.apple.security.syspolicy.plist which seems to have the frequency at which some syspolicyd actions might be taking place depending on the LaunchEvent. The com.apple.security.syspolicy.find.bundles key seems to be relevant based on the name. That runs weekly (604800 secs). There is a CLI to this: /usr/libexec/syspolicyd but it’s not very clear to me how to interface with this or whether I’m supposed to. I assume that spctl is the tool that’s supposed to interact with syspolicyd.

Investigating options to workaround code signing validation

For the curious, I did try a bunch of things to kick off the code signing validation before I tried to launch the app or to avoid code signing validation altogether.

The following does not work:

# Remove com.apple.quarantine recursively from app bundle
xattr -dr com.apple.quarantine "/Applications/Install macOS Big Sur Beta.app"

# Clear all extended attributes recursively from app bundle
xattr -cr "/Applications/Install macOS Big Sur Beta.app"

# Asses app
spctl -a "/Applications/Install macOS Big Sur Beta.app"

# Stop spotlight from trying to index app
mdutil -i off -a

# Register app with launch services
/Volumes/Macintosh\ HD/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -R -f "/Applications/Install macOS Big Sur Beta.app"

# Add path to app to Gatekeeper
spctl --add "/Applications/Install macOS Big Sur Beta.app"

# Not sure if using the –path option makes a difference
spctl --add --path "/Applications/Install macOS Big Sur Beta.app"

# Put label on app bundle and then enable label so that Gatekeepr will bypass it
spctl --add --label "Approved" "/Applications/Install macOS Big Sur Beta.app"
spctl --enable --label "Approved"

# Disable Gatekeeper
spctl --master-disable

# Tried to modify the syspolicy database sqlite3 /var/db/SystemPolicyConfiguration/ExecPolicy using:

INSERT INTO policy_scan_cache (volume_uuid,object_id,fs_type_name,bundle_id,cdhash,team_identifier,signing_identifier,policy_match,malware_result,flags,mod_time,timestamp,revocation_check_time)
VALUES ('C202AB2C-4F93-44BE-9F4F-CB5DA760F07E','189698','apfs','com.apple.InstallAssistant.Seed.macOS1016Seed1','45bc1b465728e65fb091f81bcc0ea147370f455f','','com.apple.InstallAssistant.Seed.macOS1016Seed1',11,0,512,1604217029,1604217029,1604217029);Error: attempt to write a readonly database

This failed because its a read-only database naturally.

# Tried to change the date more than 7 days into the future hoping that might trigger the OS to do its scanning:
date -u 1111000020

The following did work:

I did find one way to avoid the code signing validation but it cannot be automated with either CLI tools or through MDM. Go to Sys Pref > Security & Privacy > Privacy tab > Developer Tools > Enable Terminal. Going forward, no security check takes place when I run (from Terminal) the exact binary that’s supposed to get loaded.

With Terminal not enabled in Developer Tools:
/Applications/Install macOS Big Sur Beta.app/Contents/Resources/startosinstall --usage would normally take 120 secs
But with Terminal enabled in Developer Tools, that runs immediately.

Apple plans on removing enterprise options for macOS software update

For sometime now, Apple has allowed IT administrators to manage updates for macOS. However in a very near future that may change unless other IT administrators start to provide feedback to Apple. This will be long but please read as now is a critical time to provide Apple feedback before WWDC (whenever that takes place) and the next major OS is released.

Continue reading Apple plans on removing enterprise options for macOS software update

Revisiting: A macOS upgrade method using Jamf Pro Self Service

Back in 2017, I released my script that I use to do macOS upgrades in my organization through Self Service. Since the script I wrote has changed, I thought a new post was deserved to account for the changes I’ve made.

At my organization, we leverage the macOS installer app from Apple which has a neat little command line tool called startosinstall. There are quite a few scenarios that need to be accounted for to avoid running the upgrade in a situation where it would ultimately fail. With that in mind, here are some of the requirements I came up with that the upgrade script needed to solve:

  1. Computer has sufficient free drive space.
  2. Ensure the user is plugged into a power source.
  3. Confirm that the volume is not presently undergoing encryption/decryption.
  4. Provide a way for the end user to do FileVault authenticated restarts if possible.
  5. Provide dialogs to give the user feedback such as a time estimate and dialogs on what to expect next.
  6. Make use of Jamf Pro script parameters to allow for customization and potential re-use for future operating systems releases.
  7. Provide logging to see where failures may appear.
  8. Allow use of an install package that can be used with macOS 10.13+ installers.
  9. Make sure that the macOS installer app does not have expired certificates.
  10. Perform a jamf recon immediately after upgrade.

Continue reading Revisiting: A macOS upgrade method using Jamf Pro Self Service

Jamf Pro OS Deprecator

Getting end users to upgrade to the latest supported version of macOS in an enterprise environment can be a little tricky. Some see it as a time consuming and tedious task that can get in the way of actual work. It doesn’t help you need to do this once every year. However, there are not only security benefits to upgrading, but usually newer features that end-users can take advantage of that may increase their productivity. But more importantly for the IT admin, there’s less time and resources spent supporting multiple operating systems when you simply have only one version to support.

Depending on the environment, there are different approaches an organization can take in tackling the issue of getting company devices upgraded:

  • Compliance: The idea behind this approach is simple. Only provide access to the work resources that an end-user needs so long as the device they are on is considered “compliant” (or in this case, up-to-date).
  • Reminders: Your organization communicates and strongly encourages getting folks to upgrade through notifications, emails, and maybe even messages coming from your business communication tool of choice.
  • Force:  It doesn’t matter what the circumstances, an OS upgrade is pushed out to all devices and each of them get it at some specified time.
  • Anarchy: End users dictate the OS they run and you will support it. ¯\_(ツ)_/¯

There are probably other approaches that fall somewhere in between. The focus of this blog post is a script I wrote that takes a combination of the 2nd and 3rd approach. This may or may not work for your environment.

To start off, this script is meant to be used with Jamf Pro and makes use of Jamf Helper.
The idea behind this script is that it alerts the user that the OS they are running is
no longer supported. However it’s important to try and provide a little bit of flexibility in how often the user gets notifications. Rather than forcing updates through, the script allows you to control the pace of notifications the user receives to perform the OS upgrade by letting you set different dates of importance.

If the user opts to proceed with the upgrade, they will get taken to a second policy that you’ve configured to do the OS upgrade. Or if you don’t configure a second policy, it will simply point them to the Mac App Store.

There are three optional dates that you can supply to the script that will dictate when the user receives the notifications:
Start Date: This provides a notification to the user, but also allows them to delay when
they will receive the next reminder.
Nag Date: This provides a notification to the user. The user cannot select when to be
reminded. This will be set based on the re-notification period set by the admin. This re-notification period essentially lets you determine how often you want to remind the user to update.
End Date: Similar to the Nag Date, this provides a notification to the user where they cannot select when they will be reminded. However it nows forces the computer to go into the final countdown. Once this date has been reached, you can set it so that the user has X amount of attempts left before they are forced to upgrade.

Dates can be supplied in either epoch seconds or string format (e.g. Sep 03 12:34:56 -0400 2019). The notifications are meant to get increasingly more forceful.

Because the dates are optional, you can either supply no dates, some of the dates (e.g. Start And End Date only), or all of the dates at your discretion. What this means is that the user will only see the notification after that date has been reached. If no dates are supplied, the user will simply get the same notification as if a start date had been set with an option to be reminded later. Each date needs to be greater than the previous; your Start date can’t be set to a date after the End date.

With each notification, there is a “More Info” button in the Jamf Helper which will always open up a URL. This URL should provide the user will instructions on how to proceed to update. Because it’s a URL, you could also provide a Self Service URL as well if you want with the assumption you have a detailed description.

This script does try to account for the fact that it’s Self Service-centric. That means
there are a few checks in place to try and give the user the best chance to perform the

  • Ensure a user is logged in.
  • Power source is connected.
  • No app is opened that has a display sleep assertion active.
  • Idle time of the computer.

Given the above overview, here are the Jamf script parameters:
Parameter 4: Optional. A minimum OS version to compare against what’s running on the client. If the client is running an OS that’s the same version or greater then the
script will exit. Provide OS version in form of 10.14.6.
Parameter 5: Optional. The custom trigger name for a Jamf policy that will initiate the OS upgrade.
Parameter 6: Optional. Provide the policy kicking off this script a custom trigger name
and supply it here. This is used for situations when the user tries to quit Jamf Helper notifications.
Parameter 7: Optional. The Start Date at which point the logic in this script will provide the end-user a notification with the option to set when they will receive the next reminder. If not set, the user will start to receive notifications the second time the script is run based on the re-notification period. Date can be supplied in epoch seconds or string format (e.g. Sep 03 12:34:56 -0400 2019).
Parameter 8: Optional. The Nag Date at which point the logic in this script will provide
the end-user a notification which they can dismiss. User cannot select when to be
reminded as this is determined by the renotification period.
Parameter 9: Optional. The End Date at which point the logic in this script will provide the end-user a notification which they can defer only X amount of times (defaults to 3 times). The user will get reminded every 24 hours until those deferrals are done.
Parameter 10: Optional. The re-notification period before the user will get the notification again. This becomes applicable when you’re passed the Nag date. Default to 60 minutes.
Parameter 11: Optional. The time out period in seconds which determines how long the Jamf Helper notification will stay up. Defaults to 90 minutes.

Unfortunately, there are not enough Jamf parameters available to use for all the
customizations allowed in this script. Due to this limitation, there are other variables
in this script you can change under the section titled “Variables You Can Modify”:
MaxDeferralAttempts: Optional. Determines the number of deferral attempts a user has after the end date has been reached. Defaults to “3” if left blank.
MaxIdleTime: Optional. Number of seconds in which a computer has been idle for too long to expect someone to be sitting in front of it. Script will exit if computer has been
idle longer than this time. Defaults to 10 minutes if left blank.
MoreInfoURL: Optional. A URL that points the user to a page on the macOS upgrade process. If left blank, this will default to Apple’s macOS upgrade page which has been active since September 2016. You can optionally use a Self Service URL as well.
DelayOptions: Optional. A list of comma separated seconds to provide delay options to
the user. The seconds will show up in Jamf Helper as time values. Defaults to “0, 3600, 14400, 86400” which represent “Now, 1 hour, 4 hours, 1 day” in seconds.
ITContact: Optional. Enter either an email address, phone number, or IT department name for the end user to contact your team for assistance. Defaults to “IT” if left blank.

I’ve written the verbiage with the idea that the end user would open up Self Service
to perform upgrade macOS. Maybe this doesn’t work in your environment because you don’t have a Self Service workflow. Or maybe there’s just something else you want to change in the verbiage. Below are the variable names so that you can alter the verbiage to your liking should you want to. There is a bit of variable logic inside the verbiage so modify at your own risk.

Variable Names for the notifications:
ReminderNotification: The text used when Start date has been reached or if no Start date has been supplied.
NaggingNotification: The text used when Nag date has been reached.
FinalNotification: The text used when End date has been reached but there are still deferrals left
FinalCall: The text used when End date has been reached with no more deferrals left.

There are a few exit codes in this script that may indicate points of failure:
10: Required parameter has been left empty.
11: Make sure Start date < Nag date < End date.
12: Minimum Supported OS Major Version is not an integer.
13: Minimum Supported OS Minor Version is not an integer.
15: Incorrect property list type used. Valid types include: “array” “dict” “string” “data” “date” “integer” “real” “bool”
16: End Date has been provided without custom trigger.

If you’ve followed along up to this point, you’ll notice that there are a lot of optional variables. That was intentional. There are some organizations where each user has full admin access and the end user is expected to upgrade to macOS using the App Store as a regular consumer would at home. If you leave all parameters empty, the end user is pointed to the Mac App Store to upgrade or to check out the Apple macOS upgrade page if they want to learn more. This is without a doubt the most commonly tested scenario that Apple tries to ensure works without issue. Command line based upgrades are simply not always 100% reliable depending on which version of the macOS installer app you are deploying.

However, not all environments are like that. In some environments, the end user needs a bit more hand holding because there may be a few pre-requisites to upgrade and maintain compliance. Perhaps some software needs to be uninstalled or updated beforehand to avoid problems post-upgrade. With that in mind, you can point to a second policy that should kick off the OS upgrade as expected in your environment.

Below are a few screenshots so you can get an idea of what the notifications look like in a Self Service based workflow where a second policy is linked.

Start Date/Default Reminder notification:
You get this if the start date has been reached or no dates have been provided.


Nag Date notification:
You will get this reminder once the Nag date has been reached. It will re-appear after the re-notification period has been reached.


Nag Date notification (with an End Date that has not been reached):


End Date notification (with deferrals left):
You will get this reminder once the end date has been reached and there are still deferrals available. Note: The Postpone button will still open up the MoreInfoURL link.


End Date notification (with no deferrals left):
You will get this reminder once the end date has been reached and there are no deferrals available.


How to use this script in Jamf Pro?

Once you’ve uploaded the script to Jamf Pro, I would recommend setting up Parameter Labels. To do this, go into Jamf Pro > Settings > Scripts > JamfDreprecationNotifier.sh and click on the Options tab.

Here are the labels I’m using but you can feel free to adjust your labels:

Parameter 4: Min Supported OS Version (eg 10.14.6)
Parameter 5: Custom Trigger for OS Upgrade Policy
Parameter 6: Custom Trigger for Deprecation Policy
Parameter 7: Start Date (Sep 03 12:34:56 -0400 2019)
Parameter 8: Nag Date (Sep 03 12:34:56 -0400 2019)
Parameter 9: End Date (Sep 03 12:34:56 -0400 2019)
Parameter 10: Renotification Period (in seconds)
Parameter 11: Time Out (in seconds)

Create a new policy in Jamf Pro and use the Script payload to add this script. When you add this script to a policy, you will need to setup the following:

1: Trigger: Recurring Check-in and Custom (this is a required parameter)
2. Execution Frequency: Ongoing
3. You can optionally set client-side limitations for the policy so that it only runs between certain days and hours.
4. Scope: I recommend scoping against smart groups based on the operating systems you want upgraded and that would meet the qualifications for the operating system you want users to upgrade to.

Other things to note:

If you’re familiar with shell scripting you should be able to see where some of the information from this script is stored. That would also allow you to potentially get certain data in the form of extension attributes. However that’s an exercise I will leave up to you if you decide you want to track that information.

That’s it for now. I hope you can find some use of this script in your environment. You can find the script here. Feedback always welcomed.

Update to macOS Upgrade Script

I’ve gone ahead and updated my OS Upgrade script for compatibility with macOS High Sierra (10.13). If you’re curious on how to use it, please read my blog post here. There’s one major change to report on other than the compatibility with the new OS installer app.

Filevault Authenticated Restarts

Currently, the macOS Installer does not support authenticated Filevault restarts. This creates a situation where your user would have to run the installer, wait until the restart, authenticate and then walk away. The process now makes it so that the user is prompted for their Filevault credentials before the upgrade even starts. This is so that the user can walk away and not have to wait for the installer app to prepare the computer for the upgrade.

The script will automatically detect if Filevault is turned on. If it’s not, then the user will not see the authentication prompt. I understand some folks might not like this so with that in mind, if you wish to disable this part of the script, comment out lines 521 – 524. I would have added more JSS parameters and made this an option you could disable, but I ran out of parameters to use (vote up this feature request for more JSS parameters).

Conversion to APFS

I’ve been asked to add an option to allow APFS to be turned off or on. I did enforce conversion to APFS using the command: --converttoapfs YES (if you want to disable it, just put NO instead of YES) early on in my update to my script. But ultimately after asking for feedback from other admins, I opted to not force it and just let the app installer take care of the logic on whether to upgrade the drive to APFS. The reasoning here is that Apple knows what conditions best support APFS and which ones don’t. However, I did make a comment in my script in line 500 for those who want to always enforce it or disable APFS conversion entirely. It would have been nice to make this an option that could be toggled with a JSS parameter, but like I said earlier, I ran out of JSS parameters to use (vote up this feature request for more JSS parameters).

Additional changes include:

  • new dialogs for Filevault authenticated restarts
  • new exit codes
  • code clean up

The script referenced above can be downloaded from my GitHub repo. Please let me know if you run into any issues or have any questions regarding the script.

startosinstall updated in macOS 10.12.4 app installer and can no longer target a volume

I recently blogged about my upgrade process with Jamf Pro. The script I had worked well with 10.12.3. One would assume it would work well with the 10.12.4 macOS app installer as well. However it appears that Apple has removed a flag. Specifically, you can no longer specify what volume you want to target for the installation.

The command that you could use previously in 10.12.3 looked like:

"/Applications/Install macOS Sierra.app/Contents/Resources/startosinstall" --applicationpath "/Applications/Install macOS Sierra.app" --volume / --rebootdelay 30 --nointeraction

In 10.12.4, it now looks like:

"/Applications/Install macOS Sierra.app/Contents/Resources/startosinstall" --applicationpath "/Applications/Install macOS Sierra.app" --rebootdelay 30 --nointeraction

Those are just examples of some of the flags you could use. Basically they’ve removed --volume /. All this to say I had to update my script to account for this. This led to a bunch of other code I saw that I could optimize. I have added some additional exit codes and added additional functions to reduce code re-use. The updated script can be downloaded from my GitHub repo. For instructions on how to use it, please refer to my previous blog post.

Another method for macOS upgrades via the JSS using Self Service

There are quite a few methods that people use to make macOS updates available to their end users. My method takes a little inspiration from those posts with a few differences. This time around I wanted to use the macOS installer app from Apple which has a neat little command line tool call startosinstall. There was no particular reason to use this method other than there were no requirements to install any particular packages post-install which you can do with a tool like createOSinstallerPKG. We had a few requirements:

  1. Computer has sufficient free drive space.
  2. User is not logged in to avoid the new iCloud Drive Document Sync feature.
  3. Ensure the user is plugged into a power source.
  4. Provide dialogs to give the user feedback such as a time estimate and dialogs on what to expect next.
  5. Make use of the JSS parameter to allow for customization and potential re-use for future operating systems.

Continue reading Another method for macOS upgrades via the JSS using Self Service