API Call for Naming Convention Script with Bearer Token Auth

Jmardian
New Contributor III

I am having issues updating a classic API script with the bearer token. Testing the script below I seem to have an issue somewhere as the variable will not propagate accordingly. I am getting "MBA-1" every time I run the script. 

 

 

# Getting the computer's serial number to make the API call

serialnumber=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')

# Decrypting the string above

function DecryptString() {
echo "${1}" | /usr/bin/openssl enc -md md5 -aes256 -d -a -A -S "${2}" -k "${3}"
}
string=$(DecryptString $EncryptedString $Salt $Passphrase)

model=$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk '{print $3}' | sed 's/[1-9].*$//')

case $model in
MacBookPro)   short=MBS ;;
MacBook)       short=MB ;;
MacBookAir)   short=MBA ;;
iMac)         short=IMC ;;
*)             short=UNK ;;
esac

# A basic API Call that's getting information for the computer.

# computerxml=$(curl -s -H “Authorization: Bearer ${token}” -H ${jamfurl}/JSSResource/computers/serialnumber/${serialnumber} -X GET)

# Finding a specific component of the XML using xpath (in this case, the ID)

# id=$(echo $computerxml | xpath 'string(/computer/general/id)')

# Create an array of Computer Names in Jamf
var=$(curl -s -H “Authorization: Bearer ${token}” -H ${jamfurl}/JSSResource/computers -X GET | tidy -xml | grep '<name>' | sed -n 's|<name>\(.*\)</name>|\1|p' | grep $short | cut -d'-' -f 2)

# Add computer names to array called "name"
name=($var)

# Adds the number of values in array to varialbe "namen"
namen=${#name[@]}

# Adds the highest number in array to variable "hinumber"
IFS=$'\n'
hinumber=$(echo "${name[*]}" | sort -nr | head -n1)

# Adds the highest number +1 to a varialbe "NUM"
NUM=$(($hinumber+1))

computerName=${short}-${NUM}

serialnumber=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')

curl -s -H “Authorization: Bearer ${token}” -H "Content-Type: application/xml" ${jamfurl}/JSSResource/computers/serialnumber/${serialnumber} -d "<computer><general><name>${computerName}</name></general></computer>" -X PUT

scutil --set ComputerName ${computerName}
scutil --set HostName ${computerName}
scutil --set LocalHostName ${computerName}

echo "Computer name changed to $computerName"

jamf displayMessage -message "The computer name is ${computerName} - Please make a label reflecting this. "

 

 

The script result reads: No warnings or errors were found.  Not sure if the issue is syntax related or with the bearer authentication. 

 

1 ACCEPTED SOLUTION

Tribruin
Valued Contributor II

Looks like you might have an order of operations issue. On line 29 you call the function getAPIToken with the arguments jamfurl & basicAuth, but you don't define jamfurl until line 44. Plus, basicAuth is defined in line 28, but that line commented out (and uses the variables jamfuser and jamfpass that are not defined lines 45 & 46. You need to define your variables before you start calling them. 

Not trying to be mean, but this definitely looks like a script that has been updated and modified multiple times. For example, it has a function to use "encrypted" strings (line 68), but then never uses the decrypted string.Lots of commented lines that may or may not be necessary. 

At this point, I would suggest doing some rubber duck debugging. https://en.wikipedia.org/wiki/Rubber_duck_debugging 

Work through your script one line at a time. Try running the script locally with the command bash -x /path/to/script.sh. That will output each line as it runs, so you can confirm that commands are running successfully and, most importantly, variables are being substituted correctly. Also, you will want to run the script with exit point after key point. For example, add an exit statement right after getting the token (and add an echo $token to see the result before the exit). Run the script. Did you get a token successfully? If so, remove the exit statement and move down the line to the next major step (pull the computer list?) and repeat the process. 

Being able to to debug a script, especially if you didn't write it, is a learned skill. And, while I encourage you to try to resolve this yourself, ChatGPT is a quick way to do simple troubleshooting. Try tuning your script through ChatGPT and ask it to help debug. You will still want to review your script, and it probably won't work exactly as presented. But compare the output to your original script and see if you understand the differences. 

View solution in original post

5 REPLIES 5

Tribruin
Valued Contributor II

 I am not seeing in your script where you are making an API call to get a bearer token and set the 'token' variable before making your call to download the list of computers. You need do a curl call to the endpoint, something like this:

tokenResp=$(curl -k -u "username:password" -X POST "https:/yoururl.jamfcloud.com/api/v1/auth/token" -H "accept: application/json")

# parse the token from the response
token=$(echo $tokenResp | awk -F '[:,{"}]' ' {print $6} ')

Also, just a suggestion, you don't need to do PUT command to set the name in Jamf. Once you use the scutil command to set the local name, just run a jamf recon and the Jamf inventory will be update. Not a big deal however.  

Jmardian
New Contributor III

I didn't include the initial call in the script above: 

 

#!/bin/bash

## This function calls the Jamf (newer) "Pro" API to generate a token for subsequent calls to the "Pro" or "Classic" APIs.
function getAPIToken() {
	jamfURL=$1
	basicAuth=$2

	authToken=$(curl -s \
		--request POST \
		--url "${jamfURL}/api/v1/auth/token" \
		--header "Accept: application/json" \
		--header "Authorization: Basic ${basicAuth}" \
		2>/dev/null \
	)
	
	## Courtesy of Der Flounder
	## Source: https://derflounder.wordpress.com/2021/12/10/obtaining-checking-and-renewing-bearer-tokens-for-the-jamf-pro-api/
	if [[ $(/usr/bin/sw_vers -productVersion | awk -F . '{print $1}') -lt 12 ]]; then
		api_token=$(/usr/bin/awk -F \" 'NR==2{print $4}' <<< "$authToken" | /usr/bin/xargs)
	else
		api_token=$(/usr/bin/plutil -extract token raw -o - - <<< "$authToken")
	fi
	
	echo ${api_token}
}

## Get the token and verify connection
# basicAuth=$(echo -n "${jamfuser}:${jamfpass}" | base64)
token=$(getAPIToken "${jamfurl}" "${basicAuth}")
if [[ "${token}" == "" ]]; then
	echo "Error: Unable to authenticate"
	exit 1
fi

####################################################################################################
#
# This is the essential components needed to make an encrypted API call with variables
#
####################################################################################################


# HARDCODED VALUE FOR JAMF PRO URL IS SET HERE

jamfurl="XXX"
jamfuser="XXX"
jamfpass="XXX"

# ENCRYPTION IS DEFINED HERE. FOR MORE INFORMATION ON HOW TO CREATE ENCRYPTION IN A SCRIPT, VISIT
# https://docs.jamf.com/education-services/resources/20190418/400_Resources_S2_L5_.html

EncryptedString=$4
Salt='ca89daaf664dae66'
Passphrase='06ebc9f526579b8523b376e4'

####################################################################################################
#
# SCRIPT CONTENTS - DO NOT MODIFY BELOW THIS LINE
#
####################################################################################################

# Getting the computer's serial number to make the API call

serialnumber=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')

# Decrypting the string above

function DecryptString() {
echo "${1}" | /usr/bin/openssl enc -md md5 -aes256 -d -a -A -S "${2}" -k "${3}"
}
string=$(DecryptString $EncryptedString $Salt $Passphrase)

model=$(system_profiler SPHardwareDataType | grep "Model Identifier" | awk '{print $3}' | sed 's/[1-9].*$//')

case $model in
MacBookPro)   short=MBS ;;
MacBook)       short=MB ;;
MacBookAir)   short=MBA ;;
iMac)         short=IMC ;;
*)             short=UNK ;;
esac

# A basic API Call that's getting information for the computer.

# computerxml=$(curl -s -H “Authorization: Bearer ${token}” -H ${jamfurl}/JSSResource/computers/serialnumber/${serialnumber} -X GET)

# Finding a specific component of the XML using xpath (in this case, the ID)

# id=$(echo $computerxml | xpath 'string(/computer/general/id)')

# Create an array of Computer Names in Jamf
var=$(curl -s -H “Authorization: Bearer ${token}” -H ${jamfurl}/JSSResource/computers -X GET | tidy -xml | grep '<name>' | sed -n 's|<name>\(.*\)</name>|\1|p' | grep $short | cut -d'-' -f 2)

# Add computer names to array called "name"
name=($var)

# Adds the number of values in array to varialbe "namen"
namen=${#name[@]}

# Adds the highest number in array to variable "hinumber"
IFS=$'\n'
hinumber=$(echo "${name[*]}" | sort -nr | head -n1)

# Adds the highest number +1 to a varialbe "NUM"
NUM=$(($hinumber+1))

computerName=${short}-${NUM}

serialnumber=$(system_profiler SPHardwareDataType | awk '/Serial/ {print $4}')

curl -s -H “Authorization: Bearer ${token}” -H "Content-Type: application/xml" ${jamfurl}/JSSResource/computers/serialnumber/${serialnumber} -d "<computer><general><name>${computerName}</name></general></computer>" -X PUT

scutil --set ComputerName ${computerName}
scutil --set HostName ${computerName}
scutil --set LocalHostName ${computerName}

echo "Computer name changed to $computerName"

jamf displayMessage -message "The computer name is ${computerName} - Please make a label reflecting this. "

 Do I still need curl call that you mentioned above? Clearly, I'm a little out of my scope here with scripting, as I inherited this script with my enrollment workflow. If I do need the Curl you mentioned where exactly would it need to go? Thanks for your help! 

 

Also, here is the script result I am seeing: 

 

Script result: No warnings or errors were found.
To learn more about HTML Tidy see http://tidy.sourceforge.net Please send bug reports to html-tidy@w3.org HTML and CSS specifications are available from http://www.w3.org/ Lobby your company to join W3C, see http://www.w3.org/Consortium <html> <head> <title>Status page</title> </head> <body style="font-family: sans-serif;"> <p style="font-size: 1.2em;font-weight: bold;margin: 1em 0px;">Unauthorized</p> <p>The request requires user authentication</p> <p>You can get technical details <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2">here</a>.<br> Please continue your visit at our <a href="/">home page</a>. </p> </body> </html>Computer name changed to MBA-1

Tribruin
Valued Contributor II

Looks like you might have an order of operations issue. On line 29 you call the function getAPIToken with the arguments jamfurl & basicAuth, but you don't define jamfurl until line 44. Plus, basicAuth is defined in line 28, but that line commented out (and uses the variables jamfuser and jamfpass that are not defined lines 45 & 46. You need to define your variables before you start calling them. 

Not trying to be mean, but this definitely looks like a script that has been updated and modified multiple times. For example, it has a function to use "encrypted" strings (line 68), but then never uses the decrypted string.Lots of commented lines that may or may not be necessary. 

At this point, I would suggest doing some rubber duck debugging. https://en.wikipedia.org/wiki/Rubber_duck_debugging 

Work through your script one line at a time. Try running the script locally with the command bash -x /path/to/script.sh. That will output each line as it runs, so you can confirm that commands are running successfully and, most importantly, variables are being substituted correctly. Also, you will want to run the script with exit point after key point. For example, add an exit statement right after getting the token (and add an echo $token to see the result before the exit). Run the script. Did you get a token successfully? If so, remove the exit statement and move down the line to the next major step (pull the computer list?) and repeat the process. 

Being able to to debug a script, especially if you didn't write it, is a learned skill. And, while I encourage you to try to resolve this yourself, ChatGPT is a quick way to do simple troubleshooting. Try tuning your script through ChatGPT and ask it to help debug. You will still want to review your script, and it probably won't work exactly as presented. But compare the output to your original script and see if you understand the differences. 

Jmardian
New Contributor III

Sometimes practical advice is the best advice, so thank you! And this does not come off mean at all. You are spot on. This is a Frankenstein script that I've tried to patch together with an elementary background in programming. I was hoping to throw darts blindly and hit the bullseye, but I never really thought it'd work. Thanks again for taking the time to look at it, I really appreciate it. 

Jmardian
New Contributor III

Hate to say it... but ChatGPT identified everything. Several syntax issues and it pointed out the order of operations issue. Not the best solution, I know, but for someone like myself who has no IT team and a hundred other responsibilities, this saved me a lot of time. Sure, at the sake of my own professional development, but I gotta pick my battles.