JAMF Pro Classic API with PowerShell

Posted on September 08, 2022
Coding Powershell
...

JAMF Pro is deprecating basic authentication in the classic API endpoints in an upcoming release between now and the end of 2022. (The 10.43 release has been targeted)

Classic API Authentication Changes (jamf.com)

The new method will use bearer token authentication where you will obtain a token using the classic API /v1/auth/tokens endpoint and use this token for future API calls until it expires.

Note: /v1/auth/tokens is the only classic API endpoint that will accept a basic authentication input in order to obtain the bearer token for all future API calls.

Below is an example of how my workflows use this endpoint to obtain a token for future use.

PowerShell Function - Obtaining a Token
function getBearerToken([String]$jssuser, [String]$jsspass, [String]$jssurl) {
    $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$jssuser:$jsspass"))

    $Header = @{
        Authorization = "Basic $base64AuthInfo"
    }
    
    $Parameters = @{
        Method      = "POST"
        Uri         = "$jssurl/api/v1/auth/token"
        Headers     = $Header
    }
    
    $authToken = Invoke-RestMethod @Parameters

    $token = $authToken.token

    return $token
}

I can store the token in my script by saving the output of the function to a variable (eg. $token = getBearerToken $jssuser $jsspass $jssurl) which then allows me to pass through the token to later functions.

There is also an endpoint to renew a token prior to expiry (~30minutes) /v1/auth/keep-alive however, none of the API workflows I have in place needs to continuously run, and instead, run on an adhoc / scheduled once a day basis.

This allows me not to worry about keeping the token alive and instead, just obtain a new token from the authentication endpoint whenever I need my workflow to run.

Below are some example code snippets from my workflows.

PowerShell Functions - Using The Token
#Store the token in a variable
$jsstoken = getBearerToken $jssuser $jsspass $jssurl

#Pass the token through to a function to be used to get all devices in the group with the id of $groupid
$devices = getMobileGroupMembership $jsstoken $jssurl $groupid

#Loop through every device returned in the group array and send a remote command
foreach ($Device in $devices) {
    sendMobileRemoteCommand $jsstoken $jssurl $command $Device.id
}

#This function returns an array of mobile devices in the targeted group
function getMobileGroupMembership([String]$jsstoken, [String]$jssurl, [String]$groupid) {
    $Header = @{
        Authorization = "Bearer $jsstoken"
    }
    
    $Parameters = @{
        Method      = "GET"
        Uri         = "$jssurl/JSSResource/mobiledevicegroups/id/$groupid"
        Headers     = $Header
        ContentType = "application/json"
    }
    
    $Group = Invoke-RestMethod @Parameters

    return $Group.mobile_device_group.mobile_devices.mobile_device
}

#This function sends the command $command to the targeted mobile device id. (eg. SettingsEnableBluetooth)
function sendMobileRemoteCommand([String]$jsstoken, [String]$jssurl, [String]$command, [String]$deviceid) {
    $BodyXml = "<mobile_device_command><general><command>$command</command></general><mobile_devices><mobile_device><id>$deviceid</id></mobile_device></mobile_devices></mobile_device_command>"

    $Header = @{
        "authorization" = "Bearer $jsstoken"
    }
    
    $Parameters = @{
        Method      = "POST"
        Uri         = "$jssurl/JSSResource/mobiledevicecommands/command"
        Headers     = $Header
        ContentType = "application/xml"
        Body        = $BodyXml
    }

    Invoke-RestMethod @Parameters
}

When your API workflow is finished, it's best practice to invalidate the token so it is no longer useable. This is basically "signing out" making the token invalid and no longer useable in future API calls.

PowerShell Function - Invalidating a Token
function invalidateBearerToken([String]$token, [String]$jssurl) {

    $Header = @{
        Authorization = "Bearer $token"
    }
    
    $Parameters = @{
        Method      = "POST"
        Uri         = "$jssurl/api/v1/auth/invalidate-token"
        Headers     = $Header
    }

    Invoke-RestMethod @Parameters
}