Skip to main content

While we're waiting for Patch Management to support scripts, here's one method to leverage Microsoft AutoUpdate (MAU) version 3.18 msupdate command-line tool to update Office apps via Jamf Pro 10's built-in patch features, inspired by @pbowden.



Also available on GitHub.






Background



After testing @pbowden's MSUpdateHelper4JamfPro.sh script in our environment, my first thought was to use a payload-free package's post-install script to simply call a policy which ran Paul's script:



Microsoft Office 2016 Update 1.0.0.pkg



Post-install Script



#!/bin/sh
## postinstall

pathToScript=$0
pathToPackage=$1
targetLocation=$2
targetVolume=$3

echo " "
echo "###"
echo "# Microsoft Office 2016 Update"
echo "###"
echo " "

/usr/local/bin/jamf policy -trigger microsoftUpdate -verbose

exit 0


After counseling with @ted.johnsen, he convinced me that a Patch Policy calling a standard policy was too much of a hack.






Payload-free Post-install Script



Using Composer, create a payload-free package which contains the following post-install script. (In Composer, I named the package Microsoft Office 2016 msupdate.)



The post-install script will use the word after "Microsoft" in the pathToPackage variable as the application name to be updated (i.e., "Excel") and the word after "msupdate" in the pathToPackage variable as the target version number (i.e., "16.12.18041000").



In Composer, build the .PKG and in the Finder, manually duplicate and rename it based on the application to update and the desired version.



For example:
• Microsoft Word 2016 msupdate 16.12.18041000.pkg
• Microsoft Excel 2016 msupdate 16.12.18041000.pkg
• Microsoft PowerPoint 2016 msupdate 16.12.18041000.pkg
• Microsoft Outlook 2016 msupdate 16.12.18041000.pkg
• Microsoft OneNote 2016 msupdate 16.12.18041000.pkg



Add the packages to the definitions of the Microsoft Office Patch Management Software Titles.



When the patch policies run, the post-install script will leverage the "msupdate" binary to apply the updates.



Updated patch definitions are available on GitHub.



Script



#!/bin/sh
## postinstall

pathToScript=$0
pathToPackage=$1
targetLocation=$2
targetVolume=$3

####################################################################################################
#
# ABOUT
#
# Microsoft Office 2016 msupdate Post-install
# Inspired by: https://github.com/pbowden-msft/msupdatehelper
#
# Microsoft AutoUpdate (MAU) version 3.18 and later includes the "msupdate" binary which can be
# used to start the Office for Mac update process.
# See: https://docs.microsoft.com/en-us/DeployOffice/mac/update-office-for-mac-using-msupdate
#
# Jamf Pro 10 Patch Management Software Titles currently require a .PKG to apply updates
# (as opposed to a scripted solution.)
#
# This script is intended to be used as a post-install script for a payload-free package.
#
# Required naming convention: "Microsoft Excel 2016 msupdate 16.12.18041000.pkg"
# • The word after "Microsoft" in the pathToPackage is the application name to be updated (i.e., "Excel").
# • The word after "msupdate" in the pathToPackage is the target version number (i.e., "16.12.18041000").
#
####################################################################################################
#
# HISTORY
#
# Version 1.0.0, 26-Apr-2018, Dan K. Snelson
# Version 1.0.1, 21-Jun-2018, Dan K. Snelson
# Updated PerformUpdate function; thanks qharouff
# Recorded the version of msupdate installed
#
####################################################################################################

echo " "
echo "###"
echo "# Microsoft Office 2016 msupdate Post-install"
echo "###"
echo " "



###
# Variables
###


# IT Admin constants for application path
PATH_WORD="/Applications/Microsoft Word.app"
PATH_EXCEL="/Applications/Microsoft Excel.app"
PATH_POWERPOINT="/Applications/Microsoft PowerPoint.app"
PATH_OUTLOOK="/Applications/Microsoft Outlook.app"
PATH_ONENOTE="/Applications/Microsoft OneNote.app"

# Path to package
echo "• pathToPackage: ${1}"

# Target app (i.e., the word after "Microsoft" in the pathToPackage)
targetApp=$( /bin/echo ${1} | /usr/bin/awk '{for (i=1; i<=NF; i++) if ($i~/Microsoft/) print $(i+1)}' )

# Target version (i.e., the word after "msupdate" in the pathToPackage)
targetVersion=$( /bin/echo ${1} | /usr/bin/awk '{for (i=1; i<=NF; i++) if ($i~/msupdate/) print $(i+1)}' | /usr/bin/sed 's/.pkg//' )

echo " "





###
# Define functions
###


# Function to check whether MAU 3.18 or later command-line updates are available
function CheckMAUInstall() {
if ! -e "/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate" ]; then
echo "*** Error: MAU 3.18 or later is required! ***"
exit 1
else
mauVersion=$( /usr/bin/defaults read "/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/Info.plist" CFBundleVersion )
echo "• MAU ${mauVersion} installed; proceeding ..."
fi
}



# Function to check whether Office apps are installed
function CheckAppInstall() {
if ( ! -e "/Applications/Microsoft ${1}.app" ]; then
echo "*** Error: Microsoft ${1} is not installed; exiting ***"
exit 1
else
echo "• Microsoft ${1} installed; proceeding ..."
fi
}



# Function to determine the logged-in state of the Mac
function DetermineLoginState() {
CONSOLE=$( stat -f%Su /dev/console )
if so "${CONSOLE}" == "root" ]] ; then
echo "• No user logged in"
CMD_PREFIX=""
else
echo "• User ${CONSOLE} is logged in"
CMD_PREFIX="sudo -u ${CONSOLE} "
fi
}



# Function to register an application with MAU
function RegisterApp() {
echo "• Register App: Params - $1 $2"
$(${CMD_PREFIX}defaults write com.microsoft.autoupdate2 Applications -dict-add "$1" "{ 'Application ID' = '$2'; LCID = 1033 ; }")
}



# Function to call 'msupdate' and update the target application
function PerformUpdate() {
echo "• Perform Update: ${CMD_PREFIX}./msupdate --install --apps $1 --version $2 --wait 600 2>/dev/null"
result=$( ${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --install --apps $1 $2 --wait 600 2>/dev/null )
echo "• ${result}"
}



###
# Command
###

CheckMAUInstall
CheckAppInstall ${targetApp}
DetermineLoginState

echo " "
echo "• Updating Microsoft ${targetApp} to version ${targetVersion} ..."

case "${targetApp}" in

"Word" )

RegisterApp "${PATH_WORD}" "MSWD15"
PerformUpdate "MSWD15" "${targetVersion}"
;;

"Excel" )

RegisterApp "${PATH_EXCEL}" "XCEL15"
PerformUpdate "XCEL15" "${targetVersion}"
;;


"PowerPoint" )

RegisterApp "${PATH_POWERPOINT}" "PPT315"
PerformUpdate "PPT315" "${targetVersion}"
;;

"Outlook" )

RegisterApp "${PATH_OUTLOOK}" "OPIM15"
PerformUpdate "OPIM15" "${targetVersion}"
;;

"OneNote" )

RegisterApp "${PATH_ONENOTE}" "ONMC15"
PerformUpdate "ONMC15" "${targetVersion}"
;;

*)

echo "*** Error: Did not recognize the target appliction of ${targetApp}; exiting. ***"
exit 1

;;

esac

echo "• Update inventory ..."
/usr/local/bin/jamf recon

echo " "
echo "Microsoft Office 2016 msupdate Post-install Completed"
echo "#####################################################"
echo " "



exit 0 ## Success
exit 1 ## Failure

Updated for Microsoft Office 2019



Includes two new functions, CheckInstalledVersion and ConfirmUpdate, to execute jamf recon only after the update is complete (or the time-out conditions are met). Also available on GitHub.



#!/bin/sh
## postinstall

pathToScript=$0
pathToPackage=$1
targetLocation=$2
targetVolume=$3

####################################################################################################
#
# ABOUT
#
# Microsoft Office 2019 msupdate Post-install
# Inspired by: https://github.com/pbowden-msft/msupdatehelper
#
# Microsoft AutoUpdate (MAU) version 3.18 and later includes the "msupdate" binary which can be
# used to start the Office for Mac update process.
# See: https://docs.microsoft.com/en-us/DeployOffice/mac/update-office-for-mac-using-msupdate
#
# Jamf Pro 10 Patch Management Software Titles currently require a .PKG to apply updates
# (as opposed to a scripted solution.)
#
# This script is intended to be used as a post-install script for a payload-free package.
#
# Required naming convention: "Microsoft Excel 2019 msupdate 16.17.18090901.pkg"
# • The word after "Microsoft" in the pathToPackage is the application name to be updated (i.e., "Excel").
# • The word after "msupdate" in the pathToPackage is the target version number (i.e., "16.17.18090901").
#
####################################################################################################
#
# HISTORY
#
# Version 1.0.0, 04-Oct-2018, Dan K. Snelson
# Based on "Microsoft Office 2016 msupdate 1.0.8"
#
####################################################################################################

###
# Variables
###

msUpdatePause="600" # Number of seconds for msupdate processes to wait (recommended value: 600)
numberOfChecks="15" # Number of times to check if the target app has been updated
delayBetweenChecks="30" # Number of seconds to wait between tests

# IT Admin constants for application path
PATH_WORD="/Applications/Microsoft Word.app"
PATH_EXCEL="/Applications/Microsoft Excel.app"
PATH_POWERPOINT="/Applications/Microsoft PowerPoint.app"
PATH_OUTLOOK="/Applications/Microsoft Outlook.app"
PATH_ONENOTE="/Applications/Microsoft OneNote.app"

# Target app (i.e., the word after "Microsoft" in the pathToPackage)
targetApp=$( /bin/echo ${1} | /usr/bin/awk '{for (i=1; i<=NF; i++) if ($i~/Microsoft/) print $(i+1)}' )

# Target version (i.e., the word after "msupdate" in the pathToPackage)
targetVersion=$( /bin/echo ${1} | /usr/bin/awk '{for (i=1; i<=NF; i++) if ($i~/msupdate/) print $(i+1)}' | /usr/bin/sed 's/.pkg//' )



###
# Define functions
###


# Function to check whether MAU 3.18 or later command-line updates are available
function CheckMAUInstall() {
if ! -e "/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate" ]; then
echo "*** Error: MAU 3.18 or later is required! ***"
exit 1
else
mauVersion=$( /usr/bin/defaults read "/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/Info.plist" CFBundleVersion )
echo "• MAU ${mauVersion} installed; proceeding ..."
fi
}



# Function to check whether Office apps are installed
function CheckAppInstall() {
if ! -e "/Applications/Microsoft ${1}.app" ]; then
echo "*** Error: Microsoft ${1} is not installed; exiting ***"
exit 1
else
echo "• Microsoft ${1} installed; proceeding ..."
fi
}



# Function to determine the logged-in state of the Mac
function DetermineLoginState() {
CONSOLE=$( stat -f%Su /dev/console )
if "${CONSOLE}" == "root" ]] ; then
echo "• No user logged in"
CMD_PREFIX=""
else
echo "• User ${CONSOLE} is logged in"
CMD_PREFIX="sudo -u ${CONSOLE} "
fi
}



# Function to register an application with MAU
function RegisterApp() {
echo "• Register App: $1 $2"
$(${CMD_PREFIX}defaults write com.microsoft.autoupdate2 Applications -dict-add "$1" "{ 'Application ID' = '$2'; LCID = 1033 ; }")
}



# Function to call 'msupdate' and update the target application
function PerformUpdate() {
echo "• Perform Update: ${CMD_PREFIX}./msupdate --install --apps $1 --version $2 --wait ${msUpdatePause}"
result=$( ${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --install --apps $1 --version $2 --wait ${msUpdatePause} 2>/dev/null )
echo "• ${result}"
}



# Function to check the currently installed version
function CheckInstalledVersion() {
installedVersion=$( /usr/bin/defaults read "${1}"/Contents/Info.plist CFBundleVersion )
echo "• Installed Version: ${installedVersion}"
}



# Function to confirm the update, then perform recon
function ConfirmUpdate() {
echo "• Target Application: ${1}"
CheckInstalledVersion "${1}"
counter=0
until te ${installedVersion} == ${targetVersion} ]] || {t ${counter} -gt ${numberOfChecks} ]]; do
((counter++))
echo "• Check ${counter}; pausing for ${delayBetweenChecks} seconds ..."
/bin/sleep ${delayBetweenChecks}
CheckInstalledVersion "${1}"
done

if on ${installedVersion} == ${targetVersion} ]]; then
echo "• Target Version: ${targetVersion}"
echo "• Installed Version: ${installedVersion}"
echo "• Update inventory ..."
/usr/local/bin/jamf recon
else
echo "WARNING: Update not completed within the specified duration; recon NOT performed"
echo "• Target Version: ${targetVersion}"
echo "• Installed Version: ${installedVersion}"
echo "• Delay Between Checks: ${delayBetweenChecks}"
echo "• Number of Checks: ${numberOfChecks}"
fi

}



###
# Command
###



echo " "
echo "#############################################################"
echo "# Microsoft Office 2019 msupdate v1.0.0 for ${targetApp}"
echo "#############################################################"
echo " "
echo "• Path to Package: ${1}"
echo "• Target App: ${targetApp}"
echo "• Target Version: ${targetVersion}"
echo " "

CheckMAUInstall
CheckAppInstall ${targetApp}
DetermineLoginState

echo " "
echo "• Updating Microsoft ${targetApp} to version ${targetVersion} ..."

case "${targetApp}" in

"Word" )

RegisterApp "${PATH_WORD}" "MSWD2019"
PerformUpdate "MSWD2019" "${targetVersion}"
ConfirmUpdate "${PATH_WORD}"
;;

"Excel" )

RegisterApp "${PATH_EXCEL}" "XCEL2019"
PerformUpdate "XCEL2019" "${targetVersion}"
ConfirmUpdate "${PATH_EXCEL}"
;;

"PowerPoint" )

RegisterApp "${PATH_POWERPOINT}" "PPT32019"
PerformUpdate "PPT32019" "${targetVersion}"
ConfirmUpdate "${PATH_POWERPOINT}"
;;

"Outlook" )

RegisterApp "${PATH_OUTLOOK}" "OPIM2019"
PerformUpdate "OPIM2019" "${targetVersion}"
ConfirmUpdate "${PATH_OUTLOOK}"
;;

"OneNote" )

RegisterApp "${PATH_ONENOTE}" "ONMC2019"
PerformUpdate "ONMC2019" "${targetVersion}"
ConfirmUpdate "${PATH_ONENOTE}"
;;

*)

echo "*** Error: Did not recognize the target application of ${targetApp}; exiting. ***"
exit 1
;;

esac



echo " "
echo "Microsoft Office 2019 msupdate completed for ${targetApp}"
echo "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
echo " "



exit 0 ## Success
exit 1 ## Failure

@dan.snelson do you happen to have anything that will work if no one is logged in? I am trying to update computer labs.


@mcfarlandp Sorry, no. (I presume from your question that @pbowden's msupdatehelper is requiring a user to be logged in.)


@dan.snelson yes it is. I am trying to use the script instead of just installing over the top with individual packages. I have been talking with others about this and they were having the same issues.


What happens if the apps are open and in use? JAMFs patch management seems to kill the application and I've had users loose work.


@MatG First, please pardon the delayed response; just now seeing this.



Which patch definition are you using?



Please see how the killApps node of MicrosoftExcel2019-msupdate.json will only quit Microsoft Dummy.app.


Thanks to @hdsreid for pointing out the need for a Privacy Preferences Policy Control payload.



You may want to start with @pbowden's Privacy Preferences Policy Control payload; plus a couple of differences in the one we're using.



Add the following Identifiers for the Receiver Identifier of com.microsoft.autoupdate2:



/usr/local/jamf/bin/jamfAgent


… and …



com.jamf.management.Jamf

Dan,
I've run into an issue with this month's attempt at patch management on my Office 2019. I've got it working for last month's version using the method above (package is Microsoft Excel msupdate 16.30.19101301.pkg) but when I create a package for this month's using Microsoft Excel msupdate 16.31.19111002 the msupdate binary seems to fail out.



The binary also fails out when trying to use --version 16.31.19111002 as a flag. Checking on macadmin's website about the MAU, it doesn't show this version number as supported through the --version flag. Is that why this is failing for me?


Just in case anyone was curious - last night (11/20) the MAU site was updated to include the new version as a flag. I'll go ahead and test this through on a few of my machines just to make sure things are working. I didn't know the update times of the MAU.


@dan-snelson the 2019 script will work fine for 365 installs, correct?


@hdsreid Yes, it should (that's what we're using).



@sfurois Thanks for the posts. (@pbowden is frequently monitoring MacAdmins Slack #microsoft-office channel.)


My latest definitions are available on @brysontyrrell's Community Patch.


@dan-snelson thanks, trying to get this working on a few lab units. i'm curious, do each of your machines individually download the updates from microsoft directly? one of the current advantages of deploying the packages through jamf is the ability to cache them on local shares for remote sites.


@hdsreid Yes, each client puts the burden on Microsoft’s CDNs instead of ours, which works well for our current, worldwide use-case.



From @pbowden’s JNUC 2019 presentation, the cumulative size of all the Office updates is significantly smaller than when I originally wrote this script (although I still like leveraging the msupdate binary to allow behind-the-scenes updates).


@dan-snelson gotcha, I have 4 iMacs in a single remote office that do not have much bandwidth, but I can make workarounds for them (running overnight) to get it working as the majority of my users are connecting to the cloud distribution point from wherever they are.



i do seem to be having an issue that I was reading in another thread about msupdate tool though, where every time i use it all i get returned is "No result returned from the Update Assistant". The GUI does show that I am on 16.30 and that 16.31 is available for the entire suite, however. time to do some more digging :)


@hdsreid You may want to ping pbowden on MacAdmins Slack #microsoft-office channel


Thanks for the scripts and help so far @dan-snelson @pbowden
having deployed this to a handful of machines today, its a little hit or miss.



Here is what i have done so far.




  1. config profile for msupdate 2 set to allow terminal and Jamf to chat, etc

  2. second config profile that registers the apps along with ID: eg Applications={/Applications/Microsoft Excel.app={LCID=1033, Application ID=XCEL2019}, all to domain com.microsoft.autoupdate2

  3. changed some script variables to ensure the 2019 entries are there vs 2016, eg PerformUpdate "MSWD2019" "$TARGET_VERSION"

  4. verified on the machines in question that they had MAU 3.18+ and with version 16.29/16.30 for office apps.

  5. did a GUI MAU check and it showed 4 updates, did a msupdate check and it showed 4 updates, all up to 16.32 (oddly enough, MAU 4.18 wasnt picked up)

  6. ran the policy with "yes@16.32.19120802" in the script variable lines for all the office products i wanted (all 5 main apps)

  7. most of the time the policy needs to run multiple times to ensure everything gets up to date. not sure if its because its pulling each individual file (1 of 5, each file being 1gb or so vs if you pull fill installer its 1.5gb total) so the data rate is slow or it crashes or times out, but it would update a few files, then kinda just stop.

  8. i had new, unsaved, word, ppt, and outlook files open and it would upgrade outlook in the background with no notifications while it was running. word and ppt just hung out and stayed the same version. i was under the impression that it would prompt with a dialog box/notification asking to restart the app now or later, didnt get that.

  9. had a machine run this about 3-4 times and eventually it kinda just stopped seeing updates. now the machine is in limbo where the GUI picks up 2-3 apps that needs to be installed but the script shoots this out for every app:
    Detecting and downloading updates...
    No result returned from Update Assistant

  10. running msupdate -l also shows no updates available, however when i click the refresh button in the MAU GUI it shows that updates are available.

  11. all the above was run on catalina

  12. i ran a "defaults read com.microsoft.autoupdate2" on my mojave machine and it gives me all the info i need, i run the same on catalina and it tells me domain com.microsoft.autoupdate2 doesnt exist.
    i find instances of the autoupdate2 in users/usr/library/preferences (lots more info) and /library/managed preferences/usr (actual config profile from JAMF)



is this something to do with a timer where it stops checking after a while or something to do with it checking recently so itll STOP checking again? the goal is to do a seamless upgrade for the users as doing the upgrades via deploying the 1.5gb PKG sometimes proves problematic for some users.



i was reading somewhere about a "last check" that needs to be removed to make it successful again, but i havent come across that variable unless it means one of these:
LastService = "2019-12-17 22:32:54 +0000";
LastUpdate = "2019-12-18 00:00:10 +0000";
if thats the case, how do we code that out? wiping and making a few config profile every monthly patch?



Looking for a consistent way to get it done without causing headaches for users. theres about a 5-8% total population effect of Outlook or Word crashing witha DYAL error or something which the only fix for is to reinstall that one package (in this case, outlook or word)



Thanks!


@jcheLC Please ping pbowden on MacAdmins Slack #microsoft-office channel.



(I, too, see similar issues, which seem to vary by Office release.)


we need an invite to that channel ya?


I don't recall, @jcheLC; Have you joined macadmins.slack.com?


@dan-snelson yeah just joined earlier today.



oddly enough, will notifications, whitelisting/approving MAU and all office products and a couple more config profiles it works as expected when running the script itself (no modifiers to script since local terminal).



it gives notifications, asks to restart app, i can see it update and install apps, etc.



But self service either changes something or it doesnt like being run as root even though the script calls current user.



only difference between the scripts is me using "yes@16.32.19120802" for the script call out in Self Service vs the shell script defaulting to "Latest Version".


I know there are apps that do this other than Composer, and I have one downloaded, but I don't see an obvious way to create a payload free package in Composer. Exactly how do you do this?


As far as I know, I'm following the right procedure, but when I run the patches via Self Service and watch autoupdate.log in Console, I see this message repeated over and over again:



2020-05-05 11:45:29 [Microsoft Update Assistant] <Debug> InstallUpdatesTask.Progress: {"Payload":"Progress: installer:PHASE:Waiting for other installations to complete…
"}



I don't know what "other installations" it's waiting on. When I kicked off the update process, I saw that the test Mac did download packages to run for the updates. I can't figure out exactly what AutoUpdate is waiting on.


@howie_isaacks As it stands right now MAU has some issues if you're trying to call msupdate via a script to install updates, but the May update should address those. For a more detailed discussion see the #microsoft-autoupdate channel on macadmins.slack.com


@howie_isaacks please send me email (pbowden@microsoft.com) and we’ll figure out why things aren’t working there.


Reply