Posted on 02-02-2024 08:05 AM
My organisation has recently procured Jamf, and it's a majority Windows org.
I'm quite new to Jamf, but familiar with MDMs, and most of my day is spent using a Windows machine.
We have a number of applications that we have API access to, and I've been successful in setting up API access within powershell using client credentials, and able to make basic GET calls, and some simple POST commands - such as creating departments, or categories.
I have a list of departments I need to create, and want to also create smart groups for each of those departments, pulling in computers where the department is set to a specific value.
I've created a smart group manually and able to GET the configuration options in an API output.
My familiarity with API is using JSON for the POST calls, but looking at the GET output, it's an XML format, in a single string (weird to me, but maybe that's shell standard?)
However, I cannot get it to POST and create a group, when I modify the specific options from the GET output.
Even if I export the array as an XML file, and import it and pass that in the body, it rejects the format of the request.
For example, if a department is "Medical", I want the smart group to pull in if the department of the computer record says "Medical"
I've even tried passing that in a string as the request body, TNA:
New-JamfComputerSmartGroup -body "<computer_group><id>25</id><name>Department-Medical</name><is_smart>true</is_smart><site><id>-1</id><name>None</name></site><criteria><size>1</size><criterion><name>Department</name><priority>0</priority><and_or>and</and_or><search_type>is</search_type><value>Medical</value><opening_paren>false</opening_paren><closing_paren>false</closing_paren></criterion></criteria><computers><size>0</size></computers></computer_group>"
Anyone else had some success with this?
Solved! Go to Solution.
Posted on 02-09-2024 12:51 AM
Good news, managed to get this one resolved!
Took me a little while, but I'll document it here so that it's recorded for anyone else who might find it useful.
Initially, I had a look at the reference documentation, and copied the receipe into Shell on one of my test macs. When passing the XML body, it worked! So, I set about trying a few things in translation from curl to Powershell commands.
I took a guess at changing the content type in the Invoke-RestMethod call to "application/xml", because this is what the curl command was using, and the body being passed was in an XML format. And this worked too!
So, good news is that now Powershell is creating the group, but relies on a full, hardcoded XML body being passed to the function, which for repeat scripting and multiple group creation calls, is not ideal.
Working with a colleague (who is far better at PS stuff than I believe I am), we came up with the theory of storing an XML template in a file, and calling that into the function.
I copied the sample body from the Jamf API reference documentation and saved this in a separate file. Then imported that using
[xml]$xmlTemplate = Get-Content -path .\path\to\template.xml
My PS function contained a few parameters, namely one for the group name, and one for the search string.
I then used these parameter values to modify specific strings in the XML template, to update it to what I wanted the group to look like in Jamf
$xmlTemplate.computer_group.name = $groupName
$xmlTemplate.computer_group.criteria.criterion.value = $searchValue
And then, pass the modified XML in the body of the API call
Invoke-RestMethod -uri $URI -method $METHOD -headers $Headers -body $xmlFromFile -ContentType "application/xml"
And it worked!
I've gone back in and added many more parameters to the function since - things like
etc - this will give me more flexibility to create groups in the future not constrained to this specific use case.
I've given a lot of these parameters a default value where I think it's suitable for my setup, but these can be overwritten when passing the parameter.
We got there!
Posted on 02-02-2024 08:44 AM
I don't know much Powershell, so I can't help you there, but one thing I see that you need to change is, the ID for a new group should be "0" not a specific number like you have there. 0 designates a new object to be made when used in conjunction with a POST command.
The rest of the XML format looks ok to me at first glance, but it's been a while since I've used it to create new groups.
Posted on 02-07-2024 05:42 AM
Thanks mm2270
I've changed the ID to 0, and also modified the API URI to be https://jssname.jamfcloud.com/JSSResource/computergoups/id/0, but still getting an error.
"<computer_group><id>0</id><name>Department-Medical</name><is_smart>true</is_smart><site><id>-1</id><name>None</name></site><criteria><size>1</size><criterion><name>Department</name><priority>0</priority><and_or>and</and_or><search_type>is</search_type><value>Medical</value><opening_paren>false</opening_paren><closing_paren>false</closing_paren></criterion></criteria><computers><size>0</size></computers></computer_group>"
At the moment, the error (when using the body above) is:
Unsupported Media Type
The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method
I did have a -ContentType parameter with the value as "application/json", but even when removing that, and trying again, I get the same result.
Posted on 02-09-2024 12:51 AM
Good news, managed to get this one resolved!
Took me a little while, but I'll document it here so that it's recorded for anyone else who might find it useful.
Initially, I had a look at the reference documentation, and copied the receipe into Shell on one of my test macs. When passing the XML body, it worked! So, I set about trying a few things in translation from curl to Powershell commands.
I took a guess at changing the content type in the Invoke-RestMethod call to "application/xml", because this is what the curl command was using, and the body being passed was in an XML format. And this worked too!
So, good news is that now Powershell is creating the group, but relies on a full, hardcoded XML body being passed to the function, which for repeat scripting and multiple group creation calls, is not ideal.
Working with a colleague (who is far better at PS stuff than I believe I am), we came up with the theory of storing an XML template in a file, and calling that into the function.
I copied the sample body from the Jamf API reference documentation and saved this in a separate file. Then imported that using
[xml]$xmlTemplate = Get-Content -path .\path\to\template.xml
My PS function contained a few parameters, namely one for the group name, and one for the search string.
I then used these parameter values to modify specific strings in the XML template, to update it to what I wanted the group to look like in Jamf
$xmlTemplate.computer_group.name = $groupName
$xmlTemplate.computer_group.criteria.criterion.value = $searchValue
And then, pass the modified XML in the body of the API call
Invoke-RestMethod -uri $URI -method $METHOD -headers $Headers -body $xmlFromFile -ContentType "application/xml"
And it worked!
I've gone back in and added many more parameters to the function since - things like
etc - this will give me more flexibility to create groups in the future not constrained to this specific use case.
I've given a lot of these parameters a default value where I think it's suitable for my setup, but these can be overwritten when passing the parameter.
We got there!
Posted on 07-03-2024 01:32 PM
I don't suppose you can provide an example of how you get an access token with powershell?
As you say those particular bits are exampled with curl and I'm still trying to figure out how to perform it via powershell.
Posted on 07-03-2024 03:20 PM
Ignore my last message, after cobbling several pages of similar PowerShell examples, I've made some progress.
$body = @{client_name='Posh'
client_id='<ClientID from Jamf>'
client_secret='<Secret string from Jamf>'
grant_type='client_credentials'}
$contentType = 'application/x-www-form-urlencoded'
$uri = 'https://<Tenant>.jamfcloud.com'
$api = "$uri/api/oauth/token"
$ObjWebReq = Invoke-WebRequest -Method Post -Uri $api -Body $body -ContentType $contentType
$Token = $ObjWebReq.Content | ConvertFrom-Json
$Token.access_token
Will work on validating the token duration next
Posted on 07-04-2024 06:40 AM
Nice one @Zer0reZ!
I do it a slightly different way, but fundamentally the bits 'under the hood' are the same.
For validating token duration, you could grab the date and time when you complete the request, and then add the token validity duration to that time.
For example:
$ObjWebReq = Invoke-WebRequest -Method Post -Uri $api -Body $body -ContentType $contentType
$requestTime = Get-Date
$Token = $ObjWebReq.Content | ConvertFrom-Json
$TokenDuration = $Token.expires_in
$TokenExpiry = $requestTime.addseconds($tokenDuraction)
Write-Host "Current access token expires at: $TokenExpiry
$Token.access_token