I was recently working on performing some testing to find out why we are having problems with policies that install apps using an install package. We noticed that the packages would fail to download successfully. I created a policy that runs a packet capture while an application install policy that contains a package payload is running. The packet capture along with the Jamf log, and some other data all get zipped up and uploaded to each computer's inventory as an attachment. This enabled me to get the data I needed without having to ask the user to provide the data to me. I got the logs and other data that I needed to get help finding out what is causing the failed package downloads. This process created another problem. Over 100 Mac inventory records had multiple attachments from the multiple tests I ran. Jamf Pro does not provide an easy and automated way to delete the attachments. The API does provide a delete function. To run this function, we need to know the computer's Jamf ID and the individual attachment IDs. I tested the delete function in a script using CodeRunner on my own Mac. It worked exactly as expected. Of course, I knew my own Mac's Jamf ID and I was able to get the attachment IDs by viewing them in the inventory. I needed an automated way of automatically collecting the Jamf ID and attachment IDs. Once I have that information, I can run the delete function. The script below performs all the steps. First, it gets the serial number of the Mac. Next, it uses the serial number to find the Jamf Pro ID. Once the ID is obtained, it can be used to check if there are any attachments in the inventory for the Mac. The script compiles a list of the attachment IDs. The last step is to run the delete function. As I write this, 41 out of 130 Macs have run the policy with this script. I see in the policy log that they all had attachments, and the attachments were deleted. I wanted to share this to help others who may be in the same situation. I have myself to blame. I should have stayed on top of this and removed the attachments after running my test policy and downloading the files I needed. I hope this helps others! If anyone has recommendations on how to improve this script, I would appreciate that. I have only been using the API for about a year. As you will see noted at the top of my script a Jamf employee named Joshua Roskos provided the secret sauce that enabled me to find out if a computer's inventory has attachements. He posted an extension attribute for this. I used some of his code in my script.
#!/bin/bash
:<<ABOUT_THIS_SCRIPT
---------------------------------------------------------------------------------------------------
Finds attachments in a computer's Jamf Pro inventory and deletes them.
Inspired by https://github.com/kc9wwh/logCollection/blob/master/EA-NumAttachments.sh
Written by: Joshua Roskos | Jamf
3/12/25 - Howie Canterbury | Mac Admins Slack: HowieIsaacks
---------------------------------------------------------------------------------------------------
ABOUT_THIS_SCRIPT
# Jamf Pro API login
url="https://YourServer.jamfcloud.com"
client_id="xxxxxxxxxxxx" # Use Jamf parameter 4 instead of entering client ID here
client_secret="xxxxxxxxxxxxxxxxxx" # Use Jamf parameter 5 instead of entering client secret here
jamfAPI_auth() {
response=$(curl --silent --location --request POST "${url}/api/oauth/token" \\
--header "Content-Type: application/x-www-form-urlencoded" \\
--data-urlencode "client_id=${client_id}" \\
--data-urlencode "grant_type=client_credentials" \\
--data-urlencode "client_secret=${client_secret}")
token=$(echo "$response" | plutil -extract access_token raw -)
token_expires_in=$(echo "$response" | plutil -extract expires_in raw -)
token_expiration_epoch=$(($current_epoch + $token_expires_in - 1))
}
# Jamf Pro API authentication
jamfAPI_auth
## Validate API login (comment out when not needed)
#echo $token
#echo $token_expires_in
#echo $token_expiration_epoch
# Computer serial number
serialNumber=$( system_profiler SPHardwareDataType | grep Serial | awk '{print $NF}' )
# Jamf Pro device ID
jamfProID=$(curl -s -H "Accept: text/xml" -H "Authorization: Bearer ${token}" ${url}/JSSResource/computers/serialnumber/"$serialNumber" | xmllint --xpath '/computer/general/id/text()' -)
# Find if there are any attachments, report their IDs, and delete them
attachmentIDs=$(curl -X GET -H "Authorization: Bearer ${token}" ${url}/JSSResource/computers/id/$jamfProID | xmllint -format - | xpath -e '/computer/purchasing/attachments' | grep "<id>" )
echo "Attachments found ${attachmentIDs}"
id_array=($(echo "$attachmentIDs" | grep -oE '[0-9]+'))
for i in "${!id_array[@]}"; do
echo "Deleting attachment ${id_array[$i]}"
$(curl --request DELETE \\
--url "${url}"/api/v1/computers-inventory/$jamfProID/attachments/${id_array[$i]} \\
-H "Authorization: Bearer ${token}")
done