A better way to allow users to postpone Jamf policy execution :sunglasses:
Better Jamf Policy Deferral
Update for macOS 12.3
macOS Monterey 12.3 removes the Python 2 runtime historically bundled with the operating system. Better Jamf Policy Deferral previously relied on that "system" Python installation.
You must now install your own Python 3 runtime. I strongly recommend the "macadmins Python" distribution.
Better Jamf Policy Deferral has been updated for Python 3 compatibility, and expects that you're using macadmins Python. The shebang points to /usr/local/bin/managed_python3. If you're installing your own Python 3 runtime elsewhere, be sure to update the script accordingly.
About
Sometimes, with Jamf Pro, you need to give your users the option to delay the execution of a policy until a more convenient time. Typically, this policy might involve a reboot or some system change that would interrupt a user and present an annoyance during their work day.
The JSS provides built-in policy deferall options under the "User Interaction" tab in a policy's configuration (see the docs on "User Interaction in JSS v9.9x).
In use, it looks like this:

Maybe your users understand what a "management task" is or can somehow divine what "id:264" might do to their Mac, or why they might choose a time to start the task. Mine cannot. No options are available to customize the verbiage or presentation.
Further, you must specify a hard deadline date by which the policy must run; there's no way to specify a "floating" deadline. If a computer falls into the scope of a deferral-enabled policy after the deferral deadline, the user is never presented the choice to delay execution.
Long story short, the built-in deferral is inflexible.
Enter Better Jamf Policy Deferral.

Important: Behavior when the system is "off" at the scheduled deferral time
As an important consideration, be aware that if the system is off (i.e. the Mac is shut down) at the time a deferred action is scheduled to run, the action will not run when the system is turned back on.
This is a limitation of launchd jobs.
If you schedule alaunchdjob by setting theStartCalendarIntervalkey and
the computer is asleep when the job should have run, your job will run when
the computer wakes up. However, if the machine is off when the job should
have run, the job does not execute until the next designated time occurs.>
-- https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html
I am aware of the potential issue this creates but have not yet developed a suitable workaround. I am open to any suggestions, though! Open an issue or better yet, a pull request if you have ideas.
A Word on the Philosophy Behind Deferrals
Why do you want to allow a policy's action(s) to be postponed?
The only acceptable answer is "because those actions present a burdensome interruption for the user, but are required."
Some examples are:
- A policy that requires your user to reboot their Mac, such as updating the
- To enable FileVault disk encryption.
Limit yourself to only a couple policies with deferrals enabled. Do not set every policy to allow deferral because it looks โ on the surface โ to provide your users with "control" or autonomy.
How It Works
Better Jamf Policy Deferral (BJPD) is a Python script that uses the built-in jamfHelper tool to present your user a customizable GUI allowing them to postpone execution of a policy to a more convenient time.
Behind the scenes, BJPD writes an on-the-fly LaunchDaemon configured to call a custom policy trigger at the time selected by the user.
Using Better Jamf Policy Deferral
To better illustrate the workflow, this guide explains requiring a user to install all available Software Updates.
Setting Up the Script
- Customize
better-jamf-policy-deferral.py, modifying the variables as you
- Add
better-jamf-policy-deferral.pyto your JSS under Settings > Computer
Creating Policies
BJPD requires two separate policies. A "prompt" policy presents the GUI and handles creating a LaunchDaemon to execute your desired actions at a later time. An "execution" policy actually executes your actions, then cleans up after itself.
Execution Policy
- Create a new Policy with a sensible name. Here, we'll use "Install All
- Set the appropriate options like "Site" and "Category" in the General
- Set a Custom Trigger and make note of this value. We'll use
LDJAMFTRIGGER variable you set in better-jamf-policy-deferral.py (or
that you specify as a parameter value in your "prompt" policy).
- Add a "Scripts" payload and configure the
better-jamf-policy-deferral.py
- Configure other policy payloads to perform whatever actions you require. For
Prompt Policy
- Create a new Policy and give it an appropriate name. I prefix mine with
- Set the appropriate options like "Site" and "Category" in the General
- Add a "Scripts" payload and select the
better-jamf-policy-deferral.py
- Set an appropriate scope. You should be using Smart Groups to determine the
Notes and Considerations
BJPD Always Writes a LaunchDaemon
Even if your user chooses to start the policy "now," BJPD will still write a LaunchDaemon. In this case, the LaunchDaemon is written with the RunAtLoad key instead of a future-dated StartCalendarInterval key. This ensures the "execution policy" called by the custom trigger is properly queued up, and the "prompt policy" can complete its execution.
Blocking Apps
The BJPD workflow allows you to specify a list of "blocking apps" โย a la Munki โ that, if running, will cause the process to exit silently.This is useful for...not angering your users. For instance, if a professor or sales rep is doing a Keynote presentation, you don't want to interrupt by throwing your GUI on screen.
App names listed in the BLOCKING_APPS list in better-jamf-policy-deferral.py will be checked to see if they're running prior to any GUI elements appearing on screen.
The default list contains the two most common slide presentation apps:
BLOCKING_APPS = ['Keynote', 'Microsoft PowerPoint']
Edit the list to remove apps, or add apps that should additionally halt the display of GUI elements.
Be sure to test any new blocking apps you add to ensure they work as expected.
Note: This is a "rough" check that cannot detect if, for instance, Keynote is in full-screen presentation mode vs. simply open and running. A user could indefinitely delay execution of the policy if they leave a blocking app open all the time. In my environment, I'd rather be safe than receive the angry phone call. If you know a better way, fix it and send a pull request!
Policy Scope and Frequency
For deferral "Prompt" policies, I set the policy frequency to "Ongoing." This will cause the script policy to execute multiple times if a user chooses to defer it past the next recurring check-in. BJPD does, however, check for the existence of a deferral LaunchDaemon on the client prior to displaying any GUI elements, so the user will not see the GUI multiple times.
Support
Please open an issue on this repository if you have questions.