UAPI GET specific data

shurkin18
Contributor II

Hello,

So, I have been trying to obtain a specific data from UAPI, but keep getting 404 errors, which is a response code that it cannot find the data requested... I am able to generate and authenticate with the token fine and I am even able to pull lists, for example pulling the list of all computers via:

computerslistall=$(curl -X GET --header "Authorization: Bearer $token" "Accept: application/json" "$jssurl/uapi/preview/computers")

Is working fine and if I echo $computerslistall - I get a full list of all computers currently in JSS. However, when I am trying to obtain a specific computer, no matter what I try I get 404... I have tried:
$jssurl/uapi/preview/computers/id/XXXX
$jssurl/uapi/preview/computers/ComputerOverview/id/XXXX
$jssurl/uapi/preview/computers/results/id/XXXX

So far I have been unable to find any GET example where a specific data is obtained via UAPI. Would appreciate if anybody has any info/explanation/example on this.

Here is a full script code, if anybody needs a working token creation/user via UAPI - this should work for you:

# server connection information
jssurl="https://jss.XXXXXX.com:8443"
username="XXXXXX"
password="XXXXXXXX"

# created base64-encoded credentials
encodedCredentials=$( printf "$username:$password" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )

# generate an auth token
authToken=$( /usr/bin/curl "$jssurl/uapi/auth/tokens" 
--silent 
--request POST 
--header "Authorization: Basic $encodedCredentials" )

# parse authToken for token, omit expiration
token=$( /usr/bin/awk -F " '{ print $4 }' <<< "$authToken" | /usr/bin/xargs )

# get existing json for PreStage ID | TESTING TOKEN / DATA retreival via UAPI
#prestageJson=$( /usr/bin/curl "$jssurl/uapi/v1/computer-prestages/$prestageID/scope" --silent --request GET --header "Authorization: Bearer $token" )
#echo "prestageJson is: $prestageJson"


#Start the UAPI scripts#
computerslistall=$(curl -X GET --header "Authorization: Bearer $token" "Accept: application/json" "$jssurl/uapi/preview/computers")
echo "computerslistall is: $computerslistall"
#End the UAPI scripts#

# expire the auth token
/usr/bin/curl "$jssurl/uapi/auth/invalidateToken" 
--silent 
--request POST 
--header "Authorization: Bearer $token"

exit 0

It is also very hard to find anything on UAPI, as it keeps finding old API stuff lol

1 ACCEPTED SOLUTION

Yes, updating the name is supported. Make sure to check the center column of the documentation to determine what inputs are allowed by the endpoint you're referencing. The right column, which you included a screenshot of provides an example of a successful response (not the request). I've included a screenshot for reference, which shows that "name" is a supported element in the request body:

Screen Shot 2021-09-10 at 7.34.17 AM.png

The following format is a valid request body:

{
  "general": {
    "name": "NewDeviceName"
  }
}

Finally, the error you're getting seems to be unrelated to the request body. 404 means that the server couldn't find the resource that you specified, in this case, the device ID. Make sure the variable you're using is set properly and/or being passed properly. If you're not super familiar with an API operation or the coding language, it's generally a good idea to ensure the code you're writing works first with hardcoded values, then work backwards to make it scalable/reusable, by substituting in the variables.

View solution in original post

29 REPLIES 29

SamF
Contributor

This API does not currently support the ability to request individual computer records. You may have noted that the endpoint you're referring to is currently in a preview state, which means that it's in development and likely to be changed or added to in the near future. I can't provide a specific timeline, but keep an eye on the release notes for updates regarding the API, there will likely be updates to this endpoint in the near future.

Regarding documentation, please refer to the Jamf Developer Portal for documentation on the API. In addition to general information about the API, there is an API reference which includes a comprehensive list of endpoints and resources that are supported at this time.

shurkin18
Contributor II

So, looks like with the latest JSS upgrade, it is possible to get computer data now via the UAPI, however I am still having issues determining the computer ID/name/etc. using UDID....

Obtaining computer data by a specific ID works fine:

compdatabyUAPI=$(curl -X GET --header “Authorization: Bearer $token” “Accept: application/json” “$jssurl/uapi/preview/computers-inventory/2498”)

When trying to obtain a computer ID from JSS using the UDID, getting a 404 error:

udid=`system_profiler -detailLevel full SPHardwareDataType | grep “Hardware UUID” | cut -f2 -d : | sed ‘s/^ *//g’`
echo “udid is: $udid”
#Determine the JSS ID of the machine
id=$( /usr/bin/curl “$jssurl/uapi/preview/computers/computers-inventory/2498/$udid” --silent --request GET --header “Authorization: Bearer $token”)
echo “id is: $id”

Error:

udid is: 55FC5FA2-219F-576E-8B8D-XXXXXXXXXXXXXXX id is: { “httpStatus” : 404, “errors” : [ ] }

Anybody had any luck with this? Or maybe we can get the current mac (where the script is being triggered) info via UAPI somehow else?

chadlawson
Contributor
Contributor

Hi, @shurkin18,

I'm not sure if I'm understanding you, but let me brain dump a couple questions and notes and see if it helps along the way.

In your example above, you are already specifying the jamf ID of the computer ("2498") but trying to append the UDID after that which the docs don't list as valid. And I'm not sure from your example that the computer with the jamf ID of 2498 is the same one with the UDID you list. Are those really the same computer? If so, just use the id which is documented and you should be fine.

Is there a specific reason you are wanting to use the preview/beta version? I ask because I'm more fluent in the "classic" API and can see a way to get what you want, but I just spent about 15 minutes on the beta and I don't know how to do what you want.

Maybe consider the following with the classic API:

#!/bin/sh

AUTH=XXXX
jssURL=xxxx

## Get the serial number for querying the API
SERIAL=$(system_profiler SPHardwareDataType | awk '/Serial Number/ {print $NF}')
#echo $SERIAL

## Get the computer record from the API
computerRecord=$( 
    curl -ks 
    -H "Authorization: Basic ${AUTH}" 
    -H "Accept: text/xml" 
    https://$jssURL/JSSResource/computers/serialnumber/${SERIAL}
)

The classic API will return JSON as well if that is your preference. But you can only upload XML with classic.

If I'm not catching what you specifically need from the preview API, let me know and I'll see what I can find. Feels like the only thing you are missing right now is trying to do both jamf ID and UDID on the call at once.

shurkin18
Contributor II

Hi @chadlawson,

Thanks for the suggestions. The issue seems to be with making the correct path to access specific data via the new API (UAPI). If you open it via https://jss.COMPANY.com:8443/uapi/doc, you can open up the "computers" just like with the old API and here is the result of the new API for example, one of the machines:

{ "totalCount": 436, "results": [ { "id": "2519", "location": { "username": "USERNAME", "realName": null, "emailAddress": null, "position": "", "phoneNumber": null, "department": null, "building": null, "room": "" }, "site": null, "name": "NAME", "udid": "F093F85C-7A4E-58BE-B0ED-21219C07A6C4", "serialNumber": "C07VR6GXG1HW", "operatingSystemVersion": "10.13.1", "operatingSystemBuild": "17B1003", "macAddress": "XXXXXXXXX", "assetTag": "", "modelIdentifier": "Macmini7,1", "mdmAccessRights": 0, "lastContactDate": "2020-08-19T14:26:20.39Z", "lastReportDate": "2020-08-19T09:05:13.661Z", "lastEnrolledDate": "2019-12-26T19:42:44.538Z", "ipAddress": null, "isManaged": true },

What I am trying to do is by using udid or serial # (doesn't really matter), obtain machine's name on the JSS. Using your example, I put this together, which seem to be locating the machine, but the data it produces is weird... Here is the code (the path I basically copied directly from UAPI, I have included 2 screenshots directly from there, so it will make more sense):

compserial=$(system_profiler SPHardwareDataType | awk '/Serial Number/ {print $NF}')
echo $compserial

computerinfo=$( /usr/bin/curl "$jssurl/uapi/preview/computers?page=0&size=0&pagesize=0&page-size=0&sort=name%3Aasc/${compserial}" --silent --request GET --header "Authorization: Bearer $token")
echo $computerinfo

The result I get:

} ]Managed" : true,: "2019-04-26T17:58:37.746Z", 300""ence",

I thought I was suppose to get full information on a specific machine based on the serial # and then grab from that data it's ID by modifying the curl string, but what I am getting is information that machine is "Managed" + "lastEnrollmedDate" ("2019-04-26T17:58:37.746Z"), I am not sure what 300 and ence is. Appreciate any other ideas.

e028f32d0afb4da59344969cb62c3577

912426f39db24f5f91f533ec0526e58d

SamF
Contributor

The Jamf Pro API does not yet support the ability to request individual devices via their serial number or UDID. As of v10.23.0 you can now request individual devices using the Jamf Pro assigned ID, via the /preview/computers-inventory/{id} endpoint, but ID cannot be a serial number or UDID.

Additional functionality for the computers endpoint is being implemented, and I'd encourage you to participate in the Jamf Pro beta program to see what's new in future releases and whether those solutions will meet your needs. For the time being, you'll either be required to get data for all computers and parse out the individual device records based on serial number or use the Classic API.

shurkin18
Contributor II

@Sam.Fortuna , gotcha, I thought with the v10.23.0 release it would be already possible "he Jamf Pro API does not yet support the ability to request individual devices via their serial number or UDID.", will try that for now "get data for all computers and parse out the individual device records based on serial number". Thanks for the info.

chadlawson
Contributor
Contributor

@shurkin18, sorry I was late getting back here, but I'm glad you got the answer you needed even if it isn't the one you wanted... yet.

Happy hunting!

pshaik
New Contributor II
# server connection information
jssurl="https://XXXX.jamfcloud.com"
username="pshaik@XXXX.com"
password="XXXX"
# created base64-encoded credentials
encodedCredentials=$( printf "$username:$password" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )
echo encodedCredentials are $encodedCredentials
# generate an auth token
authToken=$( /usr/bin/curl "$jssurl/uapi/auth/tokens" 
--silent 
--request POST 
--header "Authorization: Basic $encodedCredentials" )
echo auth token is $authToken

I tried to run this and i was returned with 401 error. Any suggestions or help would be really appreciated. @shurkin18 since you went through this.. thought of asking here itself.

shurkin18
Contributor II

@pshaik , 401 - is authentication failure, make sure you can authenticate with your token on the UAPI web page itself: https://jss.YOURCOMPANY.com:8443/uapi/doc/

For example, in here it fails:
1495a5fc04854e90a785ce7b72350019

See if you can authenticate on that page first.

pshaik
New Contributor II

Thanks @shurkin18 that solved the issue, I'm using it for the first time and realised that bearer token can only be obtained from swagger api portal only.

shurkin18
Contributor II

@SamF , it seems like this is still not implemented from the most current version, do you know by any chance when it will be implemented or if there is some sort of a work around at this point? Appreciate your input on this.

I forget the exact version this was added, but I believe it was 10.25.0. These endpoints were added and support the ability to apply filters via query parameters. There are many supported filter parameters, including UDID, serial number, etc. If there's a specific one you'd like to see which is not currently available, I'd encourage you to  file a feature request. Check out the developer portal for more information on the specific usage.

I see there is an option to sort, but can't figure out how to use it:

sortarray of strings

Sorting criteria in the format: property:asc/desc. Default sort
is general.name:asc. Multiple sort criteria are supported and must be separated with
a comma.

Fields allowed in the sort: general.name, udid

Do you know by any chance if there is an example somewhere?  As this is what I am trying to use, which is obviously wrong: 

id=$(curl -X GET --header "Authorization: Bearer $token" "Accept: application/json" "$jssurl/uapi/v1/computers-inventory/$udid")

 

Appreciate your input!

The option you're looking for is `filter` not sort. Here's an example that includes both, but note the filtering by `udid` attribute:

curl --request GET --url 'https://server.jamfcloud.com/api/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=udid%3D%3DCA40DB0C-60A3-11E4-90B8-12DF261F2C7E'

The developer portal will auto-generate code samples (in the right column) based on the inputs you supply within the center column. You can find more information about RSQL filtering and the available operators here. 

@SamF , that worked, thank you, appreciate it! Do you know by any chance if POST ability is still not released yet? As under "computer-inventory" POST ability I only see "attachments" "Upload attachment and assign to computer", like we used PUT in classic API?

I don't believe there's any intent or plans to provide an endpoint to POST computer-inventory data.  Devices must be enrolled to properly communicate with the server, simply adding a device via the API would not provide any device management capabilities, which is ultimately the purpose of Jamf Pro. If you'd like to see this feature, I'd recommend filing a feature request and make sure to describe the workflows that this feature would enable for you.

@SamF , appreciate your input, I meant not adding the device, but an ability to update computer attributes on JSS via UAPI, for example LAPS password, using the old API you could easily pass the  data to JSS computer attribute via curl

The ability to update computer records is implemented via the PATCH HTTP method. The PUT method within the Classic API technically behaves like PATCH where it updates only the fields you include within the request body whereas a PUT should technically overwrite everything. This improvement to using PATCH follows modern HTTP protocols and should make it more clear to consumers when partial updates can be applied (PATCH) vs. complete resource replacement (PUT).

@SamF, you have been very helpful with this, maybe you can help me out with this one. I was able to get and update extension attributes data, but can't sort or update (PATCH) the site... is it something still not supported or am I doing it wrong?

Trying to sort by sites:

 

siteslist=`curl --request GET \
--url "https://jss.MYCOMPANY.com:8443/uapi/v1/computers-inventory?section=GENERAL&page=0&page-size=100&sort=id%3Aasc&filter=site" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token"`
echo "siteslist is: $siteslist"

 

Getting error: ""No property site found for type ComputersDenormalizedEntity!""


When trying to PATCH/change site:

 

curl --request PATCH \
--url "https://jss.MYCOMPANY.com:8443/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
		"general": 
		{
					"site": 
					{
						"name" : "APAC - Bangalore"
					}
		}
}
'

 

Getting: ""Unrecognized field \"site\" (class com.jamfsoftware.computersinventory.api.update.ComputerGeneralUpdateDto), not marked as ignorable""


I was able to obtain and PATCH extension attribute, which is under GENERAL, the same way site is, but can't get it to work with the site... For example, this curl updates the exetnsion attribute as needed per definitionID:

 

curl --request PATCH \
--url "https://jss.MYCOMPANY.com:8443/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
	"general": 
	{
		"extensionAttributes": 
		[
			{
				"definitionId": "67",
				"values": ["'$currentlocaladminpass'"]
			}
		]
	}
}
'

 

 

I just don't understand what is the difference as per: https://developer.jamf.com/jamf-pro/reference/computer-inventory-1#get_v1-computers-inventory , in the example field on the right you can see the site (see attachment please), unless somehow this is not supported yet...

Screen Shot 2021-09-04 at 2.30.45 PM.png

The PATCH endpoint does not currently support the ability to update the site of a device. The right column where you're referencing the site element is the expected response to a PATCH request, not the request itself. The center column defines allowable elements to the request body, which you'll see sites are not listed. I'd recommend filing a feature request, if one does not already exist.

Regarding your request for sorting/filtering by sites, this is also not currently supported. You can find a list of the supported sort and filter parameters in the center pane as well, neither of which include sites. Finally, the proper syntax for filtering is via RSQL, which is further described in this article. Although this may not be the answer you were hoping for, I hope it helps you understand how the API is documented and what is or isn't supported.

@SamF  uh oh, that's unfortunate, I have submitted a feature request, thanks!

Sorry, but I keep coming back with more...

Is there a need to convert somehow data received via the curl "Accept: application/json", so that Bash can read it correctly? (it works fine with XML) If I read off an empty extension attribute from JSS, it shows as empty through "echo" for example, but a simple IF [ -z "$variable" ] or IF [ "$variable" == " " ] shows that it is NOT empty.... similar to when I parse current local password from extension attribute it is not accepted via $jamf_binary resetPassword, if I run the command NOT through the UAPI - it is accepted, I have a suspicion there is a problem between JSON and bash, but unsure.

I'm not entirely sure I understand the issue you're describing, however I can comment that bash doesn't have a great way to parse JSON. If you're set on using bash, I'd recommend using jq, which makes parsing JSON much easier.

looks like that's exactly what I need, thank you!

just an FYI, simply piping the parsed JSON through jq and then cleaning it up with sed/grep/etc. worked 🙂 thanks!

Since I continue to explore the UAPI :). do you know by any chance if PATCHing computer name on JSS is not available yet as well? Screen Shot 2021-09-09 at 9.47.08 PM.png

As I get 404 error when trying to do it:

 

	curl --request PATCH \
	--url "https://jss.MYCOMPANY.com:8443/uapi/v1/computers-inventory-detail/$id" \
	--header "Accept: application/json" \
	--header "Authorization: Bearer $token" \
	--header "Content-Type: application/json" \
	--data '
	{
		"general": 
		{
			"name" : "'$computernameinput'"
		}
	}
	'

 

 

Yes, updating the name is supported. Make sure to check the center column of the documentation to determine what inputs are allowed by the endpoint you're referencing. The right column, which you included a screenshot of provides an example of a successful response (not the request). I've included a screenshot for reference, which shows that "name" is a supported element in the request body:

Screen Shot 2021-09-10 at 7.34.17 AM.png

The following format is a valid request body:

{
  "general": {
    "name": "NewDeviceName"
  }
}

Finally, the error you're getting seems to be unrelated to the request body. 404 means that the server couldn't find the resource that you specified, in this case, the device ID. Make sure the variable you're using is set properly and/or being passed properly. If you're not super familiar with an API operation or the coding language, it's generally a good idea to ensure the code you're writing works first with hardcoded values, then work backwards to make it scalable/reusable, by substituting in the variables.

View solution in original post

You are the best 🙂

404 means that the server couldn't find the resource that you specified, in this case, the device ID - that was the case, I had a miss-type for the JSS URL so it couldn't locate the actual ID, I guess I misinterpreted the 404 error, once I set the correct JSS URL it worked like a charm. Thank You!

shurkin18
Contributor II

What I was working on mainly is LAPS to work via the new UAPI, if anybody has a need for it - you can find it here: https://gist.github.com/shurkin18 

A bunch of thanks to @SamF , I would be otherwise stuck for a while 🙂

shurkin18
Contributor II

@sam, hey Sam, so far UAPI is working well for me. The only thing is, when I update something on the UAPI using curl --request PATCH - it updates fine, but also prints out all the information about the mac as well, which is making it hard to read the logs...

 

For example, this command:

 

curl --request PATCH \
--url "$apiURL/uapi/v1/computers-inventory-detail/$id" \
--header "Accept: application/json" \
--header "Authorization: Bearer $token" \
--header "Content-Type: application/json" \
--data '
{
	"general": 
	{
		"extensionAttributes": 
		[
			{
				"definitionId" : "'$currentstatusid'",
				"values" : ["'$issuedstatus'"]
			}
		]
	}
}
'

 

updates an extension attribute by it's id correctly, but also parses ALL the information about the mac, such as:

 

{
  "id" : "2670",
  "udid" : "55FC5FA2-21********************",
  "general" : {
    "name" : "MM-MA*******",
    "lastIpAddress" : "172**********6",
    "lastReportedIp" : "19***********56",
    "jamfBinaryVersion" : "10.30.3-t1624643096",
    "platform" : "Mac",
    "barcode1" : null,
    "barcode2" : null,
    "assetTag" : "6*******6",
    "remoteManagement" : {
      "managed" : true,
      "managementUsername" : "ak******"
    },
    "supervised" : true,
    "mdmCapable" : {
      "capable" : true,
      "capableUsers" : [ "ak*****v" ]
    },
    "reportDate" : "2021-10-01T13:22:28.216

 

I just included a small portion, it prints basically ALL the data about the mac, like if I would run curl GET without trimming.

 

Any ideas how to prevent it from printing all of that info?