Guidelines for installer types

A review of a few types of condition scripts you can use for your installers. Every app has its needs, and you can meet those needs elegantly.

Guidelines for installer types

While managing your fleet, you're going to be deploying software packages. Assuming your Mac apps aren't all sources from the App Store, this means you'll be pushing apps out via pkg and dmg. No matter what method you use, or the MDM platform you use (sorry, Apple Business Essentials), you'll be given the opportunity to set your own condition scripts for these installers, and how you set these conditions up can vastly change their utility.

We're going to look at a few different types of installation conditions you can use, and when they're going to be useful. None of these are better or worse than any others, they all have a time and place where they'll shine.

Basic Installer

#### APP VERSION CHECK ####
# Retrieve version number
INSTALLED_VERSION=$(defaults read "$APP/Contents/Info.plist" CFBundleShortVersionString)
# Correct version number if null return with second type of scrape
if [ "$INSTALLED_VERSION" = "" ]; then
  INSTALLED_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$APP/Contents/Info.plist";)
fi

echo "Current Version: ${INSTALLED_VERSION}"

if is_at_least "$INSTALLED_VERSION" "$TARGET_VERSION"; then
  echo "Current version $INSTALLED_VERSION is the same or newer than $TARGET_VERSION"
  echo "No action required - halting."
  exit 1
else
  echo "Installed version $INSTALLED_VERSION is older than ${TARGET_VERSION}."
    exit 0
fi
#### END VERSION CHECK ####
Basic Installer condition - version comparison

Your bread and butter installer. Your condition script will check if the app is installed and the currently installed version. Should either check fail (app not present or present and outdated), the app installs.

One of the most common variations for a Basic Installer will be adding a pgrep check to it - this can allow you to proceed with the installation only if the app is closed. For certain types of apps, like, cloud storage or font management, these can help prevent crashes or even data loss from your users, so always test your basic installers to see if waiting until the app is closed is the right choice.

Patcher

#### INSTALLED CHECK ####
if [ ! -d "$APP" ]; then
    echo "Does not exist: ${APP} - bypassing"
    exit 1
fi
#### END INSTALLED CHECK ####
Patcher logic example - bailout if the app isn't detected

In some environments, especially larger ones, you may have some optional apps in the wild. If you know you may have some users electing to use Firefox, but don't have it auto-installed for all Macs, you could use a Patcher to keep it updated for users who have it but ignore Macs that don't. A Patcher condition script will check if the app is installed - if the app is not installed, it will immediately bail out. By keeping a Patcher running for any apps that could present security vulnerabilities, you can keep your Macs secure without forcing said app onto every Mac in your fleet. This is particularly helpful for web browsers, where you may have compliance reasons to deploy security updates ASAP while wanting to give your users the freedom to choose their own browser.

Deployer

#### INSTALLED CHECK ####
if [ ! -d "$APP" ]; then
    echo "Does not exist: ${APP} - installing"
    exit 0
     else
    echo "App detected - bailout"
    exit 1
fi
#### END INSTALLED CHECK ####
Deployer logic example - bailout if the app is detected

Deployer scripts are the natural opposite of Patchers. While Patchers will only run on Macs that have a particular app installed, Deployers will only run if the app in question isn't present. My most common use case for this is for Microsoft Office. As discussed in my longer article on managing Microsoft Office, if a user has any Office app open while the installer pkg is running for a new version, it's probable that said app will be rendered unusable. To avoid this, we use a Microsoft Office Deployer script, that only will proceed if none of the suite's applications are running. This way, when we update our Deployer to a new version, it doesn't run on every Mac in the fleet. We mitigate risk while ensuring newly-enrolled devices benefit from having the newest version installed by default.

Self Service

Self Service installers can be built for a number of different needs

Many MDMs provide a self-service app of some kind for users to seek out and install apps without administrative oversight. Depending on the application in question, you can build a Self Service installer that includes different qualities than your other installers. Perhaps this version of the installer includes GUI feedback to the user, like telling them they need to quit a certain app to proceed. Or it could be a "force installer" that will run the installation pkg even if the app detected on the Mac is already the same version as the installer.

Force Installer

# Attach
hdiutil attach -nobrowse "/Library/Addigy/ansible/packages/$cs/$dmgName"
# Install
installer -pkg "/Volumes/$volumeName/$pkgName" -target /
# Detach
sudo hdiutil detach "/Volumes/$volumeName/"
Force Installer - no condition, just execute

Finally, there's the Force Installer - a conditionless script that just proceeds when triggered. While very basic, these can be simultaneous very powerful and very dangerous. These can be really helpful as Self Service installers, or as items available to your support desk technicians to deploy on supported devices, but be very careful about scoping these to auto-run on your fleet.


Review the apps that your team uses to get their work done - inevitably you'll find some software that fits the bill for each of the above types of installers, many may benefit from having multiple different ones in production. Build code snippets for each of these so you can reproduce and customize them quickly to keep your team secure and standardized.