For IT professionals and managed service providers (MSPs), ensuring a consistent, automated, and error-free deployment of software is critical—especially when managing fleets of macOS devices. Microsoft Office 365 remains a foundational productivity suite in both enterprise and SMB environments. Manually installing it across multiple machines, however, can be inefficient and prone to oversight. A powerful solution to this is to learn how to install Office 365 on macOS using a shell script. This blog post dives deep into a shell script that does just that, streamlining installation and providing options like forced reboot for full deployment control.
Background
Automating software deployments is a best practice for IT teams managing multiple endpoints. While tools like Microsoft Endpoint Manager and Jamf offer full-fledged device management, sometimes administrators need lightweight, script-based solutions—particularly in environments leveraging NinjaOne’s automation capabilities. This script simplifies the process to install Office 365 on macOS with minimal human interaction and provides logic for installation validation, cleanup, and optional rebooting.
For MSPs working with diverse client environments or internal IT departments seeking rapid deployment options, this script offers a reliable, scalable alternative.
The Script
#!/usr/bin/env bash # Description: Install Office 365 on MacOS with default install settings. # By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. # Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. # Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. # Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. # Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. # Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. # Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. # EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). # Release Notes: Initial Release print_help() { printf '\n### Below are all the (case sensitive) valid parameters for this script! ###\n' printf '\nPreset Parameter: --forcereboot \n' printf '\t%s\n' "--forcereboot: Reboot the device after the installation is complete." } # Determines whether or not help text is necessary and routes the output to stderr die() { local _ret="${2:-1}" echo "$1" >&2 test "${_PRINT_HELP:-no}" = yes && print_help >&2 exit "${_ret}" } # THE DEFAULTS INITIALIZATION - OPTIONALS _arg_forceReboot="off" _reboot_time="1" # Minutes _reboot_message_giveup=$((_reboot_time * 60 / 2)) # Seconds _reboot_message="A shutdown operation has been mandated by your IT staff and will occur in $((_reboot_time * 60)) seconds." _install_url="https://go.microsoft.com/fwlink/p/?linkid=2009112" _temp_file="/tmp/office.pkg" # Grabbing the parameters and parsing through them. parse_commandline() { while test $# -gt 0; do _key="$1" case "$_key" in --forcereboot) _arg_forceReboot="on" ;; --*) _PRINT_HELP=yes die "[Error] Got an unexpected argument '$1'" 1 ;; *) _PRINT_HELP=yes die "[Error] Got an unexpected argument '$1'" 1 ;; esac shift done } cleanup() { # Clean up the Office 365 installer # Check if the file exists if [[ -f "${_temp_file}" ]]; then if rm -f "${_temp_file}"; then echo "[Info] Cleaned up the Office 365 installer" else echo "[Error] Failed to clean up the Office 365 installer" exit 1 fi else echo "[Info] Office 365 installer not found, nothing to clean up." fi } check_installed() { # Check if Office 365 is already installed if [[ -d "/Applications/Microsoft Word.app" ]] || [[ -d "/Applications/Microsoft Excel.app" ]] || [[ -d "/Applications/Microsoft PowerPoint.app" ]] || [[ -d "/Applications/Microsoft Outlook.app" ]]; then echo "[Error] Office 365 Suite or one of its components is already installed" echo "[Info] To remove the existing installation, please follow Microsoft's instructions found here:" echo "https://support.microsoft.com/en-us/office/troubleshoot-office-for-mac-issues-by-completely-uninstalling-before-you-reinstall-ec3aa66e-6a76-451f-9d35-cba2e14e94c0" exit 1 fi } # Check if we are running as system/root if [[ $EUID -ne 0 ]]; then echo "[Error] This script must be run as System from Ninja or root in the terminal" 1>&2 exit 1 fi # shellcheck disable=SC2154 if [[ "${forceReboot}" == "true" ]]; then _arg_forceReboot="on" fi parse_commandline "$@" # Check if Office 365 is already installed check_installed # Install Office 365 echo "[Info] Downloading Office 365 installer" # Download the Office 365 Suite if curl -L -o "${_temp_file}" "${_install_url}"; then echo "[Info] Office 365 installer downloaded successfully" else echo "[Error] Failed to download the Office 365 installer" exit 1 fi # Install the Office 365 Suite _results=$(installer -pkg "${_temp_file}" -target / -dumplog 2>&1) # shellcheck disable=SC2181 if [[ $? -eq 0 ]]; then echo "[Info] Office 365 Suite installed successfully" else echo "[Error] Failed to install the Office 365 Suite" _errors=$(echo "${_results}" | grep "Error") # Parse errors if (("$(echo "$_errors" | tail -2 | wc -l)" > 0)); then # Check type of error if echo "$_errors" | tail -2 | head -n 1 | grep -q "not enough space"; then # Disk space error echo "[Error] Not enough space to install Office 365 Suite" # Print the raw error message related to disk space and how much space is needed echo "$_errors" | tail -2 | head -n 1 | cut -d ":" -f 5 | cut -d "=" -f 5 | cut -d "," -f 1 else # Other errors echo "[Error] Installer Errors:" echo "$_errors" fi fi cleanup exit 1 fi cleanup if [[ $_arg_forceReboot = "on" ]]; then echo "[Info] Sending message to user that a reboot has been initiated" osascript -e "display alert \"Reboot Initiated\" message \"${_reboot_message}\" with icon caution giving up after ${_reboot_message_giveup}" /dev/null 2>&1 echo "[Info] Rebooting the device" shutdown -r "+${_reboot_time}" 2>&1 fi
Detailed Breakdown
Here’s a comprehensive walkthrough of how this script functions.
1. Argument Parsing and Help Function
The script starts by defining a print_help function that documents the supported parameters. Currently, only one optional argument is available: –forcereboot. This triggers a system reboot after installation.
bash
CopyEdit
–forcereboot: Reboot the device after the installation is complete.
2. Defaults and Variables Initialization
The script initializes critical variables including:
- _install_url: Microsoft’s Office 365 installer URL
- _temp_file: Path where the .pkg will be stored
- _arg_forceReboot: Flag for reboot logic
- _reboot_time and _reboot_message: Used if reboot is triggered
3. Command-Line Parsing
Using parse_commandline, the script reads and validates the input arguments. Any unrecognized input causes an error and prints the help message.
4. Environment Checks
It verifies that the script is run as root or through a system context, critical for installing system-wide software.
5. Pre-Installation Check
Before downloading the installer, it checks if Office is already installed by detecting any of these apps:
- Microsoft Word
- Microsoft Excel
- Microsoft PowerPoint
- Microsoft Outlook
If any are found, it aborts the process to avoid corruption or conflicts.
6. Download and Installation
Using curl, it downloads the .pkg installer to /tmp. If successful, it proceeds with installation using the installercommand.
7. Error Handling
If the installation fails, the script captures and parses the error log. It checks for known issues such as “not enough space” and prints human-readable messages.
8. Cleanup
Whether successful or not, it deletes the downloaded installer from /tmp to keep the filesystem clean.
9. Optional Reboot
If –forcereboot is provided, it uses osascript to notify the logged-in user and schedules a reboot via shutdown.
Potential Use Cases
Scenario: Onboarding New macOS Devices
An MSP acquires a new client with 50 MacBook devices needing Office 365. Instead of manually downloading and installing Office, the IT technician deploys this script via NinjaOne’s scripting module. The script checks for pre-installed instances, downloads the latest installer, installs Office, and—if the –forcereboot parameter is set—ensures the system restarts to finalize configurations. What would take hours manually is now automated and completed in minutes.
Comparisons
Compared to other approaches, this script offers several advantages:
Method | Pros | Cons |
Manual Installation | Simple for one device | Time-consuming and error-prone |
Microsoft Endpoint Manager | Enterprise-grade features | Requires Intune license and setup |
Jamf Pro | Deep macOS integration | Cost and complexity |
This Shell Script | Lightweight, NinjaOne-compatible, scalable | Less GUI-driven customization |
This shell script sits in the sweet spot for IT teams needing automation without full-scale device management infrastructure.
FAQs
Q: Can this script be used without NinjaOne?
Yes, as long as it’s run as root on macOS. NinjaOne just simplifies deployment at scale.
Q: What happens if Office is partially installed?
The script checks for key Office apps and stops if any are found. Manual cleanup may be needed in such cases.
Q: Does this work on Intel and Apple Silicon Macs?
Yes, the Microsoft installer is universal. This script doesn’t explicitly differentiate, but the installer handles it.
Q: Can I customize which Office apps are installed?
This script uses the default installer, which installs the full suite. For custom configurations, a different .pkg or post-install script would be needed.
Implications
Deploying software through scripts carries both power and risk. On the positive side, it ensures consistency, reduces human error, and saves time. However, executing as root also poses potential risks—especially if scripts are modified or malicious. Ensuring proper validation, logging, and scope control is critical. Automating Office 365 installation aligns with IT security principles by reducing manual intervention and standardizing endpoint configuration.
Recommendations
- Test in a Lab First: Always validate the script on a test machine before production deployment.
- Use NinjaOne’s Custom Fields: Dynamically control reboot behavior or track installation status across devices.
- Monitor Logs: Integrate logging mechanisms if scaling this script further.
- Update Periodically: Check for changes in Microsoft’s download URL or installation behavior.
Final Thoughts
For IT professionals managing macOS devices, automating the installation of core software like Office 365 is a huge win. This shell script provides a smart, efficient solution tailored for environments using NinjaOne. By combining native macOS scripting with NinjaOne’s automation capabilities, IT teams can achieve rapid, scalable deployments with minimal friction. Whether you’re onboarding a new client or refreshing an existing fleet, this approach empowers you to maintain consistency, reduce overhead, and focus on strategic IT initiatives.