Menu Close

Trigger Jamf Pro Policy at Login or Reboot (0.0.2)

Run any Jamf Pro policy at the next user login or computer reboot

Trigger Policy at Login or Reboot screencast (06:55) [Music: bensound.com]

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.

  1. Add both “trigger” scripts to your Jamf Pro server
  2. Create a new (or edit an existing) policy
    • Add the “create” script to the policy’s Script payload
    • Specify the preferred Launch Option
  3. 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 OptionLoginReboot
.plist Location~/Library/LaunchAgents//Library/LaunchDaemons/
Program Argumentsopen jamfselfservice:// … "${id}"jamf policy -event "${trigger}"
Run At Loadtruetrue

Scripts

Add the Trigger Policy at Login or Reboot – Create script to your Jamf Pro server
  1. Add the Trigger Policy at Login or Reboot - Create script to your Jamf Pro server
  2. 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)
  3. 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
  1. Add the Trigger Policy at Login or Reboot - Delete script to your Jamf Pro server
  2. 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)
  3. 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:

Posted in Jamf Pro, Scripts, Tips & Tricks

Related Posts