Jamf Deployment Guide#

This guide covers adding patch management/software titles, setting up an API Role & Client in Jamf Pro, and proper management of access tokens.

Adding Software Titles#

When utilizing the Patcher tool with Jamf Pro, it’s important to understand that the tool exclusively pulls data from configured patch management titles. As it relies on the Jamf Pro API, patch data of software titles not available in the Patch Management Software Titles list in Jamf App Catalog or Title Editor will not be exported. Therefore, ensure that all necessary software titles are properly configured within Jamf Pro to ensure accurate and comprehensive patch management.

Refer to Configuring a Patch Management Software Title in the Jamf Pro Documentation for instructions on setting up software titles for patch management purposes.

Creating an API Role & Client#

To utilize Patcher with Jamf Pro, you need to create an API Role and Client first. Reference the Jamf Pro Documentation or follow the steps below.

API Role & Privileges#

Before creating an API client, you must establish an API role with the necessary privileges:

  1. Navigate to Settings in the Jamf Pro sidebar.

  2. Under System, select API Roles and Clients.

  3. Switch to the API Roles tab.

  4. Click New to create a new role.

  5. Provide a meaningful display name for the API role (e.g., “Patcher-Roles”).

  6. In the Jamf Pro API Role privileges field, type the following privileges required for use with Patcher

    • Read Patch Management Software Titles

    • Read Patch Policies

    • Read Mobile Devices

    • Read Mobile Device Inventory Collection

    • Read Mobile Device Applications

    • Read API Integrations

    • Read API Roles

    • Read Patch Management Settings

    • Update API Integrations

  7. Click Save to create the role.

API Client#

Once your API role is ready, proceed to create an API client:

  1. Follow steps 1-2 from above to navigate back to the API Roles and Clients section if not already there.

  2. Click on the API Clients tab.

  3. Select New to initiate a new API client creation.

  4. Assign a clear and descriptive display name for the API client (e.g., “Patcher-Client”).

  5. In the API Roles field, assign the previously created API role to this client.

  6. Define the Access Token Lifetime. This defines how long each token remains valid. See Access Token Lifetime below for more information.

  7. Enable the API client by clicking Enable API Client.

  8. Click Save.

  9. Record the Client ID value for safe-keeping.

Generating a Client Secret#

Important

Record the generated client secret immediately and securely as it is shown only once.

  1. Within Jamf Pro, go to the previously created API client’s details page.

  2. Click Generate Client Secret to create a new secret.

  3. Confirm the action in the dialog box by selecting Create Secret.

  4. The client secret will now be displayed.

You can now pass the client ID and client secret values when prompted by the setup assistant. The Jamf URL, client ID, client secret, and access token are all saved to keychain and can be modified if necessary.

Access Token Lifetime#

Note

Generating an access token is not required. Patcher handles obtaining and refreshing of tokens automatically. See TokenManager for more.

When defining the access token lifetime, it is recommended to use a duration of at least 5 minutes. Patcher is designed to handle automatic token refreshing and generation. Longer durations reduce the frequency of token regeneration, thereby decreasing administrative overhead. However, ensure that the chosen duration aligns with your organization’s security policies.

Token Generation#

In situations where AccessTokens need to be generated manually, copy the bash script below into the code editor of your choice. Substitute your Jamf Pro URL in the url variable, and modify the client_id and client_secret values with the Client ID and secret generated from the steps above.

#!/bin/bash

url="https://yourserver.jamfcloud.com"
client_id="your-jamf-api-client-id"
client_secret="your-jamf-api-client-secret"

response=$(curl --silent --location --request POST "${url}/api/oauth/token" \
 --header "Content-Type: application/x-www-form-urlencoded" \
 --data-urlencode "client_id=${client_id}" \
 --data-urlencode "grant_type=client_credentials" \
 --data-urlencode "client_secret=${client_secret}")
token=$(echo "$response" | plutil -extract access_token raw -)
expires_in=$(echo "$response" | plutil -extract expires_in raw -)

echo "Token: $token"
echo "Expiration: $token"

keychain_service="Patcher"

security add-generic-password -a "TOKEN" -s "$keychain_service" -w "$token" -U
security add-generic-password -a "TOKEN_EXPIRATION" -s "$keychain_service" -w "$expires_in" -U

echo "Token and expiration time have been saved to the Keychain."
import requests
import keyring

# Define the server URL and API credentials
url = "https://yourserver.jamfcloud.com/api/oauth/token"
client_id = "your-jamf-api-client-id"
client_secret = "your-jamf-api-client-secret"

# Prepare the request payload and headers
payload = {
    'client_id': client_id,
    'grant_type': 'client_credentials',
    'client_secret': client_secret
}
headers = {
    'Content-Type': 'application/x-www-form-urlencoded'
}

# Send the POST request to retrieve the token
response = requests.post(url, data=payload, headers=headers)

# Check if the response was successful
if response.status_code != 200:
  # Raise an exception with a custom error message
  raise ValueError(f"Failed to retrieve the token. Status Code: {response.status_code}, Message: {response.text}")

# Extract the token and expiration time from the JSON response
data = response.json()
token = data.get('access_token')
expires_in = data.get('expires_in')

# Output the token and expiration time
print(f"Token: {token}")
print(f"Expiration: {expires_in}")

# Define keychain service name and account
keychain_service = "Patcher"
token_account = "TOKEN"
expiration_account = "TOKEN_EXPIRATION"

# Save the token and expiration time to the keychain
keyring.set_password(keychain_service, token_account, token)
keyring.set_password(keychain_service, expiration_account, str(expires_in))

print("Token and expiration time have been saved to the Keychain.")

If successful, the script(s) above will print the access_token and expires_in values to the console and save them to the login keychain.