Jamf Pro API – Keep Chrome Updated

Maintaining updates may not be the most fun part of administering computers, but it is necessary. Updates bring new features to end-users and fixes for vulnerabilities. For this post, I will be focusing on the Google Chrome app as it is updated quite frequently. I will show how we can use Jamf Pro patch management, smart group, policies, and Installomator to keep Google Chrome updated for our users automatically. Then we can see how to implement this using the Jamf Pro API.

The first thing we need to do is make sure we have Patch Management in Jamf Pro enabled so we can report on the latest version available. Jamf updates the definitions for the software titles listed in the patch management module. For us, that’s great, we don’t need to worry about updating this ourselves whenever Google provides an update. In Jamf Pro, navigate to Patch Management > + New > find and choose Google Chrome.

Next we need to create a Smart Group to identify computers that need an update. Navigate to Smart Computer Groups > +New and give the Smart Group a name. For the criteria of this group, we are looking for ‘Patch Reporting Software Title’. If you are unable to see this in the pre-populated list, you may need to choose ‘Show Advanced Criteria’ in the top right corner of the screen. Select ‘Patch Reporting: Google Chrome’, and for the value, click the ‘…’ to see a list of available versions. Choose the highest available version. This will be listed directly under the ‘Latest Version’ option, then save.

Lastly, we need to create a policy that will update Google Chrome. Navigate to Policies > +New. Configure the policy as:
-Display Name: Application – Google Chrome – Auto Update
-Enabled: True
-Trigger: Recurring Check-in
-Execution Frequency: Ongoing
– Add the Scripts payload, configure to use the Installomator script. If you haven’t already uploaded this script to your Jamf Pro, you can find the script here: https://github.com/Installomator/Installomator
– In the Scripts payload, use the app label for parameter 4. In this case, the app label will be: googlechromepkg
– Configure the Maintenance payload to update inventory. This will ensure that after the policy updates Google Chrome the computer’s inventory will be updated and the computer will no longer be a part of the Smart Computer Group we created earlier.
– Configure the scope of the policy to target the Smart Computer Group created earlier.

Now, to truly make this an automated process, we need to head back to our Smart Computer Group and adjust the criteria. Jamf Pro currently will not allow us to scope a policy to a group using the ‘Latest Version’ criteria, but we can scope our policy to our group the way it is configured now, then change it to use the Latest Version criteria and Jamf Pro works fine. This just adds a step to our flow to make sure we are setting this up to automatically work for us. So, let’s go back to the group, change the value of our criteria to use ‘Latest Version’, then save.

We now have a flow in Jamf Pro that will automatically add computers to our group based on Patch Management definitions, then run a policy to update Google Chrome, and remove the computer from scope of the policy. We never have to package or edit our policies ever again. It is a set it – forget it type of scenario now.

This process of setting up the flow isn’t hard, but what if we could use the Jamf Pro API to do it for us? Well, we can! Check out the script I made below that sets up our flow for us using the API (https://github.com/robjschroeder/Jamf-API-Scripts/blob/main/api-CreateAutomatedGoogleChromeUpdates.sh):

#!/bin/bash

# This script will help in the automatic updating of software titles using 
# Jamf Pro's built in Patch Management module, Smart Computer Groups, and policies. 
#
# The policy used to update uses the Installomator.sh script, this should already be uploaded into your
# Jamf Pro before running this script. 
#
# This script will:
# 1. Create a Patch Management Software Title for Google Chrome
# 2. Create a Smart Computer Group that looks for computers with a less than "x" verison
# 3. Create an ongoing and recurring check-in policy that uses the Installomator.sh script to install Google Chrome, scoped to the Smart Computer Group created in Step 2. 
# 4. Updates the Smart Group created in step 2 to use 'Latest Version' as the criteria. 
# 5. There is no step 5....
#
# Created 07.06.2022 @robjschroeder

##################################################
# Variables -- edit as needed

# Jamf Pro API Credentials
jamfProAPIUsername="apiUsername"
jamfProAPIPassword="apiPassword"
jamfProURL="https://server.jamfcloud.com"

# Token declarations
token=""
tokenExpirationEpoch="0"

# Array of Patch MGMT Software Titles to add to Jamf Pro from GitHub
computerPatchURL=(
	# Google Chrome
	https://raw.githubusercontent.com/robjschroeder/JamfPatchSoftwareTitles/main/SoftwareTitle-GoogleChrome.xml
)

# Array of policy URLs to upload to Jamf Pro
computerPoliciesURL=(
	# Applications - Google Chrome - Auto Update
	https://raw.githubusercontent.com/robjschroeder/Jamf-Policies/main/Applications-GoogleChrome-AutoUpdate.xml
)

# Array of computer smart group URLs to upload to Jamf Pro
computerGroupURL=(
	# App Needs Update - Google Chrome
	https://raw.githubusercontent.com/robjschroeder/Jamf-Smart-Groups/main/AppNeedsUpdate-GoogleChrome.xml
)
#
##################################################
# Functions -- do not edit below here

# Get a bearer token for Jamf Pro API Authentication
getBearerToken(){
	# Encode credentials
	encodedCredentials=$( printf "${jamfProAPIUsername}:${jamfProAPIPassword}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )
	authToken=$(/usr/bin/curl -s -H "Authorization: Basic ${encodedCredentials}" "${jamfProURL}"/api/v1/auth/token -X POST)
	token=$(/bin/echo "${authToken}" | /usr/bin/plutil -extract token raw -)
	tokenExpiration=$(/bin/echo "${authToken}" | /usr/bin/plutil -extract expires raw - | /usr/bin/awk -F . '{print $1}')
	tokenExpirationEpoch=$(/bin/date -j -f "%Y-%m-%dT%T" "${tokenExpiration}" +"%s")
}

checkTokenExpiration() {
	nowEpochUTC=$(/bin/date -j -f "%Y-%m-%dT%T" "$(/bin/date -u +"%Y-%m-%dT%T")" +"%s")
	if [[ tokenExpirationEpoch -gt nowEpochUTC ]]
	then
		/bin/echo "Token valid until the following epoch time: " "${tokenExpirationEpoch}"
	else
		/bin/echo "No valid token available, getting new token"
		getBearerToken
	fi
}

# Invalidate the token when done
invalidateToken(){
	responseCode=$(/usr/bin/curl -w "%{http_code}" -H "Authorization: Bearer ${token}" ${jamfProURL}/api/v1/auth/invalidate-token -X POST -s -o /dev/null)
	if [[ ${responseCode} == 204 ]]
	then
		/bin/echo "Token successfully invalidated"
		token=""
		tokenExpirationEpoch="0"
	elif [[ ${responseCode} == 401 ]]
	then
		/bin/echo "Token already invalid"
	else
		/bin/echo "An unknown error occurred invalidating the token"
	fi
}

# Upload computer configuration profiles from Github to Jamf Pro
uploadComputerPatchSoftwareTitles(){
	echo "$(date '+%A %W %Y %X'): Uploading Computer Configuration Profiles from GitHub..."
	for computerPatch in ${computerPatchURL[@]}; do
		computerPatchData=$(curl --silent $computerPatch)
		curl --request POST \
		--url ${jamfProURL}/JSSResource/patchsoftwaretitles/id/0 \
		--header 'Accept: application/xml' \
		--header 'Content-Type: application/xml' \
		--header "Authorization: Bearer ${token}" \
		--data "${computerPatchData}"
		sleep 1
	done
	sleep 1
	echo ""
}

# Upload computer policies from Github to Jamf Pro
uploadComputerPolicies(){
	echo "$(date '+%A %W %Y %X'): Uploading Computer Policies from GitHub..."
	for computerPolicy in "${computerPoliciesURL[@]}"; do
		computerPolicyData=$(curl --silent $computerPolicy)
		curl --request POST \
		--url ${jamfProURL}/JSSResource/policies/id/0 \
		--header 'Accept: application/xml' \
		--header 'Content-Type: application/xml' \
		--header "Authorization: Bearer ${token}" \
		--data "${computerPolicyData}"
		sleep 1
	done
	sleep 1
	echo ""
}

# Upload Computer Smart Groups
uploadComputerGroups(){
	echo "$(date '+%A %W %Y %X'): Uploading Computer Groups from GitHub..."
	for computerGroup in "${computerGroupURL[@]}"; do
		computerGroupData=$(curl --silent $computerGroup)
		curl --request POST \
		--url ${jamfProURL}/JSSResource/computergroups/id/0 \
		--header 'Accept: application/xml' \
		--header 'Content-Type: application/xml' \
		--header "Authorization: Bearer ${token}" \
		--data "${computerGroupData}"
		sleep 1
	done
	sleep 1
	echo ""
}

updateSG(){
	curl --request PUT \
	--url ${jamfProURL}/JSSResource/computergroups/name/App%20Needs%20Update%20-%20Google%20Chrome \
	--header 'Accept: application/xml' \
	--header 'Content-Type: application/xml' \
	--header "Authorization: Bearer ${token}" \
	--data "<computer_group><criteria><criterion><name>Patch Reporting: Google Chrome</name><priority>0</priority><and_or>and</and_or><search_type>less than</search_type><value>Latest Version</value></criterion></criteria></computer_group>"
}
#
##################################################
# Script Work
#
#
# Calling all functions

checkTokenExpiration
uploadComputerPatchSoftwareTitles 
uploadComputerGroups 
# Sleep for 30 seconds, because the policy may not be able to match the scope to the Smart Group that was just created
sleep 30
uploadComputerPolicies 
updateSG
invalidateToken

exit 0

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: