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.