Add and Remove from Static Groups with the Jamf API

I have 2 scripts I’ve found useful with scoping policies and profiles to a group that isn’t easily configured by other information in a device’s record. A MacBook Pro that is shared for a lab is difficult encrypt, but the same model in the same department is assigned to a specific user and that we do want to encrypt. We can scope a policy to a static group and use a Self Service policy to add a machine to that group, then trigger the policy by a custom event to kick off.

APIaddtoStaticGroup.sh grabs the serial number of the device and adds it to a static group by that groups ID number. You can easily locate the ID number of a group by checking the URL in the JSS. In the example below, the number is in the ?id= portion, represented by XX.

https://yourjssurl.com:8443/staticComputerGroups.html?id=XX&o=r

Writing the script with these variables allows for reuse, and if an update to the parameters is made it will be easy to update one script instead of every policy that may use it.

apiData="<computer_group><computer_additions><computer><serial_number>${serialNumber}</serial_number></computer></computer_additions></computer_group>"
curl \
	-s \
	-f \
	-u ${apiUser}:${apiPass} \
	-X PUT \
	-H "Content-Type: text/xml" \
	-d "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>${apiData}" ${jssHost}/JSSResource/computergroups/id/${groupID}

A few usage examples for this would be to add a machine to an exception group to temporarily remove a profile for testing at the help desk, or adding to a group that has an encryption policy scoped to it.

APIremovefromStaticGroup.sh is similarly used to remove a machine from a static group. One of the ways I’ve used this is to remove a machine from groups when repurposing a machine with an erase install policy. It could similarly be used in conjunction with the add to static group script for help desk, with a self service policy to add and remove a profile or policy for testing purposes.

apiData="<computer_group><computer_deletions><computer><serial_number>${serialNumber}</serial_number></computer></computer_deletions></computer_group>"
curl \
	-s \
	-f \
	-u ${apiUser}:${apiPass} \
	-X PUT \
	-H "Content-Type: text/xml" \
	-d "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>${apiData}" ${jssHost}/JSSResource/computergroups/id/${groupID}

Jamf API Scripts on Github

I’ve been maintaining a Github with useful Jamf Pro scripts I’ve found helpful, and some I featured in a presentation at the 2019 Penn State MacAdmins Conference.

I specifically featured a few scripts that I use in our DEP provisioning workflow. This workflow relies heavily on using the Inventory Preload feature in Jamf Pro. It allows me to assign a machine to a user, assign it an asset tag, and put it in a department and building. The first time a machine checks in, Jamf will assign these items to the computer’s record in Jamf. These are all ways I can sort computers in Jamf Pro, but I can communicate with the Jamf API to pull some of these items and use them while preparing a new system.

For security purposes I have the username and password sent as a variable to the script. I also made the account with limited permissions to only make the call to pull computer data to mitigate concerns.

APIaddAdmin.sh is a simple script that grabs the user assigned in Jamf Pro and adds that user as an administrator. It first gets the serial number, and uses that to perform a lookup to find the assigned user via API call. It then uses a dscl command to set that user as an admin. This script would primarily be useful if you have machines prepped in advance by IT instead of initial configuration by a user.

username=$(/usr/bin/curl -H "Accept: text/xml" -sfku "${apiUser}:${apiPass}" "${jssHost}/JSSResource/computers/serialnumber/${serialNumber}/subset/location" | xmllint --format - 2>/dev/null | awk -F'>|<' '/<username>/{print $3}')
dscl . -append /Groups/admin GroupMembership $username

APIfirmware.sh could easily be customized, but this allows for a unique firmware password to be set per machine, utilizing the Asset Tag field in this case. As the firmwarepasswd command is interactive it uses the expect shell to respond. Again we get the serial number and use that to pull the asset tag from the computer’s inventory record. We use the expect shell to interact with a command line and send a unique password with a password scheme, assuming one isn’t already set. There are potential security concerns with setting a firmware password with a script. It will run in plain text and be cached to the Jamf Waiting Room, but in the case of IT preparing machines it is unlikely that any information would still be cached when the user gets the system.

doesexist=`firmwarepasswd -check`
barcode=$(/usr/bin/curl -H "Accept: text/xml" -sfku "${apiUser}:${apiPass}" "${jssHost}/JSSResource/computers/serialnumber/${serialNumber}/subset/general" | xmllint --format - 2>/dev/null | awk -F'>|<' '/<asset_tag>/{print $3}')
firmware=passwordscheme${barcode}

if [ "$doesexist" = "Password Enabled: No" ]; then
	/usr/bin/expect <<- DONE
		spawn firmwarepasswd -setpasswd
		expect "Enter new password:"
		send "$firmware\r";
		expect "Re-enter new password:"
		send "$firmware\r";
		expect EOF
	DONE
else
	echo "Firmware Password Already Exists"
fi

APIrename.sh similarly pulls the Asset Tag from the computer’s record in Jamf, again by using the serial number to perform a lookup. This sets each of the names to the same name. In this case we use the asset tag proceeded by the type of device , Mac-##### in the script. This can be coupled with a Jamf policy to keep computer name if you want to ensure you can find a machine on the network.

barcode=$(/usr/bin/curl -H "Accept: text/xml" -sfku "${apiUser}:${apiPass}" "${jssHost}/JSSResource/computers/serialnumber/${serialNumber}/subset/general" | xmllint --format - 2>/dev/null | awk -F'>|<' '/<asset_tag>/{print $3}')
hostname=Mac-$barcode
scutil --set ComputerName $hostname
scutil --set HostName $hostname
scutil --set LocalHostName $hostname