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:
- handling minor OS updates: https://babodee.wordpress.com/2019/07/23/handling-macos-software-updates-with-jamf-pro/
- handling major OS upgrades: https://babodee.wordpress.com/2019/09/19/jamf-pro-os-deprecator/
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:
- https://www.apple.com/macos/how-to-upgrade/ (for major OS updates)
- https://support.apple.com/HT201541 (for minor OS updates)
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.
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.
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.
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.
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: