pkginfo files can contain information not only on how to install a package, but also how to uninstall it. When an application is installed on a client, the Managed Software Center can therefore offer a
There are four main types of uninstaller action built in to Munki:
- Deletion of applications that live entirely within
- Running a vendor-supplied uninstaller application, binary or script.
- Removal of packages using information in the package receipts.
- A self-crafted uninstaller script.
A high proportion of applications can be uninstalled using options 1 or 3.
Jamf has only a limited offering when it comes to application removal. You have two choices:
- Manually “index” a snapshotted package using the
Jamf AdminmacOS application. Indexed policies gain a “Remove” button. This is not an option at all if you are using AutoPkg to automate the production of installer packages.
- Create a separate policy for uninstalling a policy. This must be manually crafted (normally using a script or an uninstaller package).
You cannot really replicate the “Remove” button experience of Munki with option 2, but at least you can create Uninstaller policies, scoped only to clients with those applications installed.
1. Jamf Pro: Deletion of applications that live entirely within the Applications folder
It’s easy to script this, so you can easily create a template script that takes care of Uninstaller policies for App Store apps and similarly contained applications. Here’s a quick and very basic example:
2. Jamf Pro: Running a vendor-supplied uninstaller application, binary or script
Similarly, it’s easy to write a script that will run a vendor’s uninstaller that is already on the client drive. I don’t even need to provide an example here, which is good as I don’t have one yet.
3. Jamf Pro: Removal of packages using information in the package receipts
This is tougher. You have to analyse the package receipts for the installed files, then pass that information to a script that deletes them all. Then delete the package receipt. You can get a list of files that were installed using a particular package using the following command:
pkgutil --files co.pretend.ShinyApplication
Perhaps you could use this list and pipe it to an
rm command. But of course, every package includes folders right up to the root folder, e.g.:
$ pkgutil --files jp.co.canon.CUPSPS_M.icons.SF-04.pkg Library Library/Printers Library/Printers/Canon Library/Printers/Canon/CUPSPS2 Library/Printers/Canon/CUPSPS2/Icons Library/Printers/Canon/CUPSPS2/Icons/SF-04 Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle/Contents Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle/Contents/Info.plist ...
So you can’t use
rm -rf to delete folders and their contents, as that could get very messy. You need to iterate through the list properly so that you only remove files and empty folders.
This logic has been built into the Munki client, and it provides a command-line Python script called
removepackages to perform the removal, based on a package ID:
$ sudo /usr/local/munki/removepackages -l jp.co.canon.CUPSPS_M.icons.SF-04.pkg Password: Gathering information on installed packages... 0..20..40..60..80..100 0..20..40..60..80..100 Determining which filesystem items to remove... /Library/Printers/Canon/CUPSPS2/Icons/SF-04 /Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle /Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle/Contents /Library/Printers/Canon/CUPSPS2/Icons/SF-04.bundle/Contents/Info.plist ...
Wouldn’t it be great if Jamf Pro could utilise this tool to make package removal simple, so we don’t have to write a new one?
Well, it can!
Jamf And… a bit of Munki: Using removepackages with Jamf Pro
removepackages is not a self-contained script. It imports various modules from Munki’s library of python functions. So to utilise
removepackages on a client we need to install Munki on the client.
…but not all of Munki. We only need the
core Munki tools.
Installing the core munkitools
You can download Munki as a single package, but then we would have to install the entire suite of Munki tools, the Managed Software Center, and the LaunchDaemon, which requires a reboot. We don’t want most of this stuff. Additionally, of course, we want to get the package into the JSS from AutoPkg.
I only found a
munkitools3.munki AutoPkg recipe - no
.pkg recipe. The
.munki recipe is useful to us in some ways, in that it downloads the munkitools package and repackages it into five components,
launchd. But the recipe cannot be run by Jamf Pro admins, however, as it will fail, since no Munki repository is configured. And we only want the
Fortunately it was easy to edit the existing
.munki recipe to create a
MunkiToolsCore.pkg recipe and associated
MunkiToolsCore.jss recipe to import the package into the JSS using JSSImporter. I’ve made them available on my GitHub (github.com/grahampugh/grahampugh-autopkg-recipes), but feel free to put in your own repo.
You can scope this policy to everyone, otherwise be sure to create a smart group of clients that have it installed.
What gets installed?
core package installs just the following:
/Library/Managed Installs /Library/Managed Installs/Cache /Library/Managed Installs/catalogs /Library/Managed Installs/manifests /usr/local/munki/authrestartd /usr/local/munki/launchapp /usr/local/munki/logouthelper /usr/local/munki/managedsoftwareupdate /usr/local/munki/munkilib/* /usr/local/munki/ptyexec /usr/local/munki/removepackages /usr/local/munki/supervisor
I think we only actually need the
/Library/Managed Installs folder, where a small sqlite database of package receipt info is maintained, and
/usr/local/munki/munkilib/*, but I haven’t bothered trying to re-script the recipes to trim it down further, as it is lightweight and innocuous enough as it is.
Creating a policy to uninstall a packaged application
You can add a template script to a policy which has the package ID as a parameter. If a policy installs multiple packages, just add more package IDs to the next parameters. Here’s an example I made earlier:
This script kills the app before attempting to remove it, so I added another parameter for the app name:
Now, Munki has a record of every package installed , thanks to the
makepkginfo tool. Jamf Pro does not have that information, so you have to look up the package ID using:
It may not always be obvious what the package ID is (though it normally is). If in doubt, you could look at a Munki recipe…
As with Munki,
removepackages is not suitable for all apps. There could be unintended consequences, where files required by another app get removed. So TEST!
It’s worth looking in an equivalent
pkginfo file for the following keys - if you see these, you should be good to go (but, still, TEST!):
<key>uninstallable</key> <true/> <key>uninstall_method</key> <string>removepackages</string>
If your uninstall policy is more complicated and requires the removal of more than 7 packages, or multiple applications even, then you will either need to duplicate the script so that you have more parameters to play with, or just adapt the script for that particular app and hard-code the package IDs into it.
If all else fails, you can of course fall back on writing a dedicated script to remove that particular application. You may even find such uninstaller scripts already written in existing
.munki recipes. It’s always worth a look.
If any of you try this out, please let me know how you get on! Or, if you think it’s a crazy idea with gaping pitfalls, also let me know! I only thought of the idea a few hours ago but it seems to work so far (famous last words!).