Last post I detailed the first steps taken by PatchBot, building and uploading a new version of an application package.
This post I will explain the next step, updating the testing patch policy.
First thing I should explain is why we don’t do this when we build and upload the package. It boils down to the reliability of our patch definition feed. If every time a new version was available the patch definition feed was updated at exactly the same time we could have done it all in
JPCImporter. Unfortunately the patch definitions are only updated every 12 hours (I think) and that’s enough of a window for Murphy. Kinobi keep on decreasing the window but no matter how narrow you know Murphy will have his way, so defensive design and coding.
Having a separate custom processor is also appealing for development reasons and to allow others to use
JPCImporter on it’s own.
We will also need a recipe for our custom processor. Here’s one example, pretty simple.
title field is the name of the application used in the package name and the
patch field is the display name of the title in the “Patch Management” section of Jamf Pro. These are often the same but not always and as the default display name is provided as part of the patch definition feed I don’t want to change it and possibly cause confusion.
So now we introduce our second AutoPkg custom processor,
PatchManager.py. It goes in to exactly the same folder as
Once again the first 25 lines are housekeeping. This time we have two classes, the first,
Package, is merely a convenience to hold all the information on a particular package. The
patch come from the AutoPkg recipe and the
pkg_id come from the JP server.
The second class,
PatchManager, does the actual work. Once again we open by setting up the logging and our input and output variables before our first function,
policy. This was copied almost verbatim from an earlier version of the system and every time I look at it I think how much nicer it would look if I grabbed the JSON from the Jamf Pro P API instead of the XML but I’ve stuck with “if it ain’t broke don’t fix it”. Generally, if my code is both reading and writing from the Classic API, which only accepts XML, I’ll use the XML and ElementTree module throughout. It’s only when I’m writing something that purely extracts data from the API will I use the JSON. It might look a little untidier and be just that little bit harder to debug but you get used to it.
The next function,
patch, does all the work.
First off we would like to grab the
patchsoftwaretitle record for our title, but for reasons unknown the
patchsoftwaretitle endpoint does not allow you to specify one by name, only ID. Instead we get the entire list and search it for our title before asking for that title by ID.
Now we need to find the version definition in the patch software title. If we don’t find a version definition to match our latest package our patch feed is slow to update, we give up and try again tomorrow, raising an error as we go so that a note gets added to our Autopkg report and a message goes into Teams. If we do find one we update it to point to our package.
Next step is to update out Test patch policy to use the new version. We also set the self service description of the policy to “Update <title> (<date>)” where date is today’s date in ISO 8601 format, e.g. 2020-06-29. That’s crucial as it sets the clock for the delay before we move the package from test into production.
We need to add a few lines to the
autopkg.sh script to run it.
# run the patch management /usr/local/bin/autopkg run --recipe-list=/Users/"$(whoami)"/Documents/autopkg_bits/patch.txt \ --report-plist=/Users/"$(whoami)"/Documents/patch.plist \ -k FAIL_RECIPES_WITHOUT_TRUST_INFO=yes # messages to MS Teams /Users/"$(whoami)"/Documents/autopkg_bits/PatchTeams.py \ /Users/"$(whoami)"/Documents/patch.plist
You can see the similarity with the first block. Once again we are using a text list of recipes to feed AutoPkg, once again we are calling a script to send messages to Teams.
We will also need to write all those recipes and create recipe overrides for them. Our override directory is getting mighty full.
Next post we will look at the last custom processor and moving our patch into production.