Run any Jamf Pro policy at the next user login or computer reboot
Background
Recently, we had a need to run a particular Jamf Pro policy only the next time the computer rebooted.
Having previously created Recon at Reboot, I started on a modification for this one-off need. About a third of the way into the modifications, a Heaven-inspired question came to mind:
Why don’t you write a script to execute any Jamf Pro policy at the next reboot?
Built-in Options
For policies which should always execute at login or reboot, you can leverage Jamf Pro’s built-in options:
When you’d prefer to not duplicate polices just so they’ll execute at the next login or reboot, the following “trigger” scripts may prove helpful.
Overview
Workflow
For a detailed workflow, see the Trigger Policy at Login or Reboot screencast.
- Add both “trigger” scripts to your Jamf Pro server
- Create a new (or edit an existing) policy
- Add the “create” script to the policy’s Script payload
- Specify the preferred Launch Option
- Edit the policy which you’d like to execute at the next login or reboot
- Add the “delete” script to the policy’s Script payload
- Specify the same Launch Option information as in Step No. 2
Launch Option
The following table describes the differences between using the login
or the restart
Launch Option:
Launch Option | Login | Reboot |
---|---|---|
.plist Location | ~/Library/LaunchAgents/ | /Library/LaunchDaemons/ |
Program Arguments | open jamfselfservice:// … "${id}" | jamf policy -event "${trigger}" |
Run At Load | true | true |
Scripts
Add the Trigger Policy at Login or Reboot – Create script to your Jamf Pro server
- Add the
Trigger Policy at Login or Reboot - Create
script to your Jamf Pro server - Specify the following for Options > Parameter Labels
- Parameter 4:
Reverse Domain Name Notation (i.e., "com.company")
- Parameter 5:
Launch Option (i.e., [ restart | login ] )
- Parameter 6:
Jamf Pro Policy Trigger (for restart) or ID (for login)
- Parameter 4:
- Click Save
Latest version available on GitHub.
#!/bin/bash #################################################################################################### # # ABOUT # # Execute a Jamf Pro Policy Trigger (for restart) or ID (for login) after computer reboot # https://snelson.us/2023/04/trigger-policy-at-login-or-reboot-0-0-2/ # #################################################################################################### # # HISTORY # # Version 0.0.1, 20-Mar-2023, Dan K. Snelson (@dan-snelson) # Based on Recon at Reboot # # Version 0.0.2, 13-Apr-2023, Dan K. Snelson (@dan-snelson) # Miscellaneous updates # #################################################################################################### #################################################################################################### # # Global Variables # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # scriptVersion="0.0.2" export PATH=/usr/bin:/bin:/usr/sbin:/sbin reverseDomain="${4:-"org.churchofjesuschrist"}" # Parameter 4: Reverse Domain Name Notation (i.e., "org.churchofjesuschrist") launchOption="${5:-"login"}" # Parameter 5: Launch Option (i.e., [ restart | login ] ) triggerOrID="${6:-"29"}" # Parameter 6: Jamf Pro Policy Trigger (for restart) or ID (for login) scriptLog="/var/log/${reverseDomain}.log" plistFilename="${reverseDomain}.${triggerOrID}.plist" loggedInUser=$( /bin/echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil | /usr/bin/awk '/Name :/ { print $3 }' ) #################################################################################################### # # Pre-flight Checks # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ ! -f "${scriptLog}" ]]; then touch "${scriptLog}" fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Script Logging Function # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function updateScriptLog() { echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Logging Preamble # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "\n\n###\n# Trigger Policy at Login or Reboot: Create (${scriptVersion})\n###\n" updateScriptLog "PRE-FLIGHT CHECK: Initiating …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Confirm script is running as root # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ $(id -u) -ne 0 ]]; then updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting." exit 1 fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Complete # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "PRE-FLIGHT CHECK: Complete" #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Set LaunchDaemon or LaunchAgent based on launchOption # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # case ${launchOption} in "login" ) # Create User's LaunchAgents directory as required if [[ ! -d "/Users/${loggedInUser}/Library/LaunchAgents/" ]]; then updateScriptLog "Create '/Users/${loggedInUser}/Library/LaunchAgents/' …" mkdir -pv "/Users/${loggedInUser}/Library/LaunchAgents" chown -v "${loggedInUser}":staff "/Users/${loggedInUser}/Library/LaunchAgents" chmod -v 755 "/Users/${loggedInUser}/Library/LaunchAgents" fi # Create LaunchAgent to call Jamf Pro Policy updateScriptLog "Create LaunchAgent to call Jamf Pro Policy: ${triggerOrID} ..." /bin/echo "<?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>Enabled</key> <true/> <key>EnableTransactions</key> <true/> <key>Label</key> <string>${reverseDomain}.${triggerOrID}</string> <key>ProgramArguments</key> <array> <string>/usr/bin/open</string> <string>jamfselfservice://content?entity=policy&id=${triggerOrID}&action=execute</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>" > "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" # Set the permission on the file updateScriptLog "Set permissions on launchd plist ..." chown "${loggedInUser}":staff "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" chmod 644 "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" ;; "restart" | * ) # Create LaunchDaemon to call Jamf Pro Policy Trigger updateScriptLog "Create LaunchDaemon to call Jamf Pro Policy Trigger: ${triggerOrID} ..." /bin/echo "<?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>Label</key> <string>${reverseDomain}.${triggerOrID}</string> <key>ProgramArguments</key> <array> <string>/usr/local/jamf/bin/jamf</string> <string>policy</string> <string>-event</string> <string>${triggerOrID}</string> <string>-verbose</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>" > "/Library/LaunchDaemons/${plistFilename}" # Set the permissions on LaunchDaemon updateScriptLog "Setting permissions on '/Library/LaunchDaemons/${plistFilename}' ..." chown root:wheel "/Library/LaunchDaemons/${plistFilename}" chmod 644 "/Library/LaunchDaemons/${plistFilename}" updateScriptLog "Set permissions on '/Library/LaunchDaemons/${plistFilename}'" ;; esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Exit # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "So long!" exit 0
Add the Trigger Policy at Login or Reboot – Delete script to your Jamf Pro server
- Add the
Trigger Policy at Login or Reboot - Delete
script to your Jamf Pro server - Specify the following for Options > Parameter Labels
- Parameter 4:
Reverse Domain Name Notation (i.e., "com.company")
- Parameter 5:
Launch Option (i.e., [ restart | login ] )
- Parameter 6:
Jamf Pro Policy Trigger (for restart) or ID (for login)
- Parameter 4:
- Click Save
Latest version available on GitHub.
#!/bin/bash #################################################################################################### # # ABOUT # # Delete the Jamf Pro Policy Trigger (for restart) or ID (for login) after computer reboot # https://snelson.us/2023/04/trigger-policy-at-login-or-reboot-0-0-2/ # #################################################################################################### # # HISTORY # # Version 0.0.1, 20-Mar-2023, Dan K. Snelson (@dan-snelson) # Based on Recon at Reboot # # Version 0.0.2, 13-Apr-2023, Dan K. Snelson (@dan-snelson) # Miscellaneous updates # #################################################################################################### #################################################################################################### # # Global Variables # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Version and Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # scriptVersion="0.0.2" export PATH=/usr/bin:/bin:/usr/sbin:/sbin reverseDomain="${4:-"org.churchofjesuschrist"}" # Parameter 4: Reverse Domain Name Notation (i.e., "org.churchofjesuschrist") launchOption="${5:-"login"}" # Parameter 5: Launch Option (i.e., [ restart | login ] ) triggerOrID="${6:-"29"}" # Parameter 6: Jamf Pro Policy Trigger (for restart) or ID (for login) scriptLog="/var/log/${reverseDomain}.log" plistFilename="${reverseDomain}.${triggerOrID}.plist" loggedInUser=$( /bin/echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil | /usr/bin/awk '/Name :/ { print $3 }' ) loggedInUserID=$( /usr/bin/id -u "${loggedInUser}" ) #################################################################################################### # # Pre-flight Checks # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ ! -f "${scriptLog}" ]]; then touch "${scriptLog}" fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Script Logging Function # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function updateScriptLog() { echo -e "$( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Logging Preamble # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "\n\n###\n# Trigger Policy at Login or Reboot: Delete (${scriptVersion})\n###\n" updateScriptLog "PRE-FLIGHT CHECK: Initiating …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Confirm script is running as root # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ $(id -u) -ne 0 ]]; then updateScriptLog "PRE-FLIGHT CHECK: This script must be run as root; exiting." exit 1 fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Complete # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "PRE-FLIGHT CHECK: Complete" #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Run command as logged-in user (thanks, @scriptingosx!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function runAsUser() { updateScriptLog "Run \"$@\" as \"$loggedInUserID\" … " launchctl asuser "$loggedInUserID" sudo -u "$loggedInUser" "$@" } #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Delete LaunchDaemon or LaunchAgent based on launchOption # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # case ${launchOption} in "login" ) if [[ -f "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" ]]; then # Unload LaunchAgent updateScriptLog "Unload ${plistFilename} … " runAsUser launchctl bootout gui/"${loggedInUserID}" "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" 2>&1 # Remove LaunchAgent updateScriptLog "Remove ${plistFilename} … " rm -f "/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}" 2>&1 updateScriptLog "Removed ${plistFilename}" else updateScriptLog "The file '/Users/${loggedInUser}/Library/LaunchAgents/${plistFilename}' was NOT found" fi ;; "restart" | * ) if [[ -f "/Library/LaunchDaemons/${plistFilename}" ]]; then # Unload LaunchAgent updateScriptLog "Unload ${plistFilename} … " launchctl bootout system "/Library/LaunchDaemons/${plistFilename}" 2>&1 # Remove LaunchAgent updateScriptLog "Remove ${plistFilename} … " rm -f "/Library/LaunchDaemons/${plistFilename}" 2>&1 updateScriptLog "Removed ${plistFilename}" else updateScriptLog "The file '/Library/LaunchDaemons/${plistFilename}' was NOT found" fi ;; esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Exit # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # updateScriptLog "Goodbye!" exit 0
Referenced Posts
The following posts are referenced in the screencast: