Created
May 12, 2025 13:19
-
-
Save karthikeyan-mac/08b95e4ae6dbabaa48b3dcac9f09305e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
################################################################################ | |
# Description: | |
# Adds or removes a computer (by Serial Number) to/from a static group | |
# in Jamf Pro using the OAuth 2.0 API with client credentials. | |
# | |
# Prerequisites: | |
# - Jamf Pro instance must support Bearer Token API authentication. | |
# - client_id and client_secret must belong to an API role with proper privileges. | |
# - API Privileges : Update Static Computer Groups, Read Static Computer Groups | |
# | |
# Karthikeyan Marappan | |
# Date: 2025-04-09 | |
# Usage: | |
# Configure the Jamf policy parameters. | |
################################################################################ | |
###################### CONFIGURABLE VARIABLES ################################## # Ensure no trailing slash | |
client_id="$4" # API Client ID | |
client_secret="$5" # API Client Secret | |
staticGroupID="$6" # ID of the static computer group in Jamf Pro | |
actionRequired="$7" # Action to perform: ADD or REMOVE | |
################################################################################ | |
# Get the SerialNumber | |
serialNumber=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}') # Serial number of the Mac to manage | |
jamfPrefFile="/Library/Preferences/com.jamfsoftware.jamf.plist" | |
if [[ -f "$jamfPrefFile" ]]; then | |
Jamf_URL=$(defaults read "$jamfPrefFile" jss_url | sed 's:/*$::') | |
else | |
echo "Error: $jamfPrefFile not found." >&2 | |
exit 1 | |
fi | |
# Function for timestamped logging | |
log() { | |
echo "$(date +"%Y-%m-%d %H:%M:%S") - $1" | |
} | |
# Validate all required inputs before proceeding | |
if [[ -z "$serialNumber" || -z "$Jamf_URL" || -z "$client_id" || -z "$client_secret" || -z "$staticGroupID" || -z "$actionRequired" ]]; then | |
log "Error: One or more required variables are empty." | |
exit 1 | |
fi | |
# Translate action to Jamf XML API tag | |
case "$actionRequired" in | |
ADD) action="computer_additions" ;; | |
REMOVE) action="computer_deletions" ;; | |
*) | |
log "Error: actionRequired must be 'ADD' or 'REMOVE'." | |
exit 1 | |
;; | |
esac | |
# Function to retrieve access token from Jamf Pro | |
getAccessToken() { | |
log "Fetching Jamf API token from ${Jamf_URL}" | |
response=$(curl --silent --fail-with-body --location --request POST "${Jamf_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}") | |
if [[ $? -ne 0 || -z "$response" ]]; then | |
log "Error: Failed to obtain access token. Check Jamf URL and credentials." | |
exit 1 | |
fi | |
access_token=$(echo "$response" | plutil -extract access_token raw -) | |
if [[ "$access_token" == "null" ]]; then | |
log "Error: Invalid API client credentials. Check API Role permissions." | |
exit 1 | |
fi | |
log "Successfully obtained API token." | |
} | |
# Function to invalidate API token after usage | |
invalidateToken() { | |
log "Invalidating API token..." | |
responseCode=$(curl -w "%{http_code}" -H "Authorization: Bearer ${access_token}" \ | |
"${Jamf_URL}/api/v1/auth/invalidate-token" -X POST -s -o /dev/null) | |
case "$responseCode" in | |
204) log "Token successfully invalidated." ;; | |
401) log "Token already invalid." ;; | |
*) log "Unexpected response code during token invalidation: $responseCode" ;; | |
esac | |
} | |
# Function to check if a serial number already exists in the static group | |
serialNumberExistsInGroup() { | |
log "Checking if serial number exists in static group..." | |
response=$(curl -s --location --request GET "$Jamf_URL/JSSResource/computergroups/id/$staticGroupID" \ | |
--header "Accept: application/xml" \ | |
--header "Authorization: Bearer $access_token" \ | |
--write-out '\n%{http_code}') | |
http_code=$(tail -n1 <<< "$response") | |
xml_body=$(printf "%s\n" "$response" | sed '$d') | |
if [[ "$http_code" == 200 ]]; then | |
groupName=$(echo $xml_body | xmllint --xpath '/computer_group/name/text()' -) | |
log "Static Group Found. Group Name: \"$groupName\"" | |
elif [[ "$http_code" == 401 ]]; then | |
log "Unauthorized to read static group. Check API role permissions." | |
exit 1 | |
else | |
log "Failed to retrieve static group info. HTTP code: $http_code. Please check if the Static Group ID: $staticGroupID exists in JAMF" | |
exit 1 | |
fi | |
# Check if the serial number is listed in the group | |
if echo "$xml_body" | grep -q "<serial_number>${serialNumber}</serial_number>"; then | |
return 0 # Serial number exists in group | |
else | |
return 1 # Serial number does not exist | |
fi | |
} | |
# Main logic for modifying static group membership | |
changeStaticComputerGroup() { | |
serialNumberExistsInGroup | |
exists=$? | |
if [[ $exists -eq 0 && "$actionRequired" == "ADD" ]]; then | |
log "Serial number $serialNumber already exists in group. Skipping add." | |
return | |
elif [[ $exists -eq 1 && "$actionRequired" == "REMOVE" ]]; then | |
log "Serial number $serialNumber not found in group. Skipping removal." | |
return | |
fi | |
# Construct XML payload for PUT request | |
local xmlData="<computer_group><${action}><computer><serial_number>${serialNumber}</serial_number></computer></${action}></computer_group>" | |
response=$(curl -s --location --request PUT "$Jamf_URL/JSSResource/computergroups/id/$staticGroupID" \ | |
--header "Accept: application/xml" \ | |
--header "Content-Type: application/xml" \ | |
--header "Authorization: Bearer $access_token" \ | |
--data-raw "$xmlData" \ | |
--write-out '\n%{http_code}') | |
http_code=$(tail -n1 <<< "$response") | |
if [[ "$http_code" == 201 ]]; then | |
if [[ $actionRequired == "ADD" ]]; then | |
actionPerformed="added" | |
else | |
actionPerformed="removed" | |
fi | |
log "Successfully $actionPerformed serial: $serialNumber in Static Group: \"$groupName\"" | |
elif [[ "$http_code" == 409 ]]; then | |
log "Serial number does not exist in Jamf or the device is unmanaged: $serialNumber" | |
elif [[ "$http_code" == 401 ]]; then | |
log "Unauthorized to update static group. Check API role permissions." | |
exit 1 | |
else | |
log "API request failed with HTTP code $http_code for serial: $serialNumber" | |
fi | |
} | |
# Main entry point | |
main() { | |
getAccessToken | |
changeStaticComputerGroup | |
invalidateToken | |
} | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment