JamfPatchUploader: Automate Jamf’s Patch Management with AutoPkg
After switching from munki to Jamf Pro some years ago, I really disliked the process to update and maintain software installations on the clients. The method of creating multiple Smart Groups and Install Policies felt pretty cumbersome (with clever AutoPkg workflows it’s at least tolerable, but still fills up your Jamf instance pretty fast).
Jamf Pro Patch Management⌗
When Jamf introduced a built-in Patch Management I quickly started to take a look and play around with it. The workflow of creating patches was still clunky and needed many clicks (overall a pretty nested GUI experience), but I still preferred it for multiple reasons:
- Built-In workflows (Self Service notification or automatic installation with notification) which create a better user experience. No ‘shadow installations’ executing in the background, which could interfere with the user’s current workflow. Also no need to point users to the Self Service, and “encourage” them to install the newest version by hand ;)
- A better overview of the installed versions on clients which could also be placed on the Jamf dashboard: Jamf lists all installed versions in an easy to read table which can also be filtered, which makes spotting ‘problems’ easier etc.
- Patches are built on top of patch (title) definitions, which list every software version and some more informations about the update like which applications need to quit, or if the update is an incremental one.
- Since patches have their own place in the Web-GUI, Smart Groups and Policies stay more clean and slim. I really value this point, since every Jamf admin has already more than enough policies. With less items, it’s simply easier to keep an overview.
Of course there are also drawbacks:
- Patch Policy are less flexible than normal Policies. For example, it’s not possible to add an post- or preinstall script. It’s only possible to initiate a package installation. If I need complex workflows (including scripts or different targets etc.), I create dummy installers which trigger other policies. Sadly this will be again less clean and create once again multiple policies. But I don’t often encounter this problem.
- Patch Policies depent on ‘Patch Softwaretitle Definitions’. Jamf provides quite a big list, but if you want to patch a software which isn’t on this list, you need to ether find and add an external definition (which maybe doesn’t get updated in time or get’s dropped etc.), or use Jamf’s Title Editor, which results in even more manual work. But maybe this get’s solved with another Post Processor in the future :)
- Like already mentioned, the Patch Management Workflow is also pretty click-heavy in the GUI. But like you maybe already guessed, we are gonna skip this point, and simply automate it with AutoPkg! :)
AutoPkg Custom Processor: JamfPatchUploader⌗
Jamf’s API provides endpoints to link installers to versions and craete patch policies just like normal policies. After discovering this, I HAD to built a custom Processor based on Graham’s great JamfUploader-Base!
Working with the JamfPatchUploader⌗
First you need to create the Patch Softwaretitle in Jamf by Hand. This step only needs to be done one time for each software you want to patch:
Note: Jamf lists some softwaretitles multiple times. Better select the non-legacy one.
2nd Note: Some softwaretitles, like the Mozilla Firefox
one, need additional Extension Attributes to function. You have to accept the EA, otherwise the softwaretitle will not work!
All other steps, can be managed with the Processor:
Process:
- Processor: com.github.grahampugh.jamf-upload.processors/JamfPatchUploader
Arguments:
patch_name: "%PATCH_NAME%"
patch_template: "%PATCH_TEMPLATE%"
patch_softwaretitle: "%PATCH_SOFTWARE_TITLE%"
patch_icon_policy_name: "%POLICY_NAME%"
replace_patch: True
patch_softwaretitle
is the name of the Software Title in your Jamf Instance e.g. Mozilla Firefox
. patch_name
is the name of the patch policy. If no variable is provided, the name defaults to %NAME% - %version%
. You can read more about all variables in the provided README: autopkg/grahampugh-recipes/JamfPatchUploader
Just like the JamfPolicyUploader the JamfPatchUploader uses a xml-Template to define the resulting patch policy in Jamf. It’s important to include <software_title_configuration_id>%patch_softwaretitle_id%</software_title_configuration_id>
and <id>%patch_icon_id%</id>
. You don’t need to provide these two variables - the Processor will set them - but they need to be in the template. Otherwise Jamf will reject them.
There are two types of patch policy types:
Automatically install Updates⌗
These kind of Updates install the Patch instant, or wait for defined grace period if the application is still running. After the grace period the application gets force closed. Sadly currently the user only get’s a notification when the patch gets detected, but not when the grace period is over. So workflows like ‘Install in the next 4 hours’ feel a bit sluggish:
<?xml version="1.0" encoding="UTF-8"?>
<patch_policy>
<general>
<name>%patch_name%</name>
<enabled>false</enabled>
<target_version>%version%</target_version>
<distribution_method>prompt</distribution_method>
<allow_downgrade>false</allow_downgrade>
<patch_unknown>true</patch_unknown>
</general>
<scope>
<all_computers>true</all_computers>
<computers/>
<computer_groups/>
<users/>
<buildings/>
<departments/>
<limitations>
<network_segments/>
<ibeacons/>
</limitations>
<exclusions>
<computers/>
<computer_groups/>
<users/>
<buildings/>
<departments/>
<network_segments/>
<ibeacons/>
</exclusions>
</scope>
<user_interaction>
<grace_period>
<grace_period_duration>240</grace_period_duration>
<notification_center_subject>Update</notification_center_subject>
<message>$APP_NAMES will be terminated in $DELAY_MINUTES minutes to update $SOFTWARE_TITLE.</message>
</grace_period>
</user_interaction>
<software_title_configuration_id>%patch_softwaretitle_id%</software_title_configuration_id>
</patch_policy>
Notify User about Update⌗
There is also the possibility to trigger a notification, which also get’s displayed in the Self Service application. In this scenario the user can decide when he wants to start the update. Also a deadline can be defined. In my experience this would be the better workflow, but sadly feels a bit buggy (notifications not getting cleared after successful updates etc.):
<?xml version="1.0" encoding="UTF-8"?>
<patch_policy>
<general>
<name>%patch_name%</name>
<enabled>false</enabled>
<target_version>%version%</target_version>
<distribution_method>selfservice</distribution_method>
<allow_downgrade>false</allow_downgrade>
<patch_unknown>true</patch_unknown>
</general>
<scope>
<all_computers>true</all_computers>
<computers/>
<computer_groups/>
<users/>
<buildings/>
<departments/>
<limitations>
<network_segments/>
<ibeacons/>
</limitations>
<exclusions>
<computers/>
<computer_groups/>
<users/>
<buildings/>
<departments/>
<network_segments/>
<ibeacons/>
</exclusions>
</scope>
<user_interaction>
<install_button_text>Update</install_button_text>
<self_service_description/>
<self_service_icon>
<id>%patch_icon_id%</id>
</self_service_icon>
<notifications>
<notification_enabled>true</notification_enabled>
<notification_type>Self Service and Notification Center</notification_type>
<notification_subject>%NAME% update available</notification_subject>
<notification_message/>
<reminders>
<notification_reminders_enabled>true</notification_reminders_enabled>
<notification_reminder_frequency>1</notification_reminder_frequency>
</reminders>
</notifications>
<deadlines>
<deadline_enabled>false</deadline_enabled>
<deadline_period>7</deadline_period>
</deadlines>
</user_interaction>
<software_title_configuration_id>%patch_softwaretitle_id%</software_title_configuration_id>
</patch_policy>
Icons in Self Service patches
Note that including icons in Self Service Updates is a bit tricky. We currently have no reasonable way to upload icons to the patch policies. So I built a workaround, which extracts the icon from an already uploaded non-patch policy. Simply define the policy e.g. patch_icon_policy_name: "%POLICY_NAME%"
and use the following snippet in your template:
<self_service_icon>
<id>%patch_icon_id%</id>
</self_service_icon>
Disabled Patch Policies
Note that I create patch policies disabled, since I often don’t want to distribute the updates instantly. Instead I often create a normal ‘Testing’-Policy which get’s used by specific user groups. After positive reports, I simply enable the already created patch policy via the Web-GUI. Disable replace_patch
in the recipe if you want to follow this workflow ;)
Fully automated AutoPkg recipe⌗
The ‘jack of all trades’ recipe would look like this (Note that we first upload the non-patch policy including the icon, so we can use it later in the patch policy):
Description: 'All-in-One' Mozilla Firefox.
Identifier: com.eisenschmiede.recipes.jamf.MozillaFirefox
ParentRecipe: com.github.autopkg.pkg.FirefoxSignedPkg
MinimumVersion: '2.3'
Input:
NAME: MozillaFirefox
CATEGORY: Internet
POLICY_CATEGORY: '%CATEGORY%'
POLICY_NAME: 'Install_%NAME%'
POLICY_REPLACE: 'True'
POLICY_TEMPLATE: 'Policy_SelfService_AllDevices.xml'
POLICY_SELFSERVICE_NAME: 'Mozilla Firefox'
POLICY_SELFSERVICE_DESC: 'A webbrowser made by Mozilla.'
PATCH_TEMPLATE: 'Patch_SelfService_AllDevices.xml'
DOCK_ITEM_NAME: '%NAME%'
DOCK_ITEM_TYPE: 'App'
DOCK_ITEM_PATH: 'file:///Applications/Firefox.app/'
Process:
- Processor: com.github.grahampugh.jamf-upload.processors/JamfDockItemUploader
Arguments:
dock_item_name: '%DOCK_ITEM_NAME%'
dock_item_type: '%DOCK_ITEM_TYPE%'
dock_item_path: '%DOCK_ITEM_PATH%'
replace_dock_item: True
- Processor: com.github.grahampugh.jamf-upload.processors/JamfCategoryUploader
Arguments:
category_name: '%CATEGORY%'
- Processor: com.github.grahampugh.jamf-upload.processors/JamfPackageUploader
Arguments:
pkg_category: '%CATEGORY%'
- Processor: com.github.grahampugh.jamf-upload.processors/JamfPolicyUploader
Arguments:
policy_name: '%POLICY_NAME%'
policy_template: '%POLICY_TEMPLATE%'
replace_policy: '%POLICY_REPLACE%'
icon: '%NAME%.png'
- Processor: com.github.grahampugh.jamf-upload.processors/JamfPatchUploader
Arguments:
patch_softwaretitle: "Mozilla Firefox"
patch_template: "%PATCH_TEMPLATE%"
patch_icon_policy_name: "%POLICY_NAME%"
replace_patch: True
The package got linked to the version in the Patch Softwaretitle
The patch policy get’s created and stays disabled, just like we defined it in the template
Notifications about created patches⌗
Besides creating a wonderful base to create different kinds of processor, Graham Pugh also maintains a JamfUploaderTeamsNotifier and JamfUploaderSlacker which just got an update to include information from the JamfPatchUploader. Besides that the Processor of course also outputs a summary as all the other processors:
The following patch policies were created or updated in Jamf Pro:
Patch Id Patch Policy Name Patch Softwaretitle Patch Version
-------- ----------------- ------------------- -------------
10 Mozilla Firefox - 97.0.1 Mozilla Firefox 97.0.1
I hope some people find this workflow as useful as I do :) If you have any questions, drop me a message via Twitter or on the macAdmins Slack Channel!