Managing Microsoft Office in Addigy

Managing the app lifecycle for Microsoft Office can be more sensitive than it looks at first glance. Optimize your deployment and update workflow in Addigy to keep your fleet up-to-date invisibly.

Managing Microsoft Office in Addigy

The app deployment lifecycle is a core element of managing any Apple fleet. How you handle initial installation, patching, and uninstallation can be elegant and invisible, or it can be a source of regular friction between you and your users.

We were presented with a unique challenge - our users were reaching out to the support desk with Microsoft Office apps that were unable to open. After some research, we found two root causes:  how Addigy's public software items for Microsoft Office are triggered, and how those update packages are published by Microsoft.

KEY TAKEAWAYS

  • Running the Microsoft Office suite installer package while a Microsoft Office app is running can render those apps unusable
  • Installing the Office suite on devices via a "deployer" custom software eliminates this risk
  • Managing software updates via Microsoft AutoUpdate (MAU) allows you to leverage some interesting and useful controls to offer your users a smoother experience
  • Using MAU makes you forfeit the ability to vet updates before deploying them, while releasing you from the need to track and deploy updates manually via the Addigy interface

THE PROBLEM

Our initial deployment workflow for Microsoft Office was a very simple one:

  1. Automatic installation of the app via Addigy's public software item
  2. On-demand installation of the app via a custom software installer that includes a GUI for users to see how the process is going
  3. Updates processed via Addigy's Microsoft Office Updates functionality, always using the "Ignore update if application is in use" option

I would add the new MS updates in Addigy's interface and have them begin auto-installing on client Macs. Two weeks into the month, I would update the automatic installer package from the public software library to the newest version. This ensured that most Macs would update their apps one at a time completely silently, and if users never closed their apps, they'd get enforced updates only rarely. Most of our users close their apps every now and then, which gives the policier (Addigy's tool used for these sorts of installations) time to run the app-specific updates prior to our scoping the newest full installer package.

The scary part of this is that when the public software item runs, if any Microsoft Office app is open during installation, that app will be corrupted and rendered unusable until it is reinstalled. Since the policier runs on its own schedule, we can't predict exactly when the new public software item will execute, leading to users losing access to one or more Office apps. If we try to resolve this by only deploying really old versions of the public software installer, that increases setup time for newly enrolled devices by forcing them to download out-of-date software, and then run a series of updates on those outdated apps before they're fully usable.

THE SOLUTION

Moving forward, we're amending our workflow in a few ways:

  1. Automatic installation of the app via a custom software item I'm dubbing a "Deployer"
  2. On-demand installation of the app via a custom software installer that includes a GUI for users to see how the process is going
  3. Updates processed via Microsoft AutoUpdate, configured with an MDM Profile

DEPLOYERS

The first thing we had to do was move away from reliance on Addigy's public software installer - allowing it to run on any enrolled Macs that already had Office installed was too great a risk to the user experience and productivity. Instead, I created a simple custom installer that will only trigger if Microsoft Office isn't already installed.

First, we run a basic OS check to ensure that the host operating system is supported by Microsoft. In the past, MS has boggled this check and permitted the installation of unsupported apps on older operating systems, rendering the suite inoperable. Defining it ourselves using their n-2 cadence (support for the current macOS and two before it) is a simple safety measure to implement.

#!/bin/zsh
# Deployer - Microsoft Office
# Condition for deployer scripts - will only run if app is not installed (will not update)

############ VARIABLES ############
###################################
targetOS="10.15.0" #Minimum compatible macOS version
############ /VARIABLES ###########
###################################

#### OS CHECK ####
currentOS=$(sw_vers -productVersion)
# Usage: Is $1 at least $2
is_at_least () {
    if [[ $1 == $2 ]]; then
        return 0
    fi
    local IFS=.
    local i ver1=($1) ver2=($2)
    for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do
        ver1[i]=0
    done
    for ((i=0; i<${#ver1[@]}; i++)); do
        if [[ -z ${ver2[i]} ]]; then
            ver2[i]=0
        fi
        if ((10#${ver1[i]} > 10#${ver2[i]})); then
            return 0
        fi
        if ((10#${ver1[i]} < 10#${ver2[i]})); then
            return 1
        fi
    done
    return 0
}

if is_at_least $(/usr/bin/sw_vers -productVersion) "$targetOS"; then
  echo "Current OS $currentOS meets or exceeds requirement of $targetOS"
else
    echo "Current OS $currentOS does not meet requirement of $targetOS. Halting."
    exit 1
fi
#### END OF OS CHECK ####
Condition - minimum OS check

Next, we check for Outlook- if it's not present, we can assume the rest of the apps aren't, or you can iterate through the list of apps if you want to be really safe.

# Check if apps already installed
APP="/Applications/Microsoft Outlook.app"
#### INSTALLED CHECK ####
if [ ! -d "$APP" ]; then
    echo "Does not exist: ${APP} - installing"
    exit 0
    else
        echo "Outlook already installed - halting."
        exit 1
fi
#### END INSTALLED CHECK ####
Condition - is Outlook installed?

Once the condition is cleared, the installer runs. In order to make this installer shareable to other Addigy users or child instances, I leverage Addigy's uploaded pkg installer, so I don't have to upload the pkg myself multiple times. Details on that process in a future post.

#!/bin/zsh
# Deployer script - Microsoft Office via Addigy's Public Software uploads
# Ross Matsuda - Ntiva - May 2022

############ VARIABLES ############
###################################
customSoftwareName="Microsoft Office - Deployer (16.60)" #Copy from Addigy
pkgName="MicrosoftOffice2019-16.60.pkg" # Package to install - file upload April 2022
# Note - after customizing these, the only other thing to do is update the md5 curl.

###################################
######## COMPUTED VARIABLES #######
packagePath="/Library/Addigy/ansible/packages/$customSoftwareName"

# Curl Addigy's MSO installer
set -e
echo 'Ensuring /Library/Addigy/ansible/ dir exists'
mkdir -p /Library/Addigy/ansible/
echo 'Ensuring /Library/Addigy/ansible/packages/ dir exists'
mkdir -p /Library/Addigy/ansible/packages/
echo "Ensuring /Library/Addigy/ansible/packages/$customSoftwareName"
mkdir -p "/Library/Addigy/ansible/packages/$customSoftwareName"

# Paste curl here - change folder path to match custom software name, update package name
(md5 "/Library/Addigy/ansible/packages/Microsoft Office - Deployer (16.60)/MicrosoftOffice2019-16.60.pkg" | grep "068c6e31b6751bb913789393581b3f49") || /Library/Addigy/lan-cache --md5=068c6e31b6751bb913789393581b3f49 download "https://file-manager-prod.addigy.com/file/c8dda60b-ad7e-46cb-b067-a121be95b9c8" "/Library/Addigy/ansible/packages/Microsoft Office - Deployer (16.60)/MicrosoftOffice2019-16.60.pkg" || ( (/Library/Addigy/go-agent download "https://file-manager-prod.addigy.com/file/c8dda60b-ad7e-46cb-b067-a121be95b9c8" "/Library/Addigy/ansible/packages/Microsoft Office - Deployer (16.60)/MicrosoftOffice2019-16.60.pkg" || curl -f -H "orgid: $(/Library/Addigy/go-agent agent orgid)" -H "agentid: $(/Library/Addigy/go-agent agent agentid)" -H "auth-token: $(/Library/Addigy/go-agent agent auth_secret_token)" -o "/Library/Addigy/ansible/packages/Microsoft Office - Deployer (16.60)/MicrosoftOffice2019-16.60.pkg" "https://file-manager-prod.addigy.com/file/c8dda60b-ad7e-46cb-b067-a121be95b9c8") && (md5 "/Library/Addigy/ansible/packages/Microsoft Office - Deployer (16.60)/MicrosoftOffice2019-16.60.pkg" | grep "068c6e31b6751bb913789393581b3f49") )

#Install pkg
/usr/sbin/installer -pkg "$packagePath/$pkgName" -target /

exit 0
Install Addigy's MSO 16.60 public installer pkg

With that settled, we've cleared the first hurdle - we can now assign Microsoft Office to auto-install on a selected policy and feel confident that the package won't run on devices already in production, risking damage to the apps.

INSTALLING MSO VIA MACMANAGE

Offering users a way to install Microsoft Office themselves, or reinstall it if there's a problem, can be a low-effort self-remediation workflow. There are times when using the public software item can make sense, and times when you'll want to leverage your own custom software installer.

The public software library item:

  • Will not allow you to run it if it is the same version number as the currently-installed copy of Office
  • Offers no visual feedback that action is occurring, making it hard to know if the installer has frozen, halted for any reason, or has finished

My custom software item:

  • Can be used to reinstall MSO, not just install a fresh copy
  • Includes a user approval popup to begin and then provides the user visual feedback on installation progress

Both items are useful in their own ways, but given the frequency with which we need to reinstall Microsoft Office for troubleshooting purposes, opting for an option that lacks a condition script (allowing you to reinstall) made sense.

The GUI is also a nice touch to give users a more "premium" experience - it can be uncomfortable clicking a button and not knowing what's happening during a 10-minute install. We're going to bypass talking about the makeup of my custom GUI for Microsoft Office installations in MacManage until another day - the code is much more complicated than it needs to be and I'd like to clean it up before publishing. To summarize, we make sure to offer users a way to either download Office for the first time or trigger a reinstall at their discretion, without the need for administrator approval, and give them feedback on the progress of the installer since it takes so long.

MICROSOFT AUTOUPDATE

Microsoft AutoUpdate (MAU) has not always been a useful solution. The interface and user experience used to be awkward and clunky, but things have improved over the years. Nowadays, it's offering functionality that makes it competitive with other management options.  It's possible to manage MAU via custom software/bash, but we're opting to configure it using an MDM Profile including the following keys:

# Update channel: Current
# How to check for updates: Automatically check, download, and install
# Ignore UI Open after Install
# Update deadline: Days before forced quit: 7
# Updater optimization: CPU
# Update check cadence: Default (12 hours), not overridden below

	<key>ChannelName</key>
	<string>Current</string>
	<key>HowToCheck</key>
	<string>AutomaticDownload</string>
	<key>IgnoreUIOpenAfterInstall</key>
	<true/>
	<key>PayloadDescription</key>
	<string>Configures Microsoft AutoUpdate settings</string>
	<key>PayloadDisplayName</key>
	<string>Microsoft AutoUpdate</string>
	<key>PayloadIdentifier</key>
	<string>com.github.ntiva.ProfileCreator.3C194A5F-A420-4900-8DBD-0B2579A09BF2.com.microsoft.autoupdate2.56F18800-06FC-4928-A358-8423FBC96DC9</string>
	<key>PayloadType</key>
	<string>com.microsoft.autoupdate2</string>
	<key>PayloadUUID</key>
	<string>56F18800-06FC-4928-A358-8423FBC96DC9</string>
	<key>PayloadVersion</key>
	<integer>1</integer>
	<key>UpdateDeadline.DaysBeforeForcedQuit</key>
	<integer>7</integer>
	<key>UpdaterOptimization</key>
	<string>CPU</string>
Microsoft AutoUpdate MDM Keys

This translates to MAU checking for updates every 12 hours and attempting to update apps by itself for 7 days before forcing the user to close the app to apply the update. It should offer us the best of both worlds, where silent updating is supported, but enforcement is scheduled to nudge users to quit the app after enough time has passed.

We were delighted to see a key that allows you to optimize the updater for either CPU or hard drive. If you're working with a client who is heavily mobile, maybe tethering to a cell phone, optimizing for hard drive ensures that the device uses an update source that includes smaller file sizes, but requires more processor overhead to execute. If you're working with a client that has powerful endpoint protection software, optimizing for CPU leads to the device downloading larger update files that are easier on their Mac to execute, which could prevent slowdown or poor performance due to endpoint protection scanning.

One last note (thanks to @simonhowes in the MacAdmins Slack) - MAU doesn't acknowledge and update any apps that have not yet been opened. To circumvent this, you can pre-register your apps in this same MDM config, to determine which apps will be updated. By default, I'm sticking with the apps installed by the BusinessPro installer (basic suite plus Teams), though you can opt to include Microsoft Remote Desktop, Edge, or others. To do this, add a <dict> with the key <Applications> and list out your apps like so:

	<dict>
		<key>Applications</key>
		<dict>
			<key>/Applications/Microsoft Excel.app</key>
			<dict>
				<key>Application ID</key>
				<string>XCEL2019</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/Microsoft OneNote.app</key>
			<dict>
				<key>Application ID</key>
				<string>ONMC2019</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/Microsoft Outlook.app</key>
			<dict>
				<key>Application ID</key>
				<string>OPIM2019</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/Microsoft PowerPoint.app</key>
			<dict>
				<key>Application ID</key>
				<string>PPT32019</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/Microsoft Teams.app</key>
			<dict>
				<key>Application ID</key>
				<string>TEAMS10</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/Microsoft Word.app</key>
			<dict>
				<key>Application ID</key>
				<string>MSWD2019</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Applications/OneDrive.app</key>
			<dict>
				<key>Application ID</key>
				<string>ONDR18</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
			<key>/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app</key>
			<dict>
				<key>Application ID</key>
				<string>MSau04</string>
				<key>LCID</key>
				<integer>1033</integer>
			</dict>
		</dict>
Pre-register your apps to make sure they all get updated