Skip to main content

Was having some trouble with our instance. Turns out it was multifaceted-part my org, and part a Jamf thing (apparently). Upgrade to 11.17 seems to have rectified things on the Jamf side.

To that end I was trying to figure out how to script DDM sync, and seem to have gotten it sorted:


#!/bin/bash


JSS_URL="https://[your_jss_urll_here]"
API_USER=""
API_PASS=""


log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >&2
}


fail_exit() {
log "❌ ERROR: $1"
exit 1
}


get_token() {
log "🔐 Requesting Jamf Pro API token..."
auth_response=$(curl --silent --request POST \\
--url "$JSS_URL/api/v1/auth/token" \\
--user "$API_USER:$API_PASS")


token=$(echo "$auth_response" | jq -r '.token // empty')
if [[ -z "$token" ]]; then
echo "$auth_response" > /tmp/jamf_token_error.json
fail_exit "Unable to extract token. Response saved to /tmp/jamf_token_error.json"
fi
echo "$token"
}


get_all_ids() {
local token="$1"
local page=0
local page_size=100
local all_ids=()


while true; do
log "📦 Fetching computer ID page $((page + 1))..."
response=$(curl --silent --request GET \\
--url "$JSS_URL/api/v1/computers-inventory?page=$page&page-size=$page_size" \\
--header "Authorization: Bearer $token" \\
--header "Accept: application/json")


clean=$(echo "$response" | tr -d '\\000-\\037')


if ! echo "$clean" | jq -e .results > /dev/null 2>&1; then
log "❌ JSON parsing failed for page $((page + 1)). Saved to /tmp/ddm_bad_page_${page}.json"
echo "$response" > "/tmp/ddm_bad_page_${page}.json"
break
fi


ids=$(echo "$clean" | jq -r '.results[].id')
[[ -z "$ids" ]] && break


while IFS= read -r id; do
all_ids+=("$id")
done <<< "$ids"


((page++))
done


printf "%s\\n" "${all_ids[@]}"
}


get_management_id() {
local token="$1"
local id="$2"


response=$(curl --silent --request GET \\
--url "$JSS_URL/api/v1/computers-inventory-detail/$id" \\
--header "Authorization: Bearer $token" \\
--header "Accept: application/json")


mgmt_id=$(echo "$response" | plutil -extract general.managementId raw - 2>/dev/null)
name=$(echo "$response" | jq -r '.general.name // "Unnamed"')


if [[ -z "$mgmt_id" ]]; then
log "⚠️ No Management ID for Jamf ID $id ($name). Skipping."
return 1
fi


echo "$id,$mgmt_id,$name"
}


trigger_ddm_then_fallback() {
local token="$1"
local id="$2"
local mgmt_id="$3"
local name="$4"


log "🔁 Syncing [$name] - ID $id | MGMT ID $mgmt_id"
log "🔗 Jamf UI: $JSS_URL/computers.html?id=$id"


ddm_response=$(curl --silent --write-out "HTTPSTATUS:%{http_code}" --request POST \\
--url "$JSS_URL/api/v1/ddm/$mgmt_id/sync" \\
--header "Authorization: Bearer $token" \\
--header "Content-Type: application/json")


ddm_code=$(echo "$ddm_response" | tr -d '\\n' | sed -e 's/.*HTTPSTATUS://')


if [[ "$ddm_code" == "204" ]]; then
log "✅ DDM Sync successful for [$name]"
elif [[ "$ddm_code" == "404" ]]; then
log "⚠️ [$name] not DDM-enabled. Trying Classic..."


classic_response=$(curl --silent --write-out "HTTPSTATUS:%{http_code}" --request POST \\
--url "$JSS_URL/JSSResource/computercommands/command/InventoryUpdate/id/$id" \\
--header "Authorization: Bearer $token")


classic_code=$(echo "$classic_response" | tr -d '\\n' | sed -e 's/.*HTTPSTATUS://')


if [[ "$classic_code" == "201" || "$classic_code" == "200" ]]; then
log "✅ Classic InventoryUpdate triggered for [$name]"
else
log "❌ Classic update failed for [$name]. Status: $classic_code"
fi
else
log "❌ Unexpected DDM response for [$name]. Status: $ddm_code"
fi
}


main() {
token=$(get_token) || fail_exit "Authentication failed."
ids=$(get_all_ids "$token")
count=$(echo "$ids" | wc -l | xargs)
log "🔎 Found $count total managed Macs."


if [[ $count -eq 0 ]]; then
fail_exit "No managed devices found."
fi


echo "$ids" | while read -r id; do
[[ -z "$id" ]] && continue
entry=$(get_management_id "$token" "$id") || continue
IFS=',' read -r id mgmt_id name <<< "$entry"
trigger_ddm_then_fallback "$token" "$id" "$mgmt_id" "$name"
sleep 0.3
done


log "🎉 All sync attempts complete."
}


main

-------------
Passing on in the hopes that someone else may be helped by this. And yes I know the password needs to be obfuscated.




Hey, thanks I was getting HTTP 500 no matter what format of the curl command I tried, but yours above got me to HTTP 401. Can you tell me what API Permissions you have set? I already have the Send Declarative Management Command permission assigned. Not sure what else would be missing. Thanks


Hey, thanks I was getting HTTP 500 no matter what format of the curl command I tried, but yours above got me to HTTP 401. Can you tell me what API Permissions you have set? I already have the Send Declarative Management Command permission assigned. Not sure what else would be missing. Thanks


Hi, wewenttothemoo,

I'm using my admin account to request the bearer token as trying the "script" api account I created just wasn't working--and this despite granting it umpty-dump perms.
Hi, wewenttothemoo,

I'm using my admin account to request the bearer token as trying the "script" api account I created just wasn't working--and this despite granting it umpty-dump perms.

Gotcha, well seems like deflounder just updated his site with this: Forcing a DDM sync on a Jamf Pro-managed device via the Jamf Pro API | Der Flounder

I'm trying now with the View MDM command information in Jamf Pro API


Hi, wewenttothemoo,

I'm using my admin account to request the bearer token as trying the "script" api account I created just wasn't working--and this despite granting it umpty-dump perms.

I got it working you need these API permissions at the minimum:


API client permissions:



  • View MDM command information in Jamf Pro API

  • Send Declarative Management Command


User account permissions:


Jamf Pro Server Actions:



  • View MDM command information in Jamf Pro API

  • Send Declarative Management Command


I got it working you need these API permissions at the minimum:


API client permissions:



  • View MDM command information in Jamf Pro API

  • Send Declarative Management Command


User account permissions:


Jamf Pro Server Actions:



  • View MDM command information in Jamf Pro API

  • Send Declarative Management Command


Sweet, and thanks for the update! I need the proverbial “quick and dirty” due to some issues with my cloud tenant which seemed to have introduced with the advent of Pro 11.16, and which haven’t been fully addressed by either 11.17, or 11.17.1.

I’ll likely be making that change myself.

Reply