Posted on 05-30-2018 11:50 AM
If you have downloaded Microsoft Autoupdate v3.18 and above, you will find that it comes with a handy binary "msupdate" to help us IT folk deploy updates for Microsoft Office 2016 for Mac.
I wrote a handy script and created a package that does the following:
1. checks the OS of the computer
2. Checks if Office 365 is installed (with my lazy way of checking)
3. Checks if msupdate binary is installed. If not it will install MAU 4.0 that comes with binary.
4. Checks for the proper configured com.microsoft.autoupdate2.plist file that allows the binary
to shoot up to cloud what MS Office applications have been registered on the computer and check for updates. If it is not properly configured, it will basically copy and paste the plist into the local user's Preference folder.
5. It will then use the binary to check and install updates.
I have been working on this code for about two weeks. It is not very clean as I am an amateur programmer, but these have been my findings testing this new binary package from Microsoft:
It is a lengthy script...I feel like it can be a lot shorter...I added a lot of echo's for testing. It is working fine and does find updates only if a user had previously opened a Microsoft Office app and then it will find updates. This is my main problem right now.
The package contains a pre-configured plist file for helping msupdate properly (but this method doesn't work), and it also contains a Microsoft Auto Update 4.0 installer. These two items are used in the script.
You can find the package I use for this script on my github here:
https://github.com/chrisdahfur/JSS-Deployment/tree/master/Microsoft%20Office%202016%20Update%20Script
#!/bin/bash
## DESCRIPTION OF SCRIPT
: <<'COMMENT'
This script was written by Christian Orellana.
== PURPOSE ==
This script is designed to check and update Microsoft Office to the latest available version.
This script will only install Ms Office 2016 updates to computers that have an operating system higher or equal to 10.10.
== WHAT'S IN JSS ==
For JSS, a package was created that contains the following: com.microsoft.autoupdatev2 plist file with this script and the Binary Installer.
Each function is activated with a boolean variable. Depending on the previously assigned boolean, it will either cause an error for the script and close out with a 1, or continue down the pipeline to install and update microsoft office
until it can exit with a zero.
== WHAT THE FUNCTIONS DO ==
mainProgram - This function goes ahead and runs the first function down the pipeline that will trigger certain functions accordingly.
checkProductVersion - Designed to check the OS Version of the computer. This will determine whether or not the computer is compatible
to receive the install of MS Office 2016. If the computer is on an OS older than Yosemite (10.10) then the script will close out and exit 1.
checkOfficeinstall - This will check if Microsof Office 2016 is already installed on the computer. If it is not, it will go ahead
and run the function to check if the packages for the installer are available on the computer.
checkMSBinary - This is designed to check if the "msupdate" binary has been installed on the computer. If it is not, then it will run a function
to install the binary. If it is installed it will then run the function to check if the com.microsoft.autoupdatev2 plist is installed. This plist can be found in Preferences folder in Library/Application Support.
The msupdate binary comes with Microsoft after Microsoft Auto Update reaches 3.18. Older versions of Auto Update will not have msupdate binary to help admins administer Microsoft Office.
checkForceAdd - This function will check if the autoupdatev2 plist is installed. If it is not it will run a function to install the plist necessary to find the available updates without
having to run the applications. How the msupdate binary works is that it registers a Microsoft Application to the autoupdatev2 plist file. When this application is added, msupdate will run
an internal function to check the plist and see that the application is there and then check what update is available. Without the proper configured plist file, msupdate will not find any
available updates since the application was not added to the plist file. When the Auto Update package is installed, it installs the autoupdatev2 plist, but it will be blank, not containing
any of the Microsoft Apps as a valid application to find updates for. So this function works by checking the text "Word" or "Excel" or any of the other apps in the plist to see if the application is registered in the plist. If it is, then most likely the plist was
properly configured and it can find updates for Microsoft Office. If it is not it will then run the function to add the correct plist.
forceAddinstall - This function will install a pre-configured plist file with the proper settings for msupdate to work properly. When the plist is installed it is is copied over from a folder hidden on the computer
and not accessible to users. The owner is then changed on the plist from the root user to the locally logged in user. This needs to happen because then the msupdate binary cannot properly
check for updates because it is not allowed to access the plist. After this it will go ahead and run the checkForceAdd function to see if the plist is properly configured.
checkbinaryinstaller - This function will check if the binary installer package is on the computer. What is actually being installed is Microsoft Auto Update 4.0, which also will include
the msupdate binary necessary for updating. If the package is available then it will return a boolean for ensuring that an install of the packager can occur.
installMSBinary - This function will run the packager to install the msupdate binary. After it is installed it will run the checkMSBinary function to ensure that the binary was installed.
checkForUpdates - This function will only run once the binary is installed and the proper plist has been configured for finding updates. This will use the msupdate binary to
check for any available updates using the flag --list. If it finds any available updates it will then run the installUpdates function. If there are no updates, it will run the
exitProgram function for ending the script.
installUpdates - This function will go ahead and run the --install flag for the msupdate binary. This will only run if the script found available updates through msupdate --list.
When msupdate --install runs, what happens is that it will download and queue updates for MS Office applications and install each one by one. This function is designed to keep track of
this queued process to make sure it finishes and check if there are no more available updates. If there are no more updates being installed it will then run the checkForUpdates function
which will then close out the script with the exitProgram function.
exitProgram - This function is solely written to isolate the final exit 0 call so that the script can end and return a succesful run to the JSS server.
COMMENT
## START OF SCRIPT
## VARIABLES PATHS
osvers=$(sw_vers -productVersion | awk -F. '{print $2}')
operatingsystemversion=$(sw_vers -productVersion)
updater4Direct="/.temp/office/mou4installer.pkg"
msbinary="msupdate"
loggedInUser=$(stat -f%Su /dev/console)
## VARIABLE COUNTERS
installcounter=0
forcecounter=0
wordplist=0
excelplist=0
outlookplist=0
powerpointplist=0
onenoteplist=0
plistTotal=0
## VARIABLE BOOLEANS
productVersionGood=false
wordinstalled=false
excelinstalled=false
msOfficeinstall=false
binaryexists=false
checkadd=false
forceadd=false
binaryinstaller=false
updatesavailable=false
## FUNCTIONS
mainProgram() {
checkProductVersion
}
checkProductVersion() {
echo "Running the checkProductVersion Function..."
echo "We will now check the OS Version of this computer"
echo "The operating system version is "$operatingsystemversion""
if [[ ${osvers} -lt 10 ]]; then
echo "Microsoft Office 2016 cannot be installed on this computer."
osascript -e 'display notification "Microsoft Office cannot be installed onto this computer" with title "Cannot install MS Office."'
echo "Closing the script now with exit 1"
exit 1
elif [[ ${osvers} -gt 10 || ${osvers} -eq 10 ]]; then
echo "Microsoft Office can be installed to this computer."
productVersionGood=true
checkOfficeinstall
fi
}
checkOfficeinstall() {
echo "Running the checkOfficeinstall Function..."
echo "The product version is higher than Os 10.10 is $productVersionGood"
if [[ $productVersionGood == true ]]; then
echo "We will now check if Microsoft Office is already installed."
if [[ -d /Applications/Microsoft Word.app ]]; then
echo "Word exists."
wordinstalled=true
else
echo "Word deosn't exist."
fi
if [[ -d /Applications/Microsoft Excel.app ]]; then
echo "Excel exists."
excelinstalled=true
else
echo "Excel doesn't exist."
fi
if [[ $wordinstalled == true && $excelinstalled == true ]]; then
echo "MS Office is installed"
msOfficeinstall=true
checkMSBinary
else
echo "MS Office is not installed"
echo "Need to install MS Office"
osascript -e 'display notification "Please contact Mac IT for help." with title "Microsoft Office is not installed."'
exit 1
fi
else
echo "We will now cancel the script due to the OS Version not being valid for install."
exit 1
fi
}
checkMSBinary() {
echo "Running the checkmsbinary Function..."
echo "Microsoft Office is installed on the computer is $msOfficeinstall"
if [[ $msOfficeinstall == true ]]; then
echo "We will check if the MS Office Update Binary is installed."
if [[ -f /Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate ]]; then
echo "The binary is installed, we can run updates."
binaryexists=true
checkForceAdd
else
echo "The binary does not exist, we need to install it."
checkbinaryinstaller
fi
else
echo "We cannot run updates because office is not installed."
exit 1
fi
}
checkForceAdd() {
echo "Running the checkforceadd function to see if the plist for updating is set correctly.."
echo "The binary on the computer exists is $binaryexists."
if [[ $binaryexists == true ]]; then
echo "We will check if the plist is configured correctly."
if grep 'Word' /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist; then
wordplist=1
fi
if grep 'Excel' /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist; then
excelplist=1
fi
if grep 'PowerPoint' /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist; then
powerpointplist=1
fi
if grep 'OneNote' /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist; then
onenoteplist=1
fi
if grep 'Outlook' /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist; then
outlookplist=1
fi
let plistTotal=wordplist+excelplist+powerpointplist+onenoteplist+outlookplist
echo "$plistTotal"
if [[ $plistTotal == 5 ]]; then
echo "The correct plist is running on the computer."
echo "We will now check for updates."
forceadd=true
checkForUpdates
else
echo "The plist is not on the computer."
echo "We will go ahead and install the Plist."
checkadd=true
forceAddinstall
fi
else
echo "The binary doesn't exist. We will now close the script."
exit 1
fi
}
forceAddinstall() {
echo "Running the forceaddapplications function..."
echo "The check add exists in $checkadd"
if [[ $checkadd == true ]]; then
echo "We are going to make sure the binary can see the updates."
cp /.temp/office/com.microsoft.autoupdate2.plist /Users/$loggedInUser/Library/Preferences
chown $loggedInUser /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist
if [[ -f /Users/$loggedInUser/Library/Preferences/com.microsoft.autoupdate2.plist ]]; then
echo "The plist file is installed."
forceadd=true
let forcecounter=forcecounter+1
if [[ forcecounter -eq 3 ]]; then
echo "Closing the script due to the force counter."
exit 1
fi
checkForceAdd
else
echo "The plist file was not installed."
exit 1
fi
echo "Finished force adding applications to catch updates."
fi
}
checkbinaryinstaller() {
echo "Running the checkbinaryinstaller function..."
echo "The binaryexists is $binaryexists."
if [[ $binaryexists == false ]]; then
if [[ -a $updater4Direct ]]; then
echo "The binary installer is on the computer."
binaryinstaller=true
installMSBinary
else
echo "The installer for the binary is not on the computer. Please contact Mac IT."
osascript -e 'display notification "Please contact Mac IT for help." with title "Packages were not installed."'
exit 1
fi
else
echo "No need to check if the binary package. The binary already exists."
fi
}
installMSBinary() {
echo "Running the installmsbinaryinstall Function..."
echo "The binary on the computer exists is $binaryexists"
if [[ $binaryinstaller == true ]]; then
installer -pkg "$updater4Direct" -target /
echo "The binary has been installed."
checkMSBinary
else
echo "No need to install the binary package."
fi
}
checkForUpdates() {
echo "Running the checkforupdates Function..."
echo "The binary on the computer exists is $binaryexists"
if [[ $forceadd == true ]]; then
echo "We are going to check for Updates"
if /Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --list | grep "No updates available"; then
echo "${binaryruncheck}"
echo "There are no updates available."
osascript -e 'display notification "There are no updates available." with title "MS Office is up to date!"'
echo "This script will now close."
exitProgram
else
echo "There are updates available"
osascript -e 'display notification "There are updates available for your Microsoft Office" with title "Updates Available!"'
updatesavailable=true
installUpdates
fi
fi
}
installUpdates() {
echo "Running the installupdates Function..."
echo "Updates are available is $updatesavailable"
if [[ $updatesavailable == true ]]; then
echo "We are going to install the updates available for MS Office 2016."
/Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --install
while /Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --list | grep "Updates available:"; do
echo "There are updates currently queued up and being installed."
sleep 300
let installcounter=installcounter+1
echo "$installcounter"
if /Library/Application Support/Microsoft/MAU2.0/Microsoft AutoUpdate.app/Contents/MacOS/msupdate --list | grep "No updates available"; then
echo "There are no more updates available. We are breaking the while loop."
break
fi
if [[ installacounter -gt 5 ]]; then
echo "Please check if updates were installed. We are now closing the script."
exit 1
fi
osascript -e 'display notification "Updates are still being installed onto the computer for Microsoft Office." with title "Please wait as updates install..."'
done
checkForUpdates
fi
}
exitProgram() {
echo "Thank you for keeping Microsoft Office up to date! We will now exit the script."
osascript -e 'display notification "There are no updates available. Thank for your keeping Microsoft Office up to date." with title "MS Office is up to date!"'
exit 0
}
## MAIN CODE RUN
mainProgram
exit 0
## END OF SCRIPT
Any input would be greatly appreciated. I help administer about 150+ macs with JAMF and this script would be a game changer. Apologies for the length of the post and script! haha if you want to rewrite please feel free. Thanks!
Posted on 05-31-2018 07:25 AM
This might seem nitpicky or out of the scope of the intent of the thread, but my first assessment is that while it's great to get in the habit of using functions to be more object-oriented in the way you approach coding, you're kind of just using them as GOTOs in this script. It might be a little more readable to do something like this (this is barely psuedo-code btw):
func checkOS() {
osVer=$(get the version of the OS)
if [ $osVer -ge $osVerGood ]; then
return 1
else
return 0
fi
}
func checkExcel() {
isExcelInstalled=$(well is it??)
if [ $isExcelInstalled == true ]; then
return 1
else
return 0
fi
}
func willExit() {
exitCode=$1
exitMessage="$2"
echo "$exitMessage"
log "Exited with code $exitCode: $exitMessage"
exit $exitCode
}
### You could even eliminate the variables and just check the returns if you want to get really frugal with your memory.
isOSRight=$(checkOS)
if [ isOSRight == 0 ]; then
willExit 1 "Wrong OS."
fi
isExcelRight=$(checkExcel)
if [ isExcelRight == 0 ]; then
willExit 1 "No Excel found."
fi
willExit 0 "Success!"
et c.
I'm sure there's a metaphor out there for thinking about this but I can't really think of any good ones. Think of functions as rungs on a ladder instead of links in a chain? Squish like grape? I don't know.
Posted on 06-04-2018 09:44 AM
@noahdowd Hey, I appreciate your feedback tbh. I am still getting around the ropes for cleaner code. I will rewrite this one to make it a lot cleaner.