Menu Close

Reset User-deferred Jamf Pro Polices

Easily clear your users’ policy-specific deferrals when using Installomator with Jamf Pro

Background

Since fully embracing Installomator v10.0, which includes support for swiftDialog, we’ve been testing Mischa van der Bent’s approach to leveraging Jamf Patch Management Software Titles for versioning information, which, on the whole, seems to be working quite well.

“… keep in mind Jamf Patch Management and Installomator disagree at times on whether a new app is available.”

Adam Codega

However, allowing users to defer Jamf Pro policies introduces its own set of challenges.

Policy Deferral

Jamf’s official User Interaction with Policies documentation should be considered required reading before implementing this feature in production, but since none of us have time for that, here are the highlights:

Before a policy runs on a computer, the user is prompted to choose to have the policy run immediately or to defer the policy for one of the following:

  • 1 hour
  • 2 hours
  • 4 hours
  • 1 day
  • The amount of time until the deferral limit is reached

User-facing Messages

When a policy has a Deferral Type of “No Deferral” and you enter a custom message, the custom message will be displayed as a macOS Notification.

For policies with a Deferral Type but no custom message, the user will be presented a generic message:

A management task is scheduled to run now.
Choose when vou want to start the task.

A word of caution

Jamf’s documentation also states:

To avoid policy deferment issues and excessive re-runs, the deferment must not exceed the execution frequency configured for the policy.

In our experience, Deferment Duration will almost always exceed the policy’s Execution Frequency (due to the limited number of options for Execution Frequency), so deferred policies which also include a Trigger of Recurring Check-in should be considered as having an Execution Frequency of Ongoing, regardless of the specified Execution Frequency.

In other words, since deferred policies don’t write policy logs to the Jamf Pro server until the deferral window has closed and the policy has actually executed, if users are allowed to defer a policy with an Execution Frequency of “Once every day” (as illustrated in the above policy screenshot) and the policy includes a Recurring Check-in Trigger, the Jamf Pro server won’t have a policy log of the client-side deferral, so the policy will actually execute — and be quickly deferred — at every check-in (i.e., Ongoing).

Recurring Check-in Trigger

sudo jamf policy -verbose # Simulate a Recurring Check-in
 verbose: JAMF binary already symlinked
 verbose: Checking for an existing instance of this application...
Checking for policies triggered by "recurring check-in" for user "dan"...
 verbose: Checking for active ethernet connection...
 verbose: No active ethernet connection found...
 verbose: Removing any cached policies for this trigger.
 verbose: Parsing servers...
 verbose: Parsing Policy Installomator: Google Chrome Auto-Update (337)...
 verbose: The Management Framework Settings are up to date.
 verbose: Found 1 matching policies.
 verbose: Policy 'Installomator: Google Chrome Auto-Update' will not be executed because it was deferred by the user.
Checking for patches...
No patch policies were found.

Execution by Policy ID

sudo jamf policy -id 337 -verbose # Execute by Policy ID
 verbose: JAMF binary already symlinked
 verbose: Checking for an existing instance of this application...
Checking for policy ID 337...
 verbose: Checking for active ethernet connection...
 verbose: No active ethernet connection found...
 verbose: Removing any cached policies for this trigger.
 verbose: Parsing servers...
 verbose: Parsing Policy Installomator: Google Chrome Auto-Update (337)...
 verbose: The Management Framework Settings are up to date.
 verbose: Found 1 matching policies.
 verbose: Policy 'Installomator: Google Chrome Auto-Update' will not be executed because it was deferred by the user.

XML?

After the user has delayed a policy’s execution, the information is stored client-side in a .PLIST located at /Library/Application Support/JAMF/.userdelay.plist.

In the following example, the user selected a two-day delay.

<?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">
<plist version="1.0">
<dict>
	<key>337</key>
	<dict>
		<key>deferStartDate</key>
		<date>2022-12-09T00:11:03Z</date>
		<key>lastChosenDeferDate</key>
		<date>2022-12-11T00:11:03Z</date>
	</dict>
</dict>
</plist>

While I’m no XML expert, the repeated use of key makes parsing .userdelay.plist for a single delayed policy using common XML tools fairly challenging:

xmllint --xpath "count(//key)" /Library/Application\ Support/JAMF/.userdelay.plist
3

If you’re allowing three browser-related policies to be deferred …

<?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">
<plist version="1.0">
<dict>
	<key>337</key>
	<dict>
		<key>deferStartDate</key>
		<date>2022-12-11T21:33:55Z</date>
		<key>lastChosenDeferDate</key>
		<date>2022-12-13T21:33:55Z</date>
	</dict>
	<key>357</key>
	<dict>
		<key>deferStartDate</key>
		<date>2022-12-11T21:33:58Z</date>
		<key>lastChosenDeferDate</key>
		<date>2022-12-12T21:33:58Z</date>
	</dict>
	<key>359</key>
	<dict>
		<key>deferStartDate</key>
		<date>2022-12-11T21:33:40Z</date>
		<key>lastChosenDeferDate</key>
		<date>2022-12-16T21:33:40Z</date>
	</dict>
</dict>
</plist>

… the results are thrice as nice:

xmllint --xpath "count(//key)" /Library/Application\ Support/JAMF/.userdelay.plist
9

macOS 12.6.1

To add to the challenge, macOS 12.6.1 includes xmllint version 20904 which doesn’t play nice by including line breaks:

❯ sw_vers
ProductName:    macOS
ProductVersion: 12.6.1
BuildVersion:   21G217

❯ xmllint --version
xmllint: using libxml version 20904

> xmllint --xpath "/plist/dict/key/text()" /Library/Application\ Support/JAMF/.userdelay.plist
337357359% 

macOS 13.0.1

Version 20913 seems to work as one would expect:

# sw_vers
ProductName:    macOS
ProductVersion: 13.0.1
BuildVersion:   22A400

# xmllint --version
xmllint: using libxml version 20913

# xmllint --xpath "/plist/dict/key/text()" /Library/Application\ Support/JAMF/.userdelay.plist
337
357
359

plutil

Using plutil proved to be much easier for this use-case (and Armin has a nice write-up on Editing Property Lists with plutil).

# plutil -p /Library/Application\ Support/JAMF/.userdelay.plist 
{
  "337" => {
    "deferStartDate" => 2022-12-11 21:33:55 +0000
    "lastChosenDeferDate" => 2022-12-13 21:33:55 +0000
  }
  "357" => {
    "deferStartDate" => 2022-12-11 21:33:58 +0000
    "lastChosenDeferDate" => 2022-12-12 21:33:58 +0000
  }
  "359" => {
    "deferStartDate" => 2022-12-11 21:33:40 +0000
    "lastChosenDeferDate" => 2022-12-16 21:33:40 +0000
  }
}

Configuration

Complete the following steps to both report on and selectively reset user-deferred Jamf Pro polices.

A. Add the Policy Deferrals Extension Attribute to your Jamf Pro server

Manually create an Extension Attribute and use the following script:

#!/bin/bash
#######################################################################
# A script to determine the user-selected delay for Jamf Pro policies #
#######################################################################

plist="/Library/Application Support/JAMF/.userdelay.plist"
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/

if [[ -f "${plist}" ]]; then
    deferredPolicies=$( plutil -p /Library/Application\ Support/JAMF/.userdelay.plist | sed 's/[\"\{\}=>]//g; s/ +0000//g; s/^ *//g; s/deferStartDate /S:/g; s/lastChosenDeferDate /E:/g; 1d' )
fi

if [[ -z "${deferredPolicies}" ]]; then
    deferredPolicies="None"
fi

echo "<result>${deferredPolicies}</result>"

The output will be displayed as either “None” or a listing of Policy ID(s) with the Start date(s) and End date(s):

Policy Deferrals:337
S: 2022-12-11 21:33:55
E: 2022-12-13 21:33:55

357
S: 2022-12-11 21:33:58
E: 2022-12-12 21:33:58

359
S: 2022-12-11 21:33:40
E: 2022-12-16 21:33:40

B. Add the User-deferral Removal script to your Jamf Pro server
  1. Add the Jamf Pro Policy User-deferral Removal script to your Jamf Pro server
  2. Specify the following for Options > Parameter Labels
    • Parameter 4: Script Log Location
    • Parameter 5: Policy ID (Use "0" to clear all policy deferrals)
  3. Click Save
#!/bin/bash
####################################################################################################
#
# ABOUT
#
#    Jamf Pro Policy User-deferral Removal
#
####################################################################################################
#
# HISTORY
#
#   Version 0.0.1, 06-Dec-2022, Dan K. Snelson (@dan-snelson)
#       Original Version
#
#   Version 0.0.2, 10-Dec-2022, Dan K. Snelson (@dan-snelson)
#       Leveraged code from "Policy Delay EA" for displaying file contents
#
####################################################################################################



####################################################################################################
#
# Variables
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Global Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

scriptVersion="0.0.2"
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/
testFile="/Library/Application Support/JAMF/.userdelay.plist"
updateScriptLog="${4:-"/var/tmp/org.churchofjesuschrist.log"}"
policyID="${5:-"359"}"  # Policy ID to clear; use "0" for all



####################################################################################################
#
# Functions
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Client-side Script Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

function updateupdateScriptLog() {
    echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${updateScriptLog}"
}



####################################################################################################
#
# Pre-flight Checks
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Client-side Logging
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ ! -f "${updateScriptLog}" ]]; then
    touch "${updateScriptLog}"
    updateupdateScriptLog "*** Created log file via script ***"
fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Logging preamble
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

updateupdateScriptLog "\n\n###\n# Jamf Pro Policy User-deferral Removal (${scriptVersion})\n###\n"



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Confirm script is running as root
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ $(id -u) -ne 0 ]]; then
    updateupdateScriptLog "This script must be run as root; exiting."
    exit 1
else
    updateupdateScriptLog "Script running as \"root\"; proceeding …"
fi



####################################################################################################
#
# Program
#
####################################################################################################

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Remove User Deferrals
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

if [[ -f "${testFile}" ]]; then

    updateupdateScriptLog "\"${testFile}\" does exists; proceeding …"

    updateupdateScriptLog "Reading contents of \"${testFile}\" … "
    # echo "$(<"$testFile")" | tee -a "${updateScriptLog}"
    plutil -p "${testFile}" | sed 's/[\"\{\}=>]//g; s/ +0000//g; s/^ *//g; s/deferStartDate /S:/g; s/lastChosenDeferDate /E:/g; 1d' | tee -a "${updateScriptLog}"

    if [[ "${policyID}" == "0" ]]; then

        updateupdateScriptLog "Removing entire \"${testFile}\" … "
        rm -v "${testFile}" | tee -a "${updateScriptLog}"

    else

        updateupdateScriptLog "Removing user deferral for Policy ID \"${policyID}\" … "
        plutil -remove "${policyID}" "${testFile}" | tee -a "${updateScriptLog}"

        updateupdateScriptLog "Reading contents of \"${testFile}\" … "
        # echo "$(<"$testFile")" | tee -a "${updateScriptLog}"
        plutil -p "${testFile}" | sed 's/[\"\{\}=>]//g; s/ +0000//g; s/^ *//g; s/deferStartDate /S:/g; s/lastChosenDeferDate /E:/g; 1d' | tee -a "${updateScriptLog}"

    fi

else

    updateupdateScriptLog "\"${testFile}\" does NOT exist"

fi



# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Exit
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

updateupdateScriptLog "Goodbye!"

exit 0
C. Configure User Interaction for auto-update Installomator policies

Configure user interaction for your auto-update Installomator policies as desired.

D. Configure User-deferral Removal for manual Installomator policies
  1. Clone your auto-update Installomator policy
  2. Select the Scripts payload and add the Jamf Pro Policy User-deferral Removal script, specifying the following Parameter Values
    • Script Log Location: /var/log/com.company.log
    • Policy ID (Use “0” to clear all policy deferrals): Policy ID of auto-update policy
  1. Use the following for Self Service
  1. Remove all User Interaction settings
  1. Click Save

Conclusion

The payoff for your end-users is that if they have previously delayed your auto-update policies, they can simply login to Self Service and update at-will.

Product Issue

If you find you’re frequently using policy deferrals, you may wish to open a case with Jamf Support to inform them you’re impacted by the following (currently unlisted) Product Issue:

Management Action (User Interaction) notification does not contain the icon uploaded to the Policy.

PI103485
Posted in Jamf Pro, macOS, Tips & Tricks

Related Posts