JAMF Pro API Monterey vs. Big Sur

cbarra
New Contributor II

I modified an existing script (credit to Richard Purves) that runs successfully to change the computer name to the barcode using the Pro API on Monterey machines.  My same script does not seem to work on Big Sur machines.  I use the API to pull the barcode and then use that barcode to rename the computer.  I initially thought it was because I was using the #!/bin/zsh shell, but I have switched it back to #!/bin/bash.  Same error.

The error I get from the logs is as follows:

"Script result: execution error: Error: SyntaxError: JSON Parse error: Unterminated string (-2700)"

Clearly has something to do with the JSON Parse, but this is my first Pro API script so I have very little experience here.

Copy of my script:

#!/bin/bash

# POC script for Jamf Pro API

# Variables first

# API user accounts here. One for reading, one for writing back. Security.
# Generate API base64 credentials by using:
apib64="xxxxxxxxxxxxxxxxxxxxxxx"

PREFIX="UH"
SUFFIX="UOC"

# Current JSS address
jssurl=$( /usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url )

# Hardware UDID of the Mac you're running this on
udid=$( /usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }' )

# Use our base64 creds to generate a temporary API access token in JSON form
# Use tr to strip out line feeds or the JXA will not like the input
# Retrieve the read token from the JSON response
jsonresponse=$( /usr/bin/curl -s "${jssurl}api/v1/auth/token" -H "authorization: Basic ${apib64}" -X POST | tr -d "\n" )
token=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$jsonresponse\`).token" )

# Use the read token to find the ID number of the current Mac
computerrecord=$( /usr/bin/curl -s "${jssurl}api/v1/computers-inventory?section=GENERAL&filter=udid%3D%3D%22${udid}%22" -H "authorization: Bearer ${token}" )
BARCODE=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$computerrecord\`).results[0].general.barcode1" )

echo "Barcode: $BARCODE"

sudo jamf setComputerName -name $PREFIX"$BARCODE"$SUFFIX

sudo jamf recon

# Ok we're done now.
# Invalidate the token
/usr/bin/curl -s -k "${jssurl}api/v1/auth/invalidate-token" -H "authorization: Bearer ${token}" -X POST

# All done
exit 0

1 ACCEPTED SOLUTION

cbarra
New Contributor II

Corrected code that appears to work on Big Sur:

#!/bin/bash

# POC script for Jamf Pro API

# Variables first

# API user accounts here. One for reading, one for writing back. Security.
# Generate API base64 credentials by using:
# printf "username:password" | iconv -t ISO-8859-1 | base64 -i -
apib64="xxxxxxxxxxxxxxxxxxx"

PREFIX="UH"
SUFFIX="UOC"

# Current JSS address
jssurl=$( /usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url )

# Hardware UDID of the Mac you're running this on
udid=$( /usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }' )

# Use our base64 creds to generate a temporary API access token in JSON form
# Use tr to strip out line feeds or the JXA will not like the input
# Retrieve the read token from the JSON response
jsonresponse=$( /usr/bin/curl -s "${jssurl}api/v1/auth/token" -H "authorization: Basic ${apib64}" -X POST | tr -d "\n" )
token=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$jsonresponse\`).token" )

# Use the read token to find the ID number of the current Mac
computerrecord=$( /usr/bin/curl -s "${jssurl}api/v1/computers-inventory?section=GENERAL&filter=udid%3D%3D%22${udid}%22" -H "authorization: Bearer ${token}" )

# Code for Monterey
# BARCODE=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$computerrecord\`).results[0].general.barcode1" )

#Code for Big Sur and lower
BARCODE=$( echo $computerrecord | awk 'BEGIN{RS=","} /barcode1/ {print $4}' | cut -d "\"" -f 2)

echo $BARCODE
# echo $computerrecord

# sudo jamf setComputerName -name $PREFIX"$BARCODE"$SUFFIX

# sudo jamf recon

# Ok we're done now.
# Invalidate the token
/usr/bin/curl -s -k "${jssurl}api/v1/auth/invalidate-token" -H "authorization: Bearer ${token}" -X POST

# All done
exit 0

View solution in original post

11 REPLIES 11

mm2270
Legendary Contributor II

I can't be sure, but if I had to guess, the issue is probably with one (or both) of these 2 lines in the script

 

token=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$jsonresponse\`).token" )

 

Or

 

computerrecord=$( /usr/bin/curl -s "${jssurl}api/v1/computers-inventory?section=GENERAL&filter=udid%3D%3D%22${udid}%22" -H "authorization: Bearer ${token}" )
BARCODE=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$computerrecord\`).results[0].general.barcode1" )

 

In both cases it's calling JavaScript and running a JSON.parse command against some output. It seems likely that's the issue.

Also, I'm not really sure about that API call. It seems like it's doing an actual search against Jamf and then parsing the JSON output, which is bit... strange? I've never done anything like this myself. That being said, if the original script was written by @franton then I have to believe it was a solid working script, because he knows his stuff.

I would imagine though that those lines could be swapped out for a regular API call to locate the device and extract the elements you need from the output. Unless there's some way around the original JSON error you're seeing, but I think that's a bit above what I'm capable of doing.

cbarra
New Contributor II

I believe the token portion is working correctly.  Would a JSON parse behave differently between Big Sur and Monterey?

franton
Valued Contributor III

Actually yes it can as the libraries change from OS to OS. I have code I know works on Big Sur, and code I know works on Monterey with an OS version check to swap between them.

franton
Valued Contributor III

Hi,

Excellent, someone actually read my blog post!

So the issue that i've found is that i've had inconsistent json responses out of JSS. Are you on prem or cloud? If on prem, which Jamf version are you currently running?

First thing to make sure is the base64 credentials are correct. You can find the appropriate code to generate this from Jamf at https://developer.jamf.com/jamf-pro/docs/code-samples 

Once that's done, you need then make sure the account has correct privileges to perform the operations. Test with a full admin account first, then start scoping down access. My own API account for this has read only on Computers, Departments and Users.

That should take care of the token side.

(btw @mm2270 thanks for the vote of confidence. I had a lot of help for this on mac admins slack. Since my own environment is nearly all macOS 12 now, i've moved to using plutil instead. You're right in that you have to pull a giant subset of the computer record down, filtered by the section field then find what you want inside it.)

I think where this script is getting unstuck is the "BARCODE" line. That's not something of mine, and it's not something I even do myself.

This is where I recommend having a look at this: https://developer.jamf.com/jamf-pro/reference/get_v1-computers-inventory Jamf provides excellent API documentation with an online tester both on their development page, and also on your JSS itself. Usually corpname.jamfcloud.com/api .

I'd look in those places to see if you're getting the data you think you are first. That would explain the errors. API is more like an art to me than a science but it's getting better!

cbarra
New Contributor II

@franton Thanks for the reply.  Your blog posts are great and super helpful.  I'm not getting any sort of authorization errors, so I think the token portion works fine.  Definitely works in Monterey.

The "Barcode" line is simply a modification of your line below.  I just changed the variable name and modified the parse to access the barcode1 section of the "$computer record".

id=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$computerrecord\`).results[0].id" )

It seems the API call works in both cases and returns the $computerrecord, but the parse is somehow different.

franton
Valued Contributor III

What's interesting is I ran all your code (with my own account) on my system and got "null" for the barcode1 field. Which is correct for me. Is that set on your inventory correctly?

cbarra
New Contributor II

Yes, I have the barcode1 field populated on all of our assets in JAMF.  Did you run that on a machine with Big Sur?

franton
Valued Contributor III

Sadly not, i've less than 150 Big Sur left.

franton
Valued Contributor III

Yuck, not happy with this but here's a PoC.

test=$( echo $computerrecord | grep "barcode1" | cut -d":" -f2 )
test="${test:1:-1}"

That horrible lump of code pulls the barcode1 line out then strips off the first and last characters to remove the leading space and the trailing comma.

cbarra
New Contributor II

After MANY hours of research along with trial and error, I wound up using the line below:

BARCODE=$( echo $computerrecord | awk 'BEGIN{RS=","} /barcode1/ {print $4}' | cut -d "\"" -f 2)

 Thank you for all of the input!

cbarra
New Contributor II

Corrected code that appears to work on Big Sur:

#!/bin/bash

# POC script for Jamf Pro API

# Variables first

# API user accounts here. One for reading, one for writing back. Security.
# Generate API base64 credentials by using:
# printf "username:password" | iconv -t ISO-8859-1 | base64 -i -
apib64="xxxxxxxxxxxxxxxxxxx"

PREFIX="UH"
SUFFIX="UOC"

# Current JSS address
jssurl=$( /usr/bin/defaults read /Library/Preferences/com.jamfsoftware.jamf.plist jss_url )

# Hardware UDID of the Mac you're running this on
udid=$( /usr/sbin/ioreg -rd1 -c IOPlatformExpertDevice | awk '/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }' )

# Use our base64 creds to generate a temporary API access token in JSON form
# Use tr to strip out line feeds or the JXA will not like the input
# Retrieve the read token from the JSON response
jsonresponse=$( /usr/bin/curl -s "${jssurl}api/v1/auth/token" -H "authorization: Basic ${apib64}" -X POST | tr -d "\n" )
token=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$jsonresponse\`).token" )

# Use the read token to find the ID number of the current Mac
computerrecord=$( /usr/bin/curl -s "${jssurl}api/v1/computers-inventory?section=GENERAL&filter=udid%3D%3D%22${udid}%22" -H "authorization: Bearer ${token}" )

# Code for Monterey
# BARCODE=$( /usr/bin/osascript -l 'JavaScript' -e "JSON.parse(\`$computerrecord\`).results[0].general.barcode1" )

#Code for Big Sur and lower
BARCODE=$( echo $computerrecord | awk 'BEGIN{RS=","} /barcode1/ {print $4}' | cut -d "\"" -f 2)

echo $BARCODE
# echo $computerrecord

# sudo jamf setComputerName -name $PREFIX"$BARCODE"$SUFFIX

# sudo jamf recon

# Ok we're done now.
# Invalidate the token
/usr/bin/curl -s -k "${jssurl}api/v1/auth/invalidate-token" -H "authorization: Bearer ${token}" -X POST

# All done
exit 0