Posted on 03-16-2022 12:53 AM
I watched the presentation given by @chadlawson and uploaded to the Rocketman Tech channel on YouTube (https://youtu.be/6xVmJqpbEHI) over the weekend. I decided to dive in and change a script that puts a machine into a static group (credit to @sdagley ) but whilst I can see that I'm getting a token, the computer is not going into the Static Group. The reason for changing from Basic Auth to Bearer Token Auth is because Basic Auth is deprecated so I'm trying to get this figured out before there's a panic.
Just wondering if anyone can see where I'm going wrong or suggest a better way to do this? The first script below is my working script using Basic Auth and the second longer one is the one using Bearer Token Auth. The second script is mostly using code from Rich Trouton's blog on Bearer Tokens and I do get the Bearer Token but whilst I seem to have no errors, the machine is not added to the static group.
#!/bin/sh
# AddComputerToStaticGroup.sh
# Adds the computer to a static group
# https://www.jamf.com/jamf-nation/discussions/36323/script-to-add-to-static-group
#API login info
apiuser="apiusernamehere"
apipass='apipasswordhere'
jamfProURL="https://yourserver.jamfcloud.com"
#ComputerName=$(/usr/sbin/scutil --get ComputerName)
ComputerName="ComputerNameThatExistsInServer"
# My test static group is called TestGroup and has ID of 3
GroupID="3"
GroupName="TestGroup"
apiURL="JSSResource/computergroups/id/${GroupID}"
#XML header stuff
xmlHeader="<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
apiData="<computer_group><id>${GroupID}</id><name>${GroupName}</name><computer_additions><computer><name>$ComputerName</name></computer></computer_additions></computer_group>"
curl -sSkiu ${apiuser}:${apipass} "${jamfProURL}/${apiURL}" \
-H "Content-Type: text/xml" \
-d "${xmlHeader}${apiData}" \
-X PUT > /dev/null
#!/bin/sh
# Adapted from https://derflounder.wordpress.com/2022/01/05/updated-script-for-obtaining-checking-and-renewing-bearer-tokens-for-the-classic-and-jamf-pro-apis/
# This script uses the Jamf Pro API to get an authentication token
# Explicitly set initial value for the api_token variable to null:
api_token=""
# Explicitly set initial value for the token_expiration variable to null:
token_expiration=""
# Set the Jamf Pro URL here if you want it hardcoded.
jamfpro_url="https://yourserver.jamfcloud.com"
jamfpro_user="apiusernamehere"
jamfpro_password='apipasswordhere'
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/auth" --request GET --header "Authorization: Bearer ${api_token}")
}
CheckAndRenewAPIToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, it is used to connect to the keep-alive endpoint. This will
# trigger the issuing of a new bearer token and the invalidation of the previous one.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" --silent --request POST --header "Authorization: Bearer ${api_token}" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/keep-alive" --silent --request POST --header "Authorization: Bearer ${api_token}" | plutil -extract token raw -)
fi
else
# If the current bearer token is not valid, this will trigger the issuing of a new bearer token
# using Basic Authentication.
GetJamfProAPIToken
fi
}
InvalidateToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, an API call is sent to invalidate the token.
authToken=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${api_token}" -X POST)
# Explicitly set value for the api_token variable to null.
api_token=""
fi
}
GetJamfProAPIToken
APITokenValidCheck
echo "$api_authentication_check"
echo "$api_token"
CheckAndRenewAPIToken
APITokenValidCheck
echo "$api_authentication_check"
echo "$api_token"
#ComputerName=$(/usr/sbin/scutil --get ComputerName)
ComputerName="ComputerNameThatExistsInServer"
# My test static group is called TestGroup and has ID of 3
GroupID="3"
echo "GroupID: $GroupID"
GroupName="TestGroup"
echo "GroupName: $GroupName"
apiURL="JSSResource/computergroups/id/${GroupID}"
echo "apiURL: $apiURL"
#XML header stuff
xmlHeader="<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "xmlHeader: $xmlHeader"
apiData="<computer_group><id>${GroupID}</id><name>${GroupName}</name><computer_additions><computer><name>$ComputerName</name></computer></computer_additions></computer_group>"
echo "apiData: $apiData"
curl -sSkiu "Authorization: Bearer ${api_token}" "${jamfpro_url}/${apiURL}" \
-H "Content-Type: text/xml" \
-d "${xmlHeader}${apiData}" \
-X PUT > /dev/null
InvalidateToken
APITokenValidCheck
echo "$api_authentication_check"
echo "$api_token"
Solved! Go to Solution.
03-16-2022 10:31 AM - edited 03-16-2022 10:46 AM
@dlondon Use the following for the curl command to do the addition:
curl -s \
--header "Authorization: Bearer ${api_token}" --header "Content-Type: text/xml" \
--url "${jamfpro_url}/${apiURL}" \
--data "${apiData}" \
--request PUT
P.S. Thanks for taking the lead on revising the sample to use Bearer Token Auth.
03-16-2022 10:31 AM - edited 03-16-2022 10:46 AM
@dlondon Use the following for the curl command to do the addition:
curl -s \
--header "Authorization: Bearer ${api_token}" --header "Content-Type: text/xml" \
--url "${jamfpro_url}/${apiURL}" \
--data "${apiData}" \
--request PUT
P.S. Thanks for taking the lead on revising the sample to use Bearer Token Auth.
Posted on 03-16-2022 05:52 PM
Thanks @sdagley - made my day and I'm only 5 minutes into it :)
I knew you and some others would get to it but the Rocketman presentation drew me in.
03-17-2022 12:00 AM - edited 03-17-2022 04:15 PM
Ok - I've cut out the code that wasn't used so the final and now working example looks like this:
#!/bin/sh
# This script uses the Jamf Pro API to get a Bearer Authentication Token and then adds a computer to a Static Group.
# In this case it could be the computer the script is run on if you uncomment the particular line. I actually set the computer name for testing to a specific name
# Adapted from https://derflounder.wordpress.com/2022/01/05/updated-script-for-obtaining-checking-and-renewing-bearer-tokens-for-the-classic-and-jamf-pro-apis/ - not all functions in Rich's example are used as this is a very short access to the API
# Also help from Steve Dagley in https://community.jamf.com/t5/jamf-pro/bearer-token-api-and-adding-computer-to-static-group/td-p/261269
# and https://community.jamf.com/t5/jamf-pro/script-to-add-computer-to-static-group/m-p/198738
# 2022-03-17 David London
# Find/Set the computer name. I'm manually setting it here for testing.
#ComputerName=$(/usr/sbin/scutil --get ComputerName)
ComputerName="is-m-00112"
# My test static group is called TestGroup and has ID of 3. Set these to whatever you need
GroupID="3"
GroupName="TestGroup"
# Explicitly set initial value for the api_token variable to null:
api_token=""
# Explicitly set initial value for the token_expiration variable to null:
token_expiration=""
# Set the Jamf Pro URL here if you want it hardcoded.
jamfpro_url="https://yourserver.jamfcloud.com"
# Set the username here if you want it hardcoded.
jamfpro_user="apiusernamehere"
# Set the password here if you want it hardcoded.
jamfpro_password='apipasswordhere'
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/auth" --request GET --header "Authorization: Bearer ${api_token}")
}
InvalidateToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, an API call is sent to invalidate the token.
authToken=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${api_token}" -X POST)
# Explicitly set value for the api_token variable to null.
api_token=""
fi
}
GetJamfProAPIToken
apiURL="JSSResource/computergroups/id/${GroupID}"
apiData="<computer_group><id>${GroupID}</id><name>${GroupName}</name><computer_additions><computer><name>$ComputerName</name></computer></computer_additions></computer_group>"
curl -s \
--header "Authorization: Bearer ${api_token}" --header "Content-Type: text/xml" \
--url "${jamfpro_url}/${apiURL}" \
--data "${apiData}" \
--request PUT > /dev/null
InvalidateToken
exit 0
05-29-2022 08:28 PM - edited 05-29-2022 08:30 PM
I've revisited this script and cleaned it up a little. There was no need for Group Name - just Group ID. I also needed a way to remove a computer from a Static Group so added that in. Finally I made it generic so it takes input parameters from Jamf
#!/bin/bash
# AddRemoveComputer-StaticGroup-UsingBearerAuthenticationToken.bash
# This script uses the Jamf Pro API to get an Bearer Authentication Token and then adds or removes a computer for an identified static group.
# In this case it's the computer the script is run on
# Adapted from https://derflounder.wordpress.com/2022/01/05/updated-script-for-obtaining-checking-and-renewing-bearer-tokens-for-the-classic-and-jamf-pro-apis/
# - not all functions in Rich's example are used as this is a very short access to the API
# Also help from Steve Dagley in https://community.jamf.com/t5/jamf-pro/bearer-token-api-and-adding-computer-to-static-group/td-p/261269
# and https://community.jamf.com/t5/jamf-pro/script-to-add-computer-to-static-group/m-p/198738
# 2022-03-17 David London
# - some portions of the input variable stage are commented. I've left the explicit lines in but commented them out
## Grab the serial number of the device
serialNumber="$(ioreg -rd1 -c IOPlatformExpertDevice | awk -F'"' '/IOPlatformSerialNumber/{print $4}')"
# Explicitly set initial value for the api_token variable to null:
api_token=""
# Explicitly set initial value for the token_expiration variable to null:
token_expiration=""
## Check if the variables have been provided, set them if not.
jamfpro_user="$4"
# Hard code user if desired
# jamfpro_user="your_api_user"
jamfpro_password="$5"
# Hard code password if desired
# jamfpro_password='jamfpro_password_here'
jamfpro_url="$6"
# Hard code JamfPro URL if desired
# jamfpro_url="https://your_jamf_pro_server_url_including_port_if_used_in_url"
# Remove the trailing slash from the Jamf Pro URL if needed.
jamfpro_url=${jamfpro_url%%/}
GroupID="$7"
# Hard code Group ID if desired
# GroupID="Group_ID_Number"
AddRemove="$8"
# Hard code AddRemove here if you want - possible values are "add" or "remove"
# AddRemove="add"
# AddRemove="remove"
# Make sure AddRemove is lower case
AddRemove=$(echo $AddRemove | tr '[:upper:]' '[:lower:]')
GetJamfProAPIToken() {
# This function uses Basic Authentication to get a new bearer token for API authentication.
# Use user account's username and password credentials with Basic Authorization to request a bearer token.
if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | python -c 'import sys, json; print json.load(sys.stdin)["token"]')
else
api_token=$(/usr/bin/curl -X POST --silent -u "${jamfpro_user}:${jamfpro_password}" "${jamfpro_url}/api/v1/auth/token" | plutil -extract token raw -)
fi
}
APITokenValidCheck() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
api_authentication_check=$(/usr/bin/curl --write-out %{http_code} --silent --output /dev/null "${jamfpro_url}/api/v1/auth" --request GET --header "Authorization: Bearer ${api_token}")
}
InvalidateToken() {
# Verify that API authentication is using a valid token by running an API command
# which displays the authorization details associated with the current API user.
# The API call will only return the HTTP status code.
APITokenValidCheck
# If the api_authentication_check has a value of 200, that means that the current
# bearer token is valid and can be used to authenticate an API call.
if [[ ${api_authentication_check} == 200 ]]; then
# If the current bearer token is valid, an API call is sent to invalidate the token.
authToken=$(/usr/bin/curl "${jamfpro_url}/api/v1/auth/invalidate-token" --silent --header "Authorization: Bearer ${api_token}" -X POST)
# Explicitly set value for the api_token variable to null.
api_token=""
fi
}
GetJamfProAPIToken
apiURL="JSSResource/computergroups/id/${GroupID}"
if [ "$AddRemove" == "add" ]; then
apiData="<computer_group><computer_additions><computer><serial_number>$serialNumber</serial_number></computer></computer_additions></computer_group>"
else
apiData="<computer_group><computer_deletions><computer><serial_number>$serialNumber</serial_number></computer></computer_deletions></computer_group>"
fi
curl -s \
--header "Authorization: Bearer ${api_token}" --header "Content-Type: text/xml" \
--url "${jamfpro_url}/${apiURL}" \
--data "${apiData}" \
--request PUT > /dev/null
InvalidateToken
exit 0
Posted on 08-16-2023 11:33 AM
Thanks for this - after struggling this morning trying to put together the proper request I came across your post. I pulled several parts of this for a script that, among other things, removes the computer from a group, and it worked great!
Posted on 10-03-2023 07:25 AM
Thank you kindly for the script, great example for API
Posted on 08-26-2023 03:59 AM
How would I go about removing multiple computers via csv or by hardcoding multiple computers or serial numbers?
Posted on 01-29-2024 11:58 AM
Posted on 01-29-2024 07:26 PM
Hi @cbruce and @tegus232 yes it's a script. I put it here so others (like you) can use it as a starting point for your own scripts.
I can certainly imagine how a script as described by you might work but I don't have a use for that and am flat out doing my normal work. The above script had a direct use for me so I can justify the time spent working on it.
There are some good free starter modules in Jamf Learning for shell scripting
There's some good documentation in https://developer.jamf.com/jamf-pro/docs/jamf-pro-api-overview and also some of the Jamf courses cover this.
Posted on 02-08-2024 04:07 AM
Hi, @dlondon Does this script still work for you on Jamf Pro 11? I've revisited this morning but it doesn't seem to work. Did you have to add an API role etc?
07-23-2024 01:39 PM - edited 07-23-2024 01:40 PM
API Role just needs Update Static Computer Groups and Read Computers.
This was written from scratch to use the new authentication. I've tested it to be working fine.
#!/bin/bash
# Fetch parameters from Jamf policy
CLIENT_ID="$4"
CLIENT_SECRET="$5"
STATIC_GROUP_ID="$6"
JAMF_URL="$7"
# Debugging: Display static group ID and Jamf URL without sensitive info
echo "STATIC_GROUP_ID: $STATIC_GROUP_ID"
echo "JAMF_URL: $JAMF_URL"
# Get the serial number
SERIAL_NUMBER=$(ioreg -l | awk '/IOPlatformSerialNumber/ { print $4;}' | sed 's/"//g')
echo "Serial Number: $SERIAL_NUMBER"
# Authenticate and get an access token
AUTH_RESPONSE=$(curl --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" --silent)
# Debugging: Display auth response status (remove sensitive details)
if [[ $AUTH_RESPONSE == *"access_token"* ]]; then
echo "Auth Response: Successfully retrieved access token"
else
echo "Auth Response: Failed to retrieve access token"
echo "Response: $AUTH_RESPONSE"
exit 1
fi
# Extract access token from the response
ACCESS_TOKEN=$(echo $AUTH_RESPONSE | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
# Get the Jamf Pro device ID using the access token
DEVICE_ID=$(curl -s -H "Accept: text/xml" -H "Authorization: Bearer ${ACCESS_TOKEN}" "${JAMF_URL}/JSSResource/computers/serialnumber/${SERIAL_NUMBER}" | xmllint --xpath '/computer/general/id/text()' -)
echo "Device ID: $DEVICE_ID"
# Check if DEVICE_ID was successfully retrieved
if [ -z "$DEVICE_ID" ]; then
echo "Failed to obtain Device ID"
exit 1
fi
# Add the computer to the static group
apiURL="JSSResource/computergroups/id/${STATIC_GROUP_ID}"
xmlHeader="<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
apiData="<computer_group><id>${STATIC_GROUP_ID}</id><computer_additions><computer><id>${DEVICE_ID}</id></computer></computer_additions></computer_group>"
ADD_COMPUTER_RESPONSE=$(curl -s \
--header "Authorization: Bearer ${ACCESS_TOKEN}" --header "Content-Type: text/xml" \
--url "${JAMF_URL}/${apiURL}" \
--data "${xmlHeader}${apiData}" \
--request PUT)
# Validate if the computer was added successfully
if echo "$ADD_COMPUTER_RESPONSE" | grep -q "<id>${STATIC_GROUP_ID}</id>"; then
echo "Successfully added computer to static group"
else
echo "Failed to add computer to static group"
echo "Response: $ADD_COMPUTER_RESPONSE"
exit 1
fi
exit 0
Posted on 02-08-2024 05:26 PM
Hi @paul_burgess_fu Still working on 11 although I've started using API roles so it's on the list to look at. Now is not a good time for me to do any development not directly related to packaging as I'm preparing for Semester 1 where I work. I don't know how familiar you are with scripting but try adding some echo statements and run it in Terminal and look at the value of various variables that are in the script.
You could also look at your policy log if you've just put it there before manually testing but I would encourage you always to manually test scripts before going to policy