This question seems to come up from time to time in the #Mosyle Mac Admins Slack channel, so I thought I would take a stab at describing the issue and providing my solution.
Mosyle, like all MDMs, has its strengths and weaknesses. I’ve used a handful of MDMs to manage client systems, but by far Mosyle is the one I’m most fluent with and the one I’ve been recommending to clients to manage their Apple devices for a while now.
As I’m beginning to develop this blog, Mosyle will surely be a recurring topic since I spend so much time in it, but I try my best to create processes and workflows that are platform agnostic and instead lean on built-in macOS functionality which will do what it needs to do regardless of what mechanism is delivering it.
Basic PKG Handling in Mosyle
Mosyle allows you to define PKGs hosted on their CDN (or any publicly available link) and then install those PKGs using a Mosyle Profile (this is not an MDM protocol profile, but it does utilize MDM features to determine what apps are installed on a macOS device and whether they need to be deployed or re-installed.)
When you define the PKG in Mosyle, one of the anchors of that PKG is the App Bundle. This App Bundle is what Mosyle uses to determine whether an application is installed or if it’s been removed from the system.
The App Bundle is the identifier of the app and is what is used in order for Mosyle to determine the installation status of the app. If the bundle identifier is incorrect, Mosyle will not be able verify if the app is installed, and the installation status may be incorrect. This can also result in multiple installation commands to be sent to the device.
To find the bundle identifier for an app, you can run the following command using the Terminal application: osascript -e ‘id of app “App Name”‘Mosyle Tip found at Install PKG > PKG > Edit PKG > App Bundle info button
Packages don’t always have App Bundles inside them. Mosyle is using the App Bundle name to track Package installation but this is fundamentally problematic because there is no requirement that a package contain or deliver an app.
Packages solve a fundamental macOS problem: “How do I deliver X files, with Y permissions, to Z directory, and verify they got there?” Application installation is the most common use case you will come across as a macOS user, but it’s far from the only reason to use a package. In the past few months, I’ve used PKG files to:
- Install Scripts
- Deliver branded assets (icons, logos, PDFs)
- Modify LaunchAgents for 3rd party apps
- Created an empty PKG file with a spoofed app bundle ID to use with a Mosyle “Blocked Apps” profile.
If you have a package that doesn’t include an app bundle, and you want to verify whether or not Mosyle installed it successfully, you cannot rely on the “Install PKG” profile feature. The details regarding whether the package was installed will be unreliable, and if you chose to “Reinstall apps that have been removed” then your package may run over and over each time your devices check in.
As with many macOS management questions, my preferred solution when my MDM just isn’t cutting it is to take matters into my own hands and write a shell script. MDM is fantastic but in my (never humble enough) opinion, knowing how to write, modify, or use a script is still a fundamental skill for a Mac Admin.
Scripting often seems to be the way around an MDM platform’s limitations, but I may feel this way because, hey, when your favorite tool is a hammer everything looks like a nail, right?
One of my goals with this blog will be to provide bit sized Tips & Tricks for shell scripts that I’ve learned over the years, share details about my projects on Second Son’s public github, and get into details on how/why I built a project/script. If that sort of thing interests you, stick around, and please give me feedback on what you like and don’t like or what you might like to see.
I’ve made my PKG installer script public and you can find it here: https://github.com/SecondSonConsulting/macOS-Scripts/blob/main/installGenericPKG.sh
This is a script I wrote to solve the problem described above, while maintaining the reporting and security features Mosyle provides with their “Install PKG” profile.
The script can be installed locally to your machine and called with arguments, or it can be copy/pasted directly into a Custom Command with the package information embedded directly in the script itself. (See the variables in the “User Configuration” section if you want to use this method.)
More details on how it works are written in the comments of the script itself.
While the script only requires 1 argument/option (the path to the PKG you want to install) I would strongly recommended using one or both of the additional two options: MD5 or TeamID verification.
An MD5 hash is a unique string of 32 characters generated cryptographically from a file. It’s a way to make sure that the file the script is looking at has is made up of the exact same bits as the file you actually want installed.
You can get the MD5 hash of a package (or any other file) by opening Terminal.app and following this example:
md5 -q /path/to/file.pkg
The -q flag in the example above just makes sure we don’t get any information back from the command aside from the hash value we’re looking for.
The MD5 value can also be found on the Mosyle CDN details page of any file you host there, and this is the same method used by the “Validate file integrity” option when defining a PKG in Mosyle. Any time you upload something to the CDN it is a good idea to check that Mosyle has the same MD5 value as the file that you uploaded from your workstation.
A TeamID is an identifier provided by Apple to individuals or organizations that are part of the Apple Developer program. If the .pkg you are installing is signed by a valid developer certificate, then the TeamID is “baked in” cryptographically into that package file. In theory, this can’t be faked.
Using TeamID verification with the installGenericPKG.sh script will be handy if you’re installing signed packages that might be updated from time to time. MD5 hashes will change any time the contents of the package changes, but the TeamID will stay constant so long as the package is signed by the same developer.
I recently completed enrollment process into the Apple Developer Program for my organization, and was considering writing up the experience and gotchas in another post. Let me know if you think you would find that helpful.
To obtain the TeamID used to sign a package, follow the example below:
spctl -a -vv -t install "/path/to/installer.pkg"
Go Ahead and Use Both
Confirming the TeamID used to sign a package will validate the authenticity of the source of the package while MD5 validates the integrity of the contents of a package. You can use either or both with my script. You can define them within the script itself or provide them as an argument from the command line.
The path to the package must be the first argument, but the TeamID or MD5 can be the 2nd or 3rd argument interchangeably.
If you’d like an explanation of how I handled the logic to allow either or both of these options in a script, let me know and I can write it up as a short blog post.Have you noticed yet that I’m hoping for some feedback as to what people might actually want to read? Thanks!
# Example: Run a PKG from the local disk and verify by TeamID and MD5 ./installGenericPKG.sh /path/to/installer.pkg 7Q6XP5698G 9741c346eeasdf31163e13b9db1241b3
The output of the installGenericPKG.sh script is brief but to the point. It includes:
- The name of the installer script
- You can rename it to something that suits a dedicated purpose)
- In this example, I renamed my script “installEmptyPKG.sh”
- The version of the script
- I keep finding ways to make it just a little bit better, so check back every so often for updates
- The date/time the script was run
- The MD5/TeamID details (or a warning if you choose to use neither)
installEmptyPKG.sh - v2.2 Tue Nov 29 22:20:35 PST 2022: Location: /Users/Shared/EmptyPKG.pkg Location Type: filepath Expected MD5 is: 301111e8e35045b67b494bc0019e2ea8 Expected TeamID is: Installation successful
Phew, this was a long one…
I’ll close this out by mentioning that one feature the “Install PKG” profile gives that isn’t immediately solved by this script is the “reinstall if removed” option. You may be able to achieve this functionality by getting creative with Criteria Groups or locally installed scripts that look for things and correct them.
I’m interested to hear your ideas on how you might accomplish this.
Thanks for reading, if you made it this far, and I hope you come back for more write ups like this one as well as smaller Tips & Tricks posts.