Running all Jamf Pro policies in a specified category via the API
As part of a project I’m working on, I need to run several policies from a Jamf Pro server using a script which is using the Jamf Pro agent to run policies. However, I also want to maintain maximum flexibility and retain the ability to add, remove or change policies as required without needing to change the script.
My colleague Marc provided a solution for this by letting me know that it was possible to use the Jamf Pro API to pull down a list of policies associated with a specific category and then running those policies in the order provided by the API. For more details, see below the jump.
I was able to use Marc’s technique in the following way:
1. Run the following command to get the policy IDs:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/usr/bin/curl -ksf -u api_username_goes_here:api_password_goes_here -H "Accept: application/xml" https://server.name.here/JSSResource/policies/category/Policy_Category_Goes_Here | xpath "policies/policy/id" | sed 's/\<id>//g' | tr '</id>' '\n' | sed '/^s*$/d' |
2. Add all policy IDs into a bash array.
3. Run each policy in the order they were added to the bash array, which will be the same order provided by the API.
To set the order for the policies, I need to use numbering as part of the policy name. So when naming the policies. the first policy name starts with 010, the second policy’s name begins with 020 and so on.
Once the policies are in place, a script like the one shown below can be used to access the list of policy IDs via the API and run the policies in their specified order.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Set username of the API user. | |
# Script uses Parameter 4 to get the appropriate username from Jamf Pro | |
apiUsername="$4" | |
# Set password of the API user. | |
# Script uses Parameter 5 to get the appropriate password from Jamf Pro. | |
apiPassword="$5" | |
# Set the policy category which contains the Jamf Pro | |
# policies that need to be run. Script uses Parameter 6 | |
# to get the appropriate category from Jamf Pro. | |
PolicyCategory="$6" | |
CheckBinary (){ | |
# Identify location of jamf binary. | |
jamf_binary=`/usr/bin/which jamf` | |
if [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ ! -e "/usr/local/bin/jamf" ]]; then | |
jamf_binary="/usr/sbin/jamf" | |
elif [[ "$jamf_binary" == "" ]] && [[ ! -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then | |
jamf_binary="/usr/local/bin/jamf" | |
elif [[ "$jamf_binary" == "" ]] && [[ -e "/usr/sbin/jamf" ]] && [[ -e "/usr/local/bin/jamf" ]]; then | |
jamf_binary="/usr/local/bin/jamf" | |
fi | |
} | |
# Run the CheckBinary function to identify the location | |
# of the jamf binary | |
CheckBinary | |
# If the jamf binary isn't found, stop the script | |
# and exit with an error status. | |
if [[ "$jamf_binary" == "" ]]; then | |
/bin/echo "`date +%Y-%m-%d\ %H:%M:%S` Jamf Pro agent not found. Exiting." | |
exit 1 | |
fi | |
# Identify the URL of the Jamf Pro server using the | |
# 'jamf checkJSSConnection' command | |
JamfProURLCheck=$("$jamf_binary" checkJSSConnection | awk '/Checking/ {print $4}') | |
JamfProURL=$(echo ${JamfProURLCheck///…}) | |
JamfProPolicyURL="${JamfProURL}/JSSResource/policies/category/${PolicyCategory}" | |
# Save current IFS state | |
OLDIFS=$IFS | |
# Change IFS to | |
# create newline | |
IFS=$'\n' | |
casper_policy_ids=`/usr/bin/curl -ksf -u "${apiUsername}:${apiPassword}" -H "Accept: application/xml" "${JamfProPolicyURL}" | xpath "policies/policy/id" | sed 's/\<id>//g' | tr '</id>' '\n' | sed '/^s*$/d'` | |
# read all policy IDs into an array | |
policies=($(/bin/echo "$casper_policy_ids")) | |
# restore IFS to previous state | |
IFS=$OLDIFS | |
# Get length of the array | |
tLen=${#policies[@]} | |
# Run all matching Jamf Pro policies in the order received from the Jamf Pro server | |
for (( i=0; i<${tLen}; i++ )); | |
do | |
/bin/echo "`date +%Y-%m-%d\ %H:%M:%S` Installing policy "${policies[$i]}" on this Mac." | |
"$jamf_binary" policy -id "${policies[$i]}" | |
done |
The script is also available on Github at the following address:
If you want to run this script from your Jamf Pro server, it should be set up as follows.
To use this capability, you will need to set up a category if needed and assign your policies to it. For this example, I’ve created a category named InitialSetup.
Once created, assign your policies to this category. If needed, rename the policies to include numerical values. The number values will designate the order for the policies to run.
Once the category and policies have been set up as desired, you can have a separate policy or other means run the script. The script will need to have the following information specified:
- API username
- API password
- Category name
One notable thing about running policies using this technique is the affected policies do not need to be associated with any of the usual triggers.
Instead, the policy is being called directly by its ID number, which in this case will act as a trigger to run the policy:
jamf policy -id policy_id_number_goes_here
Does that work with spaces in the category names? I think it should judging by your use of IFS.
Why not just use a custom trigger? Am I missing something?