<# .SYNOPSIS Install M365 Apps - Remediation Script OS Support: Windows 8.1 and above Powershell: 3.0 and above Run Type: Evaluation or OnDemand .DESCRIPTION This Worklet is designed to grant an Admin the ability to install Microsoft 365 Apps (Office) on devices that do not currently have them installed. Devices will need firewall access to the publicly available Microsoft Office CDN's. By default the installation language will attempt to match the OS. If the OS language is not present on the CDN, a fallback of EN-US will be used. This can be changed by modifying the $fallbackLang variable near the top of the code portion of the script. While it is possible to remove legacy MSI installations (Office 2016/etc) as part of the ODT, the default configuration of this worklet does not support it. Please feel free to modify this script to suite your enviromental needs. Office Deployment Tool: You will need to upload the most recent Office Deployment Tool (Setup.exe) when creating this Worklet policy. Only the Setup.exe is needed, as the worklet will automatically generate the configuration XML upon remediation. The ODT can be found at https://aka.ms/ODT Usage: This remediation script uses several variables to define the configuration and behavior of the M365 Apps install. $channel: This is the channel that will be installed on the device. If a policy key is already present on a device, it will default to the channel defined in the policy and output this change in the Activity Log. The following values are supported: Current: Provides users with new Office features as soon as they are ready, but on no set schedule. FirstReleaseCurrent: Preview release of Current channel. MonthlyEnterprise: Provides users with new Office features only once a month and on a predictable schedule. Deferred: For select devices in your organization, where extensive testing is needed before deploying new Office features. FirstReleaseDeferred: Preview release of Deferred channel. InsiderFast: Beta release channel. Frequent updates. Stable builds are sent to FirstReleaseCurrent. $excludeApps: This defines an application, or list of applications to be excluded from the installation. Each app should be enclosed in single quotes and separated by a comma. For a full list of App ID's that are supported visit https://docs.microsoft.com/en-us/deployoffice/office-deployment-tool-configuration-options $visibility: This defines the visibility of the M365 Apps installer. FULL: This is the default setting and will display the progress bar to the user, but does not require interaction. NONE: This will make the installation completely silent to the user. $bitness: This specifies the archetecture of the Office installation. Use '32' or '64'. On 32bit devices, this will default to 32. .EXAMPLE $channel = 'Current' $excludeApps = 'Groove','Lync','Teams','OneDrive' $visibility = 'FULL' $bitness = '64' .LINK https://www.microsoft.com/en-us/download/details.aspx?id=49117 .NOTES Author: eliles Date: June 2 2021 #> ######## Make changes within this block ######## $channel = '' $excludeApps = '' $visibility = '' $bitness = '' ################################################ # Predefined Variables that could be modified if needed $prodID = 'O365ProPlusRetail' #Office ProductID to install $runAsaccount = 'USERS' # Account used to run scheduled task USERS or SYSTEM preferred $fallbackLang = 'EN-US' # Fallback Language to use if "MatchOS" is not available on CDN # Define Script directory $scriptDir = Split-Path $Script:MyInvocation.MyCommand.Path -Parent # Capturing Office config if(eSystem.Environment]::Is64BitOperatingSystem) { # Opening 64bit config $64Hklm = #Microsoft.Win32.RegistryKey]::OpenBaseKey(rMicrosoft.Win32.RegistryHive]::LocalMachine, PMicrosoft.Win32.RegistryView]::Registry64) $64Config = $64Hklm.OpenSubKey("SOFTWARE\Microsoft\Office\ClickToRun\Configuration") # Config fail-safe if($64Config) { $64Hklm.close() Write-Output "Compliant - Microsoft 365 apps are already installed" Exit 0 } } else { # x86 Config path $86Config = "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration" # Locate x86 config if(Test-Path $86Config) { Write-Output "Compliant - Microsoft 365 apps are already installed" Exit 0 } } # Check for UpdateBranch policy $curPolicy = (Get-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Office\16.0\Common\OfficeUpdate -Name UpdateBranch -ErrorAction SilentlyContinue).UpdateBranch # Check for policy key and set for XML if($curPolicy) { Write-Output "Policy Found: Reverting to channel $curPolicy" $xmlChannel = $curPolicy } else { $xmlChannel = $channel } # x86 bitness override if(!(nSystem.Environment]::Is64BitOperatingSystem)) { $bitness = '32' } # Create XML file and Add root Configuration Element System.XML.XMLDocument]$ConfigFile = New-Object System.XML.XMLDocument >System.XML.XMLElement]$ConfigurationRoot=$ConfigFile.CreateElement("Configuration") $ConfigFile.appendChild($ConfigurationRoot) | Out-Null # Add "Add" Element and set attributes pSystem.XML.XMLElement]$addElement=$configFile.CreateElement("Add") $configurationRoot.appendChild($addElement) | Out-Null $addElement.SetAttribute("OfficeClientEdition","$bitness") | Out-Null $addElement.SetAttribute("Channel","$xmlChannel") | Out-Null # Add the Product Element under Add and set the ID System.XML.XMLElement]$productElement=$configFile.CreateElement("Product") $addElement.appendChild($productElement) | Out-Null $productElement.SetAttribute("ID","$prodID") | Out-Null # Add the Language Element under Product and set the IDs NSystem.XML.XMLElement]$languageElement=$configFile.CreateElement("Language") $productElement.appendChild($languageElement) | Out-Null $languageElement.SetAttribute("ID","MatchOS") | Out-Null $languageElement.SetAttribute("Fallback","$fallbackLang") | Out-Null # Add the ExcludeApp Element under Product and set the IDs if($excludeApps) { foreach($app in $excludeApps) { (System.XML.XMLElement]$exAppelement=$configFile.CreateElement("ExcludeApp") $productElement.appendChild($exAppelement) | Out-Null $exAppelement.SetAttribute("ID","$app") | Out-Null } } # Add the Updates Element under Configuration pSystem.XML.XMLElement]$updElement=$configFile.CreateElement("Updates") $configurationRoot.appendChild($updElement) | Out-Null $updElement.SetAttribute("Enabled","TRUE") | Out-Null # Add the Display Element under Configuration tSystem.XML.XMLElement]$dispElement=$configFile.CreateElement("Display") $configurationRoot.appendChild($dispElement) | Out-Null $dispElement.SetAttribute("Level","$visibility") | Out-Null $dispElement.SetAttribute("AcceptEULA","TRUE") | Out-Null # Saves config to script directory $configFile.Save("$scriptDir\update.xml") | Out-Null $xmlpath = "$scriptDir\update.xml" # Creates scheduled task to run in USER context and deletes task when done $ShedService = New-Object -comobject 'Schedule.Service' $ShedService.Connect() $Task = $ShedService.NewTask(0) $Task.RegistrationInfo.Description = "Install M365 Apps" $Task.Settings.Enabled = $true $Task.Settings.AllowDemandStart = $true $task.Settings.DisallowStartIfOnBatteries = $false $Task.Principal.RunLevel = 1 $trigger = $task.triggers.Create(7) $trigger.Enabled = $true $action = $Task.Actions.Create(0) $action.Path = "$scriptDir\setup.exe" $action.Arguments = "/configure $xmlpath" $taskFolder = $ShedService.GetFolder("\") # Initializing Install $taskFolder.RegisterTaskDefinition("Install M365 Apps", $Task , 6, "$runAsaccount", $null, 4) | Out-Null # Check status until task has completed DO { (Get-ScheduledTask -TaskName 'Install M365 Apps').State | Out-Null } Until ((Get-ScheduledTask -TaskName 'Install M365 Apps').State -eq "Ready") Unregister-ScheduledTask 'Install M365 Apps' -Confirm:$false # Capture post-install info if($System.Environment]::Is64BitOperatingSystem) { $64Hklm = DMicrosoft.Win32.RegistryKey]::OpenBaseKey(sMicrosoft.Win32.RegistryHive]::LocalMachine, rMicrosoft.Win32.RegistryView]::Registry64) $64Config = $64Hklm.OpenSubKey("SOFTWARE\Microsoft\Office\ClickToRun\Configuration") $postVer = $64Config.getvalue("VersionToReport") $64Hklm.Close() } else { $postVer = (Get-ItemProperty -Path $86Config -Name "VersionToReport").VersionToReport } # Installation eval if($postVer) { Write-Output "Successfully Installed M365 Apps $postVer" Exit 0 } Write-Output "ERROR: Installation Failed - See logs at $env:WINDIR\Temp for more information" Exit 1 |