Public/Connect-JamfPro.ps1

<#
    .SYNOPSIS
        Connects to JamfPro
    .DESCRIPTION
        Connects to JamfPro with bearer token
    .PARAMETER Server
        Specify the JamfPro 'server'
    .PARAMETER Credential
        Specify the credentails
    .PARAMETER Force
        Force reconnection to API ignoring 'valid' token
    .EXAMPLE
        Connect-JamfPro -Server trusty.jamfcloud.com -Credential $Creds
    .EXAMPLE
        Connect-JamfPro -Server trusty.jamfcloud.com -Credential $Creds -Force
#>

function Connect-JamfPro {

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true,
            HelpMessage = 'Url of the Jamf Pro server')]
        [ValidateNotNull()]
        [ValidateNotNullOrEmpty()]
        [string]$Server,

        [Parameter(Mandatory = $false,
            HelpMessage = 'Valid credentials for the Jamf Pro API')]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty,

        [Parameter(Mandatory = $false)]
        [switch]$Force
    )

    BEGIN {
        $Server = ConvertTo-FQDN $Server
        $uri_Auth      = "https://$Server/api/v1/auth/token"
        $uri_Verify    = "https://$Server/api/v1/auth"
        $uri_Build     = "https://$Server/JSSCheckConnection"
        $uri_KeepAlive = "https://$Server/api/v1/auth/keep-alive"
        $app_Type   = "application/json"
        $app_Headers = @{
            Server  = "$Server"
            BaseURL = "$Server/JSSResource"
            Accept  = $app_Type
        }
        Add-Type -TypeDefinition @"
            using System;
            using System.Security;
            
            public class ConvertToSS {
                public static SecureString Set(string token) {
                    SecureString ss = new SecureString();
                    foreach (var c in token) {
                        ss.AppendChar(c);
                    }
                    return ss;
                }
            }
            
"@

    }

    PROCESS {
        # Check for existing session
        if ( $force -or (-not $TokenJamfPSPro.expires) -or (-not $TokenJamfPSPro.token) ) {

            Write-Debug "Try to get new token"

            if ( $TokenJamfPSPro.Credential ) {
                Write-Debug "Using saved credential: $($TokenJamfPSPro.Credential.UserName)"
                $Credential = $TokenJamfPSPro.Credential
            }
            while ( $Credential -eq [System.Management.Automation.PSCredential]::Empty ) {
                $Credential = Get-Credential
            }

            try {
                $TokenResult = Invoke-RestMethod $uri_Auth -Credential $Credential -Authentication Basic -Method POST -ContentType $app_Type
            } catch {
                Write-Error "Could not get token from `'$Server`'"
                break
            }

            $Token = [PSCustomObject]@{
                token      = [ConvertToSS]::Set($TokenResult.Token)
                expires    = (Get-Date $TokenResult.expires).AddMinutes((Get-TimeZone).BaseUtcOffset.TotalMinutes)
                server     = $Server
                credential = $Credential
            }

            try {
                $VerifyAuth = Invoke-RestMethod $uri_Verify -Authentication Bearer -Token $Token.token -ContentType $app_Type -Headers $app_Headers
                Set-Variable -Name 'TokenJamfPSPro' -Value $Token -Scope Global -Option ReadOnly -Description "Token for Jamf Pro API" -Force
                $JamfBuild = Invoke-RestMethod $uri_Build -Authentication Bearer -Token $TokenJamfPSPro.token -ContentType $app_Type -Headers $app_Headers -Method GET
            } catch {
                $Error[0]
                Write-Error "$($_.Exception.Response.StatusCode.value__): $($_.Exception.Response.StatusCode)"
                Write-Error "Error authenticating to `'$Server`' with token"
                break
            }

            $ConnectionDetails = [PSCustomObject][Ordered]@{
                Account = $VerifyAuth.account.username
                Access  = $VerifyAuth.account.accessLevel
                Server  = $Server
                Build   = $JamfBuild
                Expires = $TokenJamfPSPro.expires
            }
            $ConnectionDetails | Format-Table

        } elseif ( $TokenJamfPSPro.token -and ( (Get-Date $TokenJamfPSPro.expires) -le ((Get-Date).AddMinutes(20)) ) ) {
            Write-Debug "Found token with healthy expiry. Expires: $($TokenJamfPSPro.expires)"
            try {
                Write-Debug "Attempting to refresh token"
                $KeepAlive = Invoke-RestMethod $uri_KeepAlive -Authentication Bearer -Token $TokenJamfPSPro.token -ContentType $app_Type -Headers $app_Headers -Method POST
                $TokenJamfPSPro.psobject.Properties.Remove('token')
                $TokenJamfPSPro.psobject.Properties.Add([PSNoteProperty]::new('token', [ConvertToSS]::Set($KeepAlive.Token)))
                $TokenJamfPSPro.expires = (Get-Date $KeepAlive.expires).AddMinutes((Get-TimeZone).BaseUtcOffset.TotalMinutes)
                Write-Debug "New token expires: $($TokenJamfPSPro.expires)"
            } catch {
                Write-Debug "Error refreshing token"
                if ( $TokenJamfPSPro.Server -and $TokenJamfPSPro.Credential ) {
                    $TokenJamfPSPro.psobject.Properties.Remove('expires')
                    $TokenJamfPSPro.psobject.Properties.Remove('token')
                    Write-Debug "Cleared expiry and token"
                    Connect-JamfPro -Server $TokenJamfPSPro.Server -Credential $TokenJamfPSPro.credential
                } else {
                    Write-Debug "No stored credentials"
                    Clear-Variable -Name TokenJamfPSPro -Force
                    Connect-JamfPro
                }
            }
        }
    }

}