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.