A function for your
~/.zshrcto self-heal thejamfbinary via the Jamf Pro API

Background
We’ve had one too many Macs with sideways configurations lately, which we’ve attempted to resolve by various re-enrollment strategies.
After reviewing Emily’s Jamf binary self-heal with the Jamf API post, we wanted to easily issue the redeploy command via macOS Terminal.
Assumptions
This method assumes the problematic computer is still receiving and executing MDM commands successfully; please first confirm this by reviewing the computer’s Management History in Jamf Pro.
Caution
To help protect your Jamf Pro server credentials, only execute this script from your computer (i.e., do not configure this script on the affected Mac).
Jamf Pro Configuration
You’ll need a Jamf Pro User Account with (at least) the following privileges:
- Jamf Pro Server Objects
- Computers > Read
- Jamf Pro Server Settings
- Check-in > Read
- Jamf Pro Server Actions
- Flush MDM Commands
- Flush Policy Logs
- Send Computer Remote Command to Install Package
~/.zshrc Configuration
Backup your current ~/.zshrc and add the following jbsh function:
jbsh () { # [j]amf [b]inary [s]elf-[h]eal
if [ -z ${1} ]; then
printf "\n###\n# [j]amf [b]inary [s]elf-[h]eal\n###\n\n"
printf "Usage:\n1. Type \"jbsh\", followed by a [Space]\n2. Paste the computer's Serial Number or Jamf Pro Computer ID\n3. Press [Return]\n\n"
printf "(Enter \"jbsh help\" for initial setup.)\n\n"
return
fi
if [ ${1} = "help" ]; then
apiURL=$( /usr/bin/defaults read "/Library/Preferences/com.jamfsoftware.jamf.plist" jss_url )
/usr/bin/clear
printf "\n###\n# [j]amf [b]inary [s]elf-[h]eal Help\n###\n\n"
printf "Use the following commands to create entries in Keychain Access:\n\n"
printf " security add-generic-password -s \"jbshApiUrl\" -a ${USER} -w \"${apiURL}\"\n"
printf " security add-generic-password -s \"jbshApiUser\" -a ${USER} -w \"API Username Goes Here\"\n"
printf " security add-generic-password -s \"jbshApiPassword\" -a ${USER} -w \"API Password Goes Here\"\n\n"
printf "Once the Keychain entries have been created …\n\n"
printf " 1. Enter \"jbsh\", followed by a [Space]\n 2. Paste the computer's Serial Number or Jamf Pro Computer ID\n 3. Press [Return]\n\n"
else
apiURL=$( security find-generic-password -s "jbshApiUrl" -a "${USER}" -w 2>/dev/null )
apiUser=$( security find-generic-password -s "jbshApiUser" -a "${USER}" -w 2>/dev/null )
apiPassword=$( security find-generic-password -s "jbshApiPassword" -a "${USER}" -w 2>/dev/null )
if [ -z ${apiURL} ] || [ -z ${apiUser} ] || [ -z ${apiUser} ]; then
printf "\n###\n# ERROR: Unable to read API credentials\n###\n\nPlease run \"jbsh help\" to configure API credentials.\n\n"
return
fi
# Obtain Jamf Pro Bearer Token via Basic Authentication
apiBearerToken=$( /usr/bin/curl -X POST --silent -u "${apiUser}:${apiPassword}" "${apiURL}/api/v1/auth/token" | /usr/bin/plutil -extract token raw - )
# Determine if user provided a Jamf Pro Computer ID (i.e., a number) or a Serial Number (i.e., NOT a number)
numberValidation='^[0-9]+$'
if ! [[ "${1}" =~ "${numberValidation}" ]] ; then
# Determine the computer's Jamf Pro Computer ID via the computer's Serial Number, "${1}"
jssID=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -s "${apiURL}"/JSSResource/computers/serialnumber/"${1}"/subset/general | xpath -e "/computer/general/id/text()" )
else
jssID="${1}"
fi
generalComputerInfo=$( /usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -H "Accept: text/xml" -sfk "${apiURL}"/JSSResource/computers/id/"${jssID}/subset/General" -X GET )
# echo ${generalComputerInfo}
computerName=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/name/text()" )
computerSerialNumber=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/serial_number/text()" )
computerIpAddress=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/ip_address/text()" )
computerIpAddressLastReported=$( echo ${generalComputerInfo} | xpath -q -e "/computer/general/last_reported_ip/text()" )
printf "\nSelf-healing the Jamf binary for:\n"
printf "• Name: $computerName\n"
printf "• Serial Number: $computerSerialNumber\n"
printf "• IP Address: $computerIpAddress\n"
printf "• IP Address (LR): $computerIpAddressLastReported\n\n"
printf "Via MDM:\n"
printf "• Server: ${apiURL}\n"
# printf "• Username: ${apiUser}\n"
printf "• Computer ID: ${jssID}\n\n"
# Brute-force clear all failed MDM Commands
printf "Brute-force clear all failed MDM Commands …\n"
/usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" --progress-bar --fail-with-body "${apiURL}/JSSResource/commandflush/computers/id/${jssID}/status/Failed" -X DELETE
# Redeploy Jamf binary
printf "\n\nRedeploy Jamf binary …\n"
/usr/bin/curl -H "Authorization: Bearer ${apiBearerToken}" -H "accept: application/json" --progress-bar --fail-with-body "${apiURL}"/api/v1/jamf-management-framework/redeploy/"${jssID}" -X POST
# Invalidate the Bearer Token
apiBearerToken=$( /usr/bin/curl "${apiURL}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${apiBearerToken}" -X POST )
apiBearerToken=""
# Set the default browser
browser=""
if [ "${browser}" == "" ]; then
printf "\n\n(Preferred browser not specified; using Safari.)\n\n"
browser="Safari"
fi
case ${browser} in
Chrome ) browserPath="/Applications/Google Chrome.app/" ;;
Firefox ) browserPath="/Applications/Firefox.app/" ;;
Safari ) browserPath="/Applications/Safari.app/" ;;
* ) browserPath="/Applications/Safari.app/" ;;
esac
# Open the "Management" tab for the Jamf Pro Computer Record
printf "Viewing Computer Record in ${browser} …\n\n"
url="${apiURL}/computers.html?id=${jssID}&o=r&v=management"
/usr/bin/open -a "${browserPath}" "${url}"
printf "\n\n"
fi
}
Function Configuration
After you’ve added the above jbsh function and saved your ~/.zshrc, quit and re-launch Terminal.
In Terminal, enter jbsh and press Return; you should observe the following:
❯ jbsh ### # [j]amf [b]inary [s]elf-[h]eal ### Usage: 1. Type "jbsh", followed by a [Space] 2. Paste the computer's Serial Number or Jamf Pro Computer ID 3. Press [Return] (Enter "jbsh help" for initial setup.)
Again in Terminal, enter jbsh help, press Return and you should observe a personalized version of the following:
❯ jbsh help
###
# [j]amf [b]inary [s]elf-[h]eal Help
###
Use the following commands to create entries in Keychain Access:
security add-generic-password -s "jbshApiUrl" -a myShortName -w "https://companyname.jamfcloud.com/"
security add-generic-password -s "jbshApiUser" -a myShortName -w "API Username Goes Here"
security add-generic-password -s "jbshApiPassword" -a myShortName -w "API Password Goes Here"
Once the Keychain entries have been created …
1. Enter "jbsh", followed by a [Space]
2. Paste the computer's Serial Number or Jamf Pro Computer ID
3. Press [Return]
Follow the on-screen instructions to use the security command to add the three entries to your login Keychain, using your Jamf Pro server address and the Jamf Pro User Account credentials you created earlier:
- Jamf Pro URL
- API Username
- API Password
Function Usage
Enter jbsh followed by a Space, paste either the computer’s Serial Number or its Jamf Pro Computer ID and press Return.
For at least your first run, you’ll most likely see a dialog box(es) similar to the following; enter your Mac’s login keychain credentials to proceed.

Presuming I didn’t make any coding errors, you should observe something similar to the following:
❯ jbsh D13YC7QRTE8Y
Self-healing the Jamf binary for:
• Name: Test MacBook Pro
• Serial Number: D13YC7QRTE8Y
• IP Address: 17.32.61.186
• IP Address (LR): 172.17.0.29
Via MDM:
• Server: https://companyname.jamfcloud.com
• Computer ID: 7279
Brute-force clear all failed MDM Commands …
<?xml version="1.0" encoding="UTF-8"?><commandflush><status>+failed</status><computers>[7279]</computers></commandflush>
Redeploy Jamf binary …
{
"deviceId" : "7279",
"commandUuid" : "d11deddf-9ad6-41e2-8e2d-3141bfb1db61"
}
(Preferred browser not specified; using Safari.)
Viewing Computer Record in Safari …
Safari should open the Computer Record to the Management tab, where you can view any pending MDM commands:

In my testing, occasionally the command would fail, so the function also leverages a brute-force commandflush for all failed commands.

Also, on occasion, the command was successfully issued faster than I could observe its progress. If this happens, switch over to the History tab and view Management History:


Monitor Progress
As Emily points out, you can use the following command — client-side — to check the ”healing” progress:
log show --info --debug --predicate 'subsystem == "com.apple.ManagedClient" AND category == "ManagedApps"' --last 1h
Troubleshooting
Jamf Pro User Account Privileges
You may wish to manually validate the Jamf Pro User Account’s privileges by using the Swagger UI to confirm the Redeploy Jamf Management Framework command works as expected:
# Authorize Jamf Pro User Account
# The Jamf Pro API uses a token-based authentication. Enter username and password to generate a token to be used with the 'Try it out!' feature.
apiURL=$( /usr/bin/defaults read "/Library/Preferences/com.jamfsoftware.jamf.plist" jss_url ) ; open ${apiURL}/api/doc/
# Redeploy Jamf Management Framework
open ${apiURL}/api/doc/#/jamf-management-framework
Updates
January 2023
Be sure to checkout Richard Mallion’s Jamf Framework Redeploy.