Handling major upgrades and minor updates for macOS with Jamf

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 for major OS releases and every 2 months for regular minor updates (or more frequently in some cases). 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. I’ve previously written two other tools that tackle the issue in two separate ways:

Because the use cases are similar enough I combined the two scripts into one. The other scripts mentioned in those blog posts will no longer be maintained.

The idea behind this new script is that it alerts the user that the OS they are running is no longer supported through Jamf Helper dialogs. It’s important to try and provide a little bit of flexibility in how often the user gets notifications. Rather than forcing updates after, the script allows you to control the pace of notifications the user receives to perform the OS upgrade/update by letting you set different dates of importance. 

This script can be used to handle either major OS upgrades or minor OS updates. By default, if you do not set any parameters or variables, the script will check for minor OS updates. One thing to note is that the script will not do BOTH at the same time. In other words, you will need one policy in Jamf Pro when this script is used to handle minor OS updates and a second policy to handle major OS upgrades.

This script does try to account for the fact that it requires user interaction. That means there are a few checks in place to try and give the user the best chance to perform the upgrade/update before they get a notification:

  • Ensure a user is logged in.
  • Power source is connected when an update is being forced.
  • No app is opened that has a display sleep assertion active (e.g. web conferencing apps, presentation tools, screen sharing tools, etc. tend to make this assertion to prevent the computer from going to sleep)
  • Idle time of the computer.

If the user opts to proceed with the upgrade/update, the default experience will take the user to the Software Update preference pane to perform the minor update or the Mac App Store to perform the major upgrade or a custom policy that you’ve configured to do the OS upgrade. The one exception to this is If you do supply custom policy name for major OS upgrades (discussed a little later), the user will be directed to Self Service as an alternative to perform the major OS upgrade instead of going through the App Store.

With each notification, there is a “More Info” button in the Jamf Helper dialog which will always open up a URL. This URL should provide the user with instructions on how to proceed to update. Because it’s a URL, you could also provide a Self Service URL as well. If you do not provide a URL, it will simply default to Apple pages:

As you can tell, this script mostly attempts to get the user to take action for most steps. However if there are minor OS updates available that do not require a restart, an attempt will be made to install them through the command line so long as there are not other updates that do require a restart.

There are three optional dates that you can supply to the script that will dictate when the user receives the notifications:

  • Start Date: This is the date when the user will start receiving notification with the option to delay when
    they will receive the next reminder.
  • Nag Date: This is the date when the user will start receiving notifications without an option on when to receive the next reminder. This allows the user to essentially turn things up and nag the user to update a bit more aggressively. The frequency between notifications will be 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: Once this date has been reached, the user has X amount of attempts (3 by default) left before they are forced to upgrade. The user will be reminded every 24 hours until they’ve exhausted their X postponements. Similar to the Nag Date, this provides a notification to the user where they cannot select when they will be reminded. If you were previously handling minor OS updates using the script in this blog post then you can emulate the same behavior by simply setting an End Date.

Dates can be supplied in either epoch seconds or string format (e.g. Sep 03 12:34:56 -0400 2019). Note that the date is evaluated against the computer’s local time. 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.

If you’ve configured an End Date and the user has no more deferrals/postponements left, the script will take a few actions:

  • if we’re dealing with major OS upgrades and minor OS updates on Apple Silicon Macs, the user will simply be given a final chance to perform the upgrade/update before the computer is shut down. The user will get at least an hour (or longer if you’ve set the Time Out period to be longer) to perform the OS upgrade/update once they’ve reached this state. On Apple Silicon Macs, you cannot perform a command line install without user interaction which is why a command line install is not attempted.
  • if we’re dealing with minor OS updates on Intel Macs, a command line install will take place.


Given the above overview, here are the Jamf script parameters:

  • Parameter 4: Optional. Enter “major” or “minor” which will dictate if the script alerts against a necessary major OS upgrade or minor OS update. If not set, the default will be “minor.”
  • Parameter 5: Optional. The custom trigger name for a Jamf policy that will initiate the major OS upgrade.
  • Parameter 6: Required. 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 have been exhausted.
  • 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.
  • 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.
  • AssertionsToIgnore: Comma separated string of process names to ignore when evaluating display sleep assertions (e.g. “firefox,Google Chrome,Safari,Microsoft Edge,Opera,Amphetamine,caffeinate”). If a process you’ve listed has a display sleep assertion, the script will resume as normal rather than exit. This may result in valid display sleep assertions being ignored. If left blank, any Display Sleep assertion detected during execution of the script will be honored.
  • MoreInfoURL: Optional. A URL that points the user to a page on the macOS upgrade/update process. If left blank, this will default to either https://www.apple.com/macos/how-to-upgrade/ (for major OS updates) or https://support.apple.com/HT201541 (for minor OS updates). You can optionally use a Self Service URL as well.
  • TimeOutinSecForForcedCLI: Optional. Time out period for CLI installs. This is useful for situations where you want there to be a shorter time out period than normal. If left blank, defaults to the regular default time out period.
  • 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 just generically enough that that only a few words are swapped out based on whether the user is performing a major OS upgrade or minor OS update. The user is always given instructions on how to perform the upgrade/update on their own since we want the user to take action where possible. The one exception to this is If you provide a custom policy name for doing major OS upgrades, the user will be pointed to Self Service.

However, I understand my verbiage may not work for all organizations. 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.
  • FinalCallNotificationForCLIUpdate: The text used during a CLI update when End date has
    been reached with no more deferrals left.
  • FinalCallNotificationForGUIUpdate: The text used during a forced GUI update when End
    date has been reached with no more deferrals left.
  • ShutdownWarningMessage: The text used just before shutting down.
  • BackgroundInstallMessage: The text used when CLI updates are being actively performed.

Exit Codes

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: Insufficient space to perform update.
  • 13: /usr/bin/softwareupdate failed.
  • 15: Incorrect property list type used. Valid types include: “array” “dict” “string”
  • “data” “date” “integer” “real” “bool”
  • 16: Invalid date entered.


Below are a few screenshots so you can get an idea of what the notifications look like.

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

minor OS update
major OS upgrade (if you point to a custom Jamf policy the instructions will point to Finder > Self Service)

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.

minor OS update
major OS upgrade

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

minor OS update
major OS upgrade

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.

minor OS updates
major OS upgrade

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.

minor OS update (on Intel)
minor OS update (on Apple Silicon)
major OS upgrade

Shut Down notification which comes up if the user fails to proceed with the OS upgrade/update:

Configuring 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 > OSUpdateNotifier.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: Mode (“major” or “minor”)
Parameter 5: Custom Trigger For OS Upgrade Policy (optional)
Parameter 6: Custom Trigger For This Policy (required)
Parameter 7: Start Date (eg Sep 03 12:34:56 -0400 2019)
Parameter 8: Nag Date (eg Sep 03 12:34:56 -0400 2019)
Parameter 9: End Date (eg 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 in the policy:

1: Trigger: Recurring Check-in and Custom (this is a required in parameter 6)
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/updated. For example, if you want to use this script for doing major OS upgrades, you could create a smart group with criteria that uses “Operating System Versions that are less than 11.0” since macOS 11 is the latest at the time of this post. Alternatively, for minor OS updates, you can create smart groups for the latest OS (e.g. “OS Version is greater than or equal to 11.0″ AND “OS Version is less than 11.2.3″; 11.2.3 is the latest version as of this post). Or maybe you can scope it against “OS Version is greater than or equal to 11.0″ AND “Number of Available Updates more than 0″. These are just basic ideas. As a Jamf admin in your org, you’ll have a better idea of how to better scope against your devices you want updated.

Because the script will run at every check-in, one thing to keep in mind is that the script does rely on checking for updates through command line: softwareupdate -l . To avoid having the system repeatedly check for updates at every check-in, the script will only check for updates after 4 hours since the last time it checked.

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.

Other Mentions

Here are other tools that may be of interest to you which should also work in achieving a similar purpose. These should work with any management tool including Jamf Pro:

No more sudo with softwareupdate or unattended updates on macOS running on Apple Silicon

Today, Apple announced new Macs running on Apple Silicon running on macOS Big Sur. Let’s talk about a change that should concern IT admins. On Apple Silicon Macs, you get the following message when you try to run:

sudo softwareupdate -i

Using softwareupdate to install updates on Apple Silicon should not be run with sudo

And immediately after, you get prompted to authenticate with a GUI prompt.

Two things to note here:

  1. Almost all software management tools for macOS are built to run as root and run as sudo.
  2. Never before has an update required a user to confirm an update by authenticating their credentials. This makes unattended updates impossible.

What are the methods to get users to update their operating system?

There are a few different methods that have been used by admins to get users to update their Macs.

  • Schedule an OS update using the MDM command. Apple approved.
    • This is unreliable. It does not work consistently on either iOS or tvOS, let alone with macOS. There is also not a very good user experience attached. If the command goes through successfully, the user simply sees their computer restart immediately.
  • Manage automatic update settings though a configuration profile. Apple approved.
    • This is not reliable either. There are specific requirements that need to be met in order for updates to automatically install that Apple has not documented. Running applications can also easily prevent an OS update from taking place. This also seems to take place in the middle of the night when laptops may not be powered on.
  • Force updates using the command line tool: softwareupdate.
    • Up until Macs with T2 chips were introduced, this was the most reliable method. With T2 Macs, this has been a less reliable particularly when bridgeOS updates are part of the equation.
  • Constantly notify the user that they need to update their OS using custom tooling.
    • Perhaps the most reliable method today since this is the method that Apple has designed their software update process around for consumers. This has the drawback of annoying your end users. This also partially relies on being able to read what updates may be available through softwareupdate.
  • Using Zero Trust security policies, block end users from accessing company resources until they’ve updated their OS.
    • This works great if your environment has been setup with all resources being gated behind networked resources and your company has been able to implement zero trust policies. However if there are resources that are not gated behind zero trust policy, you may end up with end users that never update.
  • Deploy the latest full OS installer every time a new update is released.
    • To be fair, I don’t know of anyone using this method. But it is an option nonetheless. Updates will likely take 30-35 minutes if you go this route. Big Sur is supposed to improve and speed up the update process. Deploying the full OS installer would likely undo any of these update improvements. This also would mean deploying ~12GB installers every time there is an update as opposed to the ~5GB downloads you’d be dealing with (combo updates have largely been hovering around this size). Another thing to consider is that it may be possible that a full installer is skipped on certain updates. And there are other side effects of performing a full install vs an update.

Each method has their pros and cons. They all work great in environments where a user is assigned to them. However, they don’t really address the methods in which 1) there is no human available to install an update or 2) where the end user simply is not complying with the notifications or requirements.

Apple has not provided better management options for macOS updates. In fact, with each major OS release they’ve gotten rid of the options that have been available to us. You can no longer run your own Apple Software Update Server. You can no longer ignore specific updates. The command line tool softwareupdate has become less reliable depending on the conditions in which you tried to run it on Macs with T2 chips. Now it seems

I (and I’m hoping other IT admins, too) have requested better options and improvements to the current management tooling, but Apple has yet to implement any of them. As of today, you have the two options listed above which involve sending MDM commands or a configuration profile. Additionally, you have the ability to delay updates up to 90 days where major and minor OS updates are treated the same. But that doesn’t achieve the purpose of getting devices on to the latest OS version.

In our environment, this would be quite disastrous as we have computers that are online, but unattended. There’s no reliable automated method to update Macs to a specific OS version with a good user experience.

The request to Apple: better update enforcement tooling

From an IT admin perspective, I do not believe what is being asked would conflict with Apple’s goal of having users running a secure operating system.

I am (and hopefully other admins are too) just asking for a method that allows us to force a device to be updated by a set date using a configuration profile. We cannot rely on MDM commands that simply may or may not reach a device. This workflow should also account for situations where a user may be turning on a computer for the first time after the deadline has passed, they should have X amount of reminders before the update is forced at this point.

The UX around this request should be handled by Apple (a company that prides itself on its UX) so that IT admins don’t have to deal with custom tooling. This would result in the user getting reminded regularly and then forced to upgrade if the deadline has passed. I believe the above request would work well for other Apple platforms like iOS and tvOS as well.

Final Thoughts

This is not the first time I’ve written about managing software updates on macOS. I cannot help but wonder how a company like Apple can promote and put so much focus on the security of their products and yet not make proper management tooling to keep those same products up-to-date when used in a business environment? Microsoft has figured out how to manage OS updates on Windows and Google has figured it out on ChromeOS so why can’t Apple do the same for its various platforms? Why does Apple make it more difficult for companies to keep devices they have purchased/owned up-to-date?

As of right now, I’m not entirely sure how we’ll be able to manage macOS updates going forward on these machines that are unattended. In the grand scheme of things, our organization is probably insignificant to Apple in terms of the amount of money we spend. I’m writing this in hopes that other admins provide feedback to Apple because this makes the macOS platform considerably harder to keep secure (on the latest version) in business environments. Jamf has a good article that discusses the various options for providing Apple with feedback. Please let your fellow admins know and start filing that feedback with Apple.

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.

This iPhone is supervised and managed by … Learn more about device supervision…on a personal iPhone?

In some organizations, some employees are provided a company purchased iPhone and then allowed to keep it after a certain time has passed. The first thing that you as an IT administrator want to do is most likely ensure the device is no longer managed and contains no company data. Apple walks you through the steps of releasing a iOS device from Apple Business Manager with a final note stating: “After a device is released, it must be erased and restored.

You would think this would be very simple, but unfortunately it’s not. The process of personalizing an iPhone can be quite convoluted in some scenarios because there are some remnants that get left behind if you try to simply restore the data on the iPhone after an erase and restore.

You might argue, the user should not keep any personal data on a company phone, but the lines get blurred especially as most people prefer to carry one device. It’s not unreasonable for someone who is getting gifted a company phone to perhaps want to retain the settings, apps and data on the phone that are personal without having any of the company data on it restored. Sure, you can just tell the user to erase and setup the phone as new, but reconfiguring an iPhone can be quite a lot to ask of someone! In any case, this blog post is not about the merits of mixing personal and corporate data on one device so that’s all I’ll say on the matter.

The problem: This iPhone is supervised…even after a restore!

Before I continue, note that the following scenario was tested in iOS 12 & iOS 13 and with a MDM profile that was set to be unremovable.

There are going to be a lot of steps here for you to reproduce the issue and a subsequent number of steps if you want to work around the issue.

Continue reading This iPhone is supervised and managed by … Learn more about device supervision…on a personal iPhone?

macOS 11 and Semantic Versioning

Last week at WWDC 2020, Apple announced macOS 11 (Big Sur). One of the questions that has come up in the MacAdmins Slack come up is whether macOS is really macOS 11 or macOS 10.16. Right now, we have nothing more to go by then what is in the betas which I won’t discuss, but it’s my understanding that Apple does indeed intend to go by macOS 11 in the end.

Many admins who write scripts that check for the OS version tend to rely on sw_vers -productVersion which would normally spit out a value such as 10.15.5. The first of the 3 digit groups has not changed in 20 years which means many scripts simply don’t check for that change. That leaves the last 2 digit groups which often get referred to as major and minor versions.

With macOS 11, it’s going to be important to check all 3 digit groups for backwards and forwards compatibility. We do have a point of reference on how these changes should go if we look at iOS, iPadOS, tvOS, and watchOS which have 3 digit versions.

This changes things a bit and I’m proposing that it’d be great if other IT admins used similar naming convention when referring to the the variable names representing each digit group should you need to split each value up.

I propose using the following semantic version guidance on how to refer to 3 digit group separated versions from https://semver.org/:

1. MAJOR version when you make incompatible API changes,
2. MINOR version when you add functionality in a backwards compatible manner, and
3. PATCH version when you make backwards compatible bug fixes.

Obviously, we cannot know for sure what next year may bring, but I’m hoping that Apple sticks to these changes and continues to increment the macOS major version as it has on its other platforms. This would mean that macOS 11 would then move on to macOS 12 by the end of 2021.

Presumably if the same behavior from iOS comes to macOS, the days of “supplemental updates” should be no more. If macOS 11.0.0 releases today and a “supplemental update” needs to come out a few days later then that’s really just a patch and the macOS version would increment from 11.0.0 to 11.0.1. However when macOS gets its minor OS releases that tend to come out every 6-8 weeks then we might go from say 11.1.0 to 11.2.0. That’s a simple change to track in most version comparison logic found in programs or scripts.

To highlight the issue a bit more clearly, a “supplemental update” would in the past would usually mean we’d go from macOS 10.15.5 to macOS 10.15.5 with the only way to know the difference being the build number which has always been incremented. Although not impossible, it’s a little less straight forward to compare a mix of alphanumeric characters vs integers. Here’s a really good blog post by Armin Briegel: https://scriptingosx.com/2017/11/on-the-macos-version/ that covers OS versioning and build numbers in more detail.

These are going to be very welcomed changes going forward because right now once macOS has hit the last minor update the only way to determine if it was on the latest version was to look at the build number of the OS.

However in shell scripting languages, it is much easier to just focus on splitting the version number into different groups since most admins will be familiar with the marketing version compared to dissecting the build numbers. Here is one way to do this going forward in a shell language:

Major Version: /usr/bin/sw_vers -productVersion | /usr/bin/cut -d . -f 1

Minor Version: /usr/bin/sw_vers -productVersion | /usr/bin/cut -d . -f 2

Patch Version: /usr/bin/sw_vers -productVersion | /usr/bin/cut -d . -f 3

Here’s a shell example on how this might be used:

[[ "$os_major_ver" -ge 11 || "$os_major_ver" -eq 10 && "$os_minor_ver" -gt 12 || "$os_major_ver" -eq 10 && "$os_minor_ver" -eq 12 && "$os_patch_ver" -ge 3 ]]

In this example, I’m testing for a condition that means either:

macOS major version is greater than or equal to 11
macOS major version equals 10 AND macOS minor version is greater than 12
macOS major version equals 10 AND macOS minor version equals 12 AND macOS patch version is greater than 3

In any case, this blog post is mostly going out as a reminder to admins that if you haven’t already heard of this coming change, I would suggest that you review any scripts that rely on checking the OS version as they may need some updating.

Managing Google Chrome Auto Updates

Google Chrome has a few ways in which you can manage its update mechanism which I wanted to post about.

Google Chrome Enterprise Installer

If you’re not already doing so, I recommend you start using the enterprise Google Chrome installer package. This package installs Google Chrome but also configures the Google Software Update Agent which many admins were doing with a separate script. Once Chrome is registered with the Google Software Update agent, there’s a few options for managing it to ensure that Google Chrome stays up to date.

Options for Managing Google Software Update/Keystone

Configuration Profiles

Here is an article that talks about how to manage Google Chrome updates: https://support.google.com/chrome/a/answer/7591084

By generating a configuration profile that manages the domain com.google.Keystone.plist, you can ensure that Google’s auto update mechanism is configured according to your needs. Keep in mind that the Google Software Update Agent can manage the updates for various Google apps on macOS. Unfortunately, the Google Software Update Agent won’t necessarily force users to do anything as it’s pretty transparent in the background and its job when unmanaged is just to check for updates and stage them for installation on a relaunch of a Google application. Once a registered Google application is relaunched, it will ensure the app is running the latest version.

Chrome Browser Cloud Management

Here is an article that talks about how to manage Chrome using the Chrome Browser Cloud Management console: https://support.google.com/chrome/a/answer/9116814

This would allow you to manage the Chrome web browser from a single console. To do so, you would generate a token that would get pushed out through a configuration profile to your Macs. Once enrolled, you can manage the same settings through the Google Admin console for each Chrome web browser. Please note that the user does not need to be signed in with their Google account for this option to work.

You are able to combine both options if you wish: 1) push configuration profiles from your MDM server to manage settings and enroll the device into Chrome Browser Cloud Management and 2) use Chrome Browser Cloud Management for reporting only.

Managing Update Notifications

Fortunately, Google has provided a way for admins to notify users that a Chrome update is pending and force them to relaunch. This article talks about how to manage relaunch notifications: https://support.google.com/chrome/a/answer/7679871?hl=en&ref_topic=9023245


Tells users to relaunch Chrome Browser or restart their device running Chrome OS to get the latest update. Choose one of the options:

Relaunch recommended—Users can close the notification and keep using the old version of Chrome Browser or Chrome OS until they choose to relaunch Chrome Browser or restart their Chrome device.
Relaunch required—Users can close the notification but will see a recurring message that Chrome Browser will automatically relaunch or their Chrome device will restart after a certain time. Use the RelaunchNotificationPeriod policy to set the relaunch time (details below).

Unset: On the toolbar, the More icon More changes to indicate that an update is available but users aren’t forced to relaunch.


Sets the time period, in milliseconds (ms), that a user is repeatedly notified to relaunch Chrome Browser or restart their Chrome device to apply an update.

Unset: The default time period is 604,800,000 ms (7 days).

In our environment, we’ve got it set to  86400000 ms (or 24 hours). It has worked out pretty well for us. As an aside, I have no idea why Google chose to work with milliseconds.

Here is an example screenshot of what the notification looks like in Google Chrome if you set it to require a relaunch in 4 days:

Screen Shot 2019-06-21 at 9.39.38 AM

When does Google Software Update run?

The last part of the equation to account for is controlling when Google Software Update Agent runs. At the moment, there is no way to do so. Google Software Update Agents runs based on a launch daemon in /Library/LaunchDaemons/com.google.keystone.daemon.plist and 2 launch agents in /Library/LaunchAgents/com.google.keystone.agent.plist and /Library/LaunchAgents/com.google.keystone.xpcservice.plist.

Here are my observations:

  • The agent will automatically check for updates when the user logs into macOS.
  • The agent will automatically check for updates when Google Chrome is launched. However, it will only install any updates after a relaunch of Chrome.
  • The agent is supposed to automatically check for updates every 3623 seconds (this may be randomized on each device). As someone who does not use Chrome, I found that the app remained unpatched for days even when updates were definitely available which leads me to believe that this is not checking aggressively enough or there are some strings attached. I purposely did not launch Chrome to see how long it would take for it to auto update.

This becomes a bit of a problem because most people rarely restart their web browser, let alone log out and log back into their computer. I wanted a more guaranteed way that would ensure that Chrome was at least regularly checking for updates in the background. The idea here is if someone is not using Chrome then I want it to be updated as soon as they do decide to launch it the next time. I don’t want to try and check for updates in the background only when the browser is launched.

I did a bit of searching and found the following discussion on Jamf Nation which shows you can run the following command as the user (it will not work if you run this as root or a user other than the one in the current session):

"/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/Resources/GoogleSoftwareUpdateAgent.app/Contents/MacOS/GoogleSoftwareUpdateAgent" -runMode oneshot -userInitiated YES "$@"

That eventually led me to create a launch agent that I could then deploy myself to devices:

<?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”&gt;
<plist version=”1.0″>
I was able to push this out through a simple package that installed this at /Library/LaunchAgents/ with permissions set to 644 and owner/group set to root:wheel. The idea here is that the launch agent will run every 21600 seconds (or 6 hours). I chose 6 hours because it would at least ensure that the Google Software Update agent checks for an update at least once a day during the work day. Feel free to change those values for yourself.
If you think I missed anything or have something useful to add, feel free to write in the comments. I hope you’ve found this at least somewhat informative.

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

Reset the macOS printing system through the command line

Sometimes you need to reset your printing system to resolve some weird issues with one or more of your print queues. Prior to macOS Catalina, one way to reset the printing system was through the GUI. For example in macOS Mojave, you could go through System Preferences > Printers & Scanners and right-click (cmd + click) the list of printers and select “Reset printing system…” from the contextual menu.

If you’re reading this blog post you’re probably interested in automating that task. Back in 2015, I set out to find a way to do this programmatically so that it could be scripted. I shared the script on JamfNation at the time. Today, the logic and script still works, but it sure would be nice if it wasn’t needed in the first place.

With macOS Catalina, Apple silently introduced a new option in their printtool command line tool to reset the printing system. Type the following in Terminal:

/System/Library/Frameworks/ApplicationServices.framework/Frameworks/PrintCore.framework/Versions/A/printtool --reset -f

And just like that, all your print queues should disappear! There’s not much more to this blog post than that. I just wanted to document this new option because it hasn’t really been documented anywhere that I can tell. And it’s a handy one liner that may come in handy when troubleshooting. Hope this is helpful to you in some way. I’ve also gone ahead and uploaded my original script to Github as well.

Preemptively granting permission to Camera and Microphone in macOS

Starting with macOS 10.14, Apple introduced the requirement that applications requesting access to certain APIs would require permissions from the end-user. This is commonly referred to by other IT administrators are Privacy Preference Policy Control (PPPC) or Transparency Consent Control (TCC). The intent is to make the end user aware of what the application they are using requires access to. In 10.14, the list of services that could be whitelisted included 25 TCC services and it has grown to 39 services. From a consumer perspective, the can be a good thing, albeit exhausting, as you need to essentially provide access to multiple services for individual apps.

Apple to their credit has allowed IT administrators to manage most of these services that applications require in order to function by use of configuration profiles when your device is enrolled into an MDM server (whether user approved or via DEP). However for 4 services (Microphone, Camera, ListenEvent, and ScreenCapture), Apple decided that even IT administrators on enterprise-owned devices cannot preemptively provide allow access to these services (one can only deny access). This complicates things because end-users often need to join video conferences on the fly and those applications typically require access to the microphone and camera. If the user gets bombarded with different alerts to provide access, they may accidentally deny it and not be aware how to resolve the issue. Every application/developer is on their own in terms of how to best communicate how to resolve accidental denials of apps to functional services. And for some services, it is implied that you need to quit the app and restart it. Yes, you need to get out of the meeting and re-join it. This becomes an IT support issue as you can imagine.

It’d be great if Apple would provide proper options to allow these services on enterprise-owned devices that are supervised by an MDM server. You can submit feedback to Apple at http://feedbackassistant.apple.com.

To cut to the chase, I was alerted to this article from Zoom last week where it seems that Zoom is recommending that you provide full disk access to Zoom Rooms so that it can then provide itself Camera and Microphone access. Shame on Zoom for doing this since this is after all how we got here in the first place (Dropbox previously got caught doing similar stuff in 2016, but quickly rectified it). But I do get it to some extent. From their perspective, they are concerned about providing as seamless of an experience as possible. And the same is true of IT administrators to some extent. Computers are tools for people to get work done. It shouldn’t be super complicated to join video conference meetings on your computer in 2019. But Apple has made it so.

Since the cat’s out the bag, I made a script that provides camera and microphone access. It was designed to work with Jamf Pro which automatically has granted its binaries full disk access when its MDM profile has been user approved (or installed via DEP). This allows the script to make the modifications to the user’s TCC.db. I tested this against Microphone, Camera, ListenEvent, and ScreenCapture services (kTCCServiceMicrophone, kTCCServiceListenEventkTCCServiceCamera, and kTCCServiceScreenCapture). The last two don’t work just so you’re aware.

To use this script, add it to your Jamf Pro server and make use of Jamf Pro Script Parameters:

Parameter 4: is used to provide the full path to the application (e.g. /Application/Firefox.app)
Parameter 5: is used to provide the TCC service name. See $tcc_service_list array for valid entries. Of importance to you will be: “kTCCServiceMicrophone” and “kTCCServiceCamera

The script can be found on my GitHub repo. Feedback/improvements always welcomed.

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