Posted on 04-26-2018 08:21 AM
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.
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:
#!/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.
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.
#!/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 [[ "${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
Posted on 11-22-2019 06:23 AM
@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 :)
Posted on 11-22-2019 07:05 AM
@hdsreid You may want to ping pbowden on MacAdmins Slack #microsoft-office channel
Posted on 12-18-2019 05:05 PM
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.
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!
Posted on 12-18-2019 06:30 PM
@jcheLC Please ping pbowden on MacAdmins Slack #microsoft-office channel.
(I, too, see similar issues, which seem to vary by Office release.)
Posted on 12-19-2019 08:23 AM
we need an invite to that channel ya?
Posted on 12-19-2019 09:22 AM
I don't recall, @jcheLC; Have you joined macadmins.slack.com?
Posted on 12-19-2019 03:46 PM
@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".
Posted on 05-01-2020 07:32 AM
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?
Posted on 05-05-2020 09:50 AM
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.
Posted on 05-05-2020 06:46 PM
@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
Posted on 05-05-2020 06:52 PM
@howie_isaacks please send me email (pbowden@microsoft.com) and we’ll figure out why things aren’t working there.
Posted on 05-11-2020 06:31 AM
@sdagley and @pbowden Thanks for responding. I will definitely follow the Microsoft AutoUpdate channel to get more info. I have 8 Jamf Pro servers that I have been uploading about 5.5GB of Office updates to once a month. When I do that the process takes nearly my whole day. My download speed in my home office is 1Gb/s but my upload is only around 40Mb/s. If I could get this process working, I could stop doing that.
Posted on 06-23-2020 08:47 PM
@howie_isaacks I think you're probably running into the same problem I am which is the script is being run from a post-install on a package which is holding up the installer process. As soon as the script exits installer gets freed up and installs the update. I'm not sure how @dan-snelson has gotten around this issue.
Posted on 06-24-2020 06:04 AM
@howie_isaacks: I, too, am impacted by the issue described immediately above by @lmatthews.
At @pbowden's suggestion, I'm now calling ./msupdate --install
once to update all Office apps, using Outlook as a reference.
(You'll need to use something similar to "MicrosoftOffice2019-msupdate" from dan-snelson (024201f66e32b719aad0ce2372d5a1e8)
Community Patch as the Patch Definition.)
#!/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.
#
# No required naming convention; all Office apps are updated to their latest version.
#
####################################################################################################
#
# HISTORY
#
# Version 2.0.1, 10-Mar-2020, Dan K. Snelson
# Based on "Microsoft Office 2019 msupdate 1.0.5"
#
####################################################################################################
###
# Variables
###
msUpdatePause="300" # Number of seconds for msupdate processes to wait (recommended value: 600)
numberOfChecks="2" # Number of times to check if the target app has been updated
delayBetweenChecks="10" # 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"
PATH_SKYPEBUSINESS="/Applications/Skype for Business.app"
PATH_REMOTEDESKTOP="/Applications/Microsoft Remote Desktop.app"
PATH_COMPANYPORTAL="/Applications/Company Portal.app"
PATH_DEFENDER="/Applications/Microsoft Defender ATP.app"
APPID_WORD="MSWD2019"
APPID_EXCEL="XCEL2019"
APPID_POWERPOINT="PPT32019"
APPID_OUTLOOK="OPIM2019"
APPID_ONENOTE="ONMC2019"
APPID_SKYPEBUSINESS="MSFB16"
APPID_REMOTEDESKTOP="MSRD10"
APPID_COMPANYPORTAL="IMCP01"
APPID_DEFENDER="WDAV00"
# 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)}' )
targetApp="Outlook" # Hard-code a check for Outlook
# 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
jssLog "*** 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 we are allowed to send Apple Events to MAU
function CheckAppleEvents() {
MAURESULT=$(${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --config | grep 'No result returned from Update Assistant')
if [[ "$MAURESULT" = *"No result returned from Update Assistant"* ]]; then
echo "ERROR: Cannot send Apple Events to MAU. Check privacy settings."
exit 1
fi
}
# Function to check whether MAU is up-to-date
function CheckMAUUpdate() {
MAUUPDATE=$(${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --list | grep 'MSau04')
if [[ "$MAUUPDATE" = *"MSau04"* ]]; then
echo "Updating MAU to latest version... $MAUUPDATE"
echo "$(date)"
RESULT=$(${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --install --apps MSau04)
sleep 120
fi
}
# Function to check whether its safe to close Excel because it has no open unsaved documents
function OppCloseExcel() {
APPSTATE=$(${CMD_PREFIX}pgrep "Microsoft Excel")
if [ ! "$APPSTATE" == "" ]; then
DIRTYDOCS=$(${CMD_PREFIX}defaults read com.microsoft.Excel NumTotalBookDirty)
if [ "$DIRTYDOCS" == "0" ]; then
echo "$(date)"
echo "Closing Excel as no unsaved documents are open"
$(${CMD_PREFIX}pkill -HUP "Microsoft Excel")
fi
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() {
# The following line is courtesy of @macmule - https://macmule.com/2014/11/19/how-to-get-the-currently-logged-in-user-in-a-more-apple-approved-way/
CONSOLE=$(/usr/bin/python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");')
if [ "$CONSOLE" == "" ]; then
echo "No user currently logged in to console - using fall-back account"
CONSOLE=$(/usr/bin/last -1 -t ttys000 | /usr/bin/awk '{print $1}')
echo "Using account $CONSOLE for update"
CMD_PREFIX="sudo -u $CONSOLE "
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 flush any existing MAU sessions
function FlushDaemon() {
$(${CMD_PREFIX}defaults write com.microsoft.autoupdate.fba ForceDisableMerp -bool TRUE)
$(${CMD_PREFIX}pkill -HUP "Microsoft Update Assistant")
}
# Function to call 'msupdate' and update the target application
function PerformUpdate() {
echo "• Perform Update: ${CMD_PREFIX}./msupdate --install" # Update ALL Office apps
result=$( ${CMD_PREFIX}/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --install 2>/dev/null ) # Update ALL Office apps
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 [[ ${installedVersion} == ${targetVersion} ]] || [[ ${counter} -gt ${numberOfChecks} ]]; do
((counter++))
echo "• Check ${counter}; pausing for ${delayBetweenChecks} seconds ..."
/bin/sleep ${delayBetweenChecks}
CheckInstalledVersion "${1}"
done
if [[ ${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 v2.0.1 #"
echo "#########################################"
echo " "
echo "• Path to Package: ${1}"
echo "• Target App: ${targetApp}"
#echo "• Target Version: ${targetVersion}"
echo " "
CheckMAUInstall
CheckAppInstall ${targetApp}
DetermineLoginState
FlushDaemon
CheckAppleEvents
CheckMAUUpdate
FlushDaemon
RegisterApp "$PATH_WORD" "$APPID_WORD"
RegisterApp "$PATH_EXCEL" "$APPID_EXCEL"
RegisterApp "$PATH_POWERPOINT" "$APPID_POWERPOINT"
RegisterApp "$PATH_OUTLOOK" "$APPID_OUTLOOK"
RegisterApp "$PATH_ONENOTE" "$APPID_ONENOTE"
#RegisterApp "$PATH_SKYPEBUSINESS" "$APPID_SKYPEBUSINESS"
#RegisterApp "$PATH_REMOTEDESKTOP" "$APPID_REMOTEDESKTOP"
#RegisterApp "$PATH_COMPANYPORTAL" "$APPID_COMPANYPORTAL"
#RegisterApp "$PATH_DEFENDER" "$APPID_DEFENDER"
OppCloseExcel
echo " "
#echo "• Updating Microsoft ${targetApp} to version ${targetVersion} ..."
echo "• Updating Microsoft Office to the latest version ..."
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}"
;;
"Office" )
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"
echo "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #"
echo " "
exit 0 ## Success
exit 1 ## Failure
Posted on 08-13-2020 02:17 PM
I lost focus on this due to other more pressing issues, but next week, I will start working on it again.
Posted on 05-15-2022 07:21 AM
NOTE: The helper scripts are now deprecated. Use the new deffered and version pinning capability in Microsoft AutoUpdate for scenarios where you need more control over the timing of updates. See https://www.kevinmcox.com/2021/10/microsoft-now-provides-curated-deferral-channels-for-autoupdate/ for more details.