Over the years Office has had different install processes especially when it comes to enterprise deployments. I still remember the days we used to use the Office Customization Tool but with Office 365, and the introduction the Office Deployment Toolkit, the process has become a lot easier since then. That said, having done over a thousands of Office deployments, I have realized that legacy artifacts of of older office version uninstalls still continued to remain. We would presume that the uninstall would have taken out all the artifacts which includes files, folder, registries and others but sadly that was not the case. What that leads to is a diluted experience for the customer since they no longer have a clean product without any legacy files affecting it. At this point, I feel like an archaeologist, since my team and I have to manually dig up and clean up these artifacts. Another thing I have noticed is that users can install office from multiple sources which leads to different configurations such as:

  1. Non managed configuration with SCCM
  2. Incorrect Office channel
  3. Incorrect version cadence
  4. Incorrect update cadence
  5. Incorrect combination of products

When it comes to enterprise environments, setting a standard and being homogeneous is the key to a low ticket or low office crash environment. Who said the motto statement was “Happy Wife, Happy Life”? I would like to change that to “Happy Office, Happy Life”. Sorry had to throw some humor in there.

I will explain below on how the basic install of Office 365 ODT works and then incorporate the components I used to ensure that we have a scrubbed and stable version of office across all enterprise users. Do keep in mind that Office 365 has user based licensing so the activation’s are based on the users logging in on their devices which then auto registers it with the Office portal. The users can always review their devices by logging into portal.office.com.

Basic ODT Installation Steps:

Setup file for Office Deployment Toolkit: https://www.microsoft.com/en-us/download/details.aspx?id=49117&WT

This link will allow you to download the office deployment tool which will than decompress into the setup.exe and provide you with a sample configuration file.

The core command for deployment is as below but we renamed our configuration file to Install_Office365_x86_SemiAnnual.xml. This was done to segregate the x86 and x64 installer configuration files along with the channel of our choice. We also try to limit the x64 install base to limited number of users that work on high data sets as the x86 has more application compatibility with other applications over the x64 version.

setup.exe /configure Install_Office365_x86_SemiAnnual.xml

Preparing the Configuration File

XML Editor for configuration file: https://officedev.github.io/Office-IT-Pro-Deployment-Scripts/XmlEditor.html

Once on the site you can pick the values using the GUI and create your own configuration file. Our requirements were defined as:

  1. x86 version
  2. Semi-Annual Channel
  3. Enable Force Upgrade
  4. English as the primary language
  5. Exclude Lync(We use Slack in our environment), groove and Publisher(The lesser the products, the quicker the install)
  6. SCCM to control the updates
  7. We did not want users to update on their own
  8. We wanted a popup while the installation was in progress (Over the months we realize since Office is a core productivity tool, users try to open and work on office during the install phase)
  9. All office applications need to be shutdown so the install can complete successfully.(This was done so we can ensure SCCM enforcement)
  10. Enable logging to a certain path
  11. No shared licensing as the target machines had primary users and were not shared.
  12. We wanted to pin icons to the taskbar

Later in the article, I will walk through the process of scrubbing older versions but starting 16th of June 2018 Microsoft introduced MSI scrubbing which would allow users not to have to reinstall Visio and Project after they attempted the install if they had either of the 2 products from 2015 or 2016 Click to Run edition installers. Want to say a big thanks to Jonathan Alexander for bringing this to my attention.

Configuration file (This is what we use based on our needs):

<Configuration>
<Add OfficeClientEdition=”32″ Channel=”Broad” OfficeMgmtCOM=”TRUE” ForceUpgrade=”TRUE”>
<Product ID=”O365ProPlusRetail”>
<Language ID=”en-us”/>
<ExcludeApp ID=”Lync”/>
<ExcludeApp ID=”Groove”/>
<ExcludeApp ID=”Publisher”/>
</Product>
</Add>
<Updates Enabled=”FALSE” Channel=”Broad”/>
<Display Level=”Full” AcceptEULA=”TRUE”/>
<Logging Level=”Standard” Path=”%temp%”/>
<Property Name=”FORCEAPPSHUTDOWN” Value=”TRUE”/>
<Property Name=”SharedComputerLicensing” Value=”0″/>
<Property Name=”PinIconsToTaskbar” Value=”TRUE”/>
<RemoveMSI>
<IgnoreProduct ID=”VisPro” />
<IgnoreProduct ID=”PrjPro” />
<IgnoreProduct ID=”VisStd” />
<IgnoreProduct ID=”PrjStd” />
</RemoveMSI>
</Configuration>

In the event you chose another channel feel free to refer to list below and change the channel accordingly. Also keep in mind this has to be updated at 2 places in the above configuration, once in the Office Client Channel and the other in the Updates Channel.

  1. Channel=”Monthly”
  2. Channel=”Broad”
  3. Channel=”Targeted”

Once changed the registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\ClickToRun\Configuration\CDNBaseUrl will reflect to the respective CDNURL paths post deployment:

  1. Monthly Channel (formerly Current Channel):
    CDNBaseUrl = http://officecdn.microsoft.com/pr/492350f6-3a01-4f97-b9c0-c7c6ddf67d60

  2. Semi-Annual Channel (formerly Deferred Channel):
    CDNBaseUrl = http://officecdn.microsoft.com/pr/7ffbc6bf-bc32-4f92-8982-f9dd17fd3114
  3. Monthly Channel (Targeted)(formerly First Release for Current Channel):
    CDNBaseUrl = http://officecdn.microsoft.com/pr/64256afe-f5d9-4f86-8936-8840a6a4f5be
  4. Semi-Annual Channel (Targeted) (formerly First Release for Deferred Channel):
    CDNBaseUrl = http://officecdn.microsoft.com/pr/b8f9b850-328d-4355-9145-c59439a0c4cf

Note: Channel names changed in Sept 2017 and the Microsoft article can be found here.

Scrubbing Process

We amended the script to have 3 more components apart from the ODT execution:

  1. Track the scrubs within the registry
  2. Scrub all the pre Office 2016 components
  3. Update the Group Policy so the application can register itself to the portal

Core CMD file for SCCM Packaging

Based on the updated requirement, our new command set is updated as below and can be put into a .cmd file to simplify the SCCM deployment called Install_Office365_x86.cmd. This file can be downloaded here.

Powershell.exe -ExecutionPolicy Bypass -File ".\ScrubFlag.ps1"
Powershell.exe -ExecutionPolicy Bypass -File ".\Remove-PreviousOfficeInstalls.ps1"
setup.exe /configure Install_Office365_x86.xml
GPUPDATE /FORCE

Scrub Flag File

The Scrub flag file can be downloaded here.

######Get date and time
$datetime = Get-Date -Format 'MM\/dd\/yyyy HH:mm:ss'
$date = Get-Date -Format 'MM\/dd\/yyyy'
$time = Get-Date -Format 'HH:mm:ss'

######Initialize variable values
#Last Attempt Variables
$registryPath1 = "REGISTRY::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\OfficeScrub\ScrubConfiguration"
$namedate1 = "LastAttemptedDate"
$nametime1 = "LastAttemptedTime"
$date1 = $date
$time1 = $time

#First Attempt Variables
$registryPath2 = $registryPath1
$namedate2 = "FirstAttemptedDate"
$nametime2 = "FirstAttemptedTime"
$date2 = $date
$time2 = $time

#Scrub Count Variables
$registryPath3 = $registryPath1
$Name3 = "ScrubCounts"
$Value3 = 1

#Scrub Attempt Variables
$registryPath4 = $registryPath1
$Name4 = "ScrubAttempt"
$Value4 = "True"

IF(!(Test-Path $registryPath1))
{
#Create Path
New-Item -Path $registryPath1 -Force | Out-Null

New-ItemProperty -Path $registryPath1 -Name $namedate1 -Value $date1 -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $registryPath1 -Name $nametime1 -Value $time1 -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $registryPath2 -Name $namedate2 -Value $date2 -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $registryPath2 -Name $nametime2 -Value $time2 -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $registryPath3 -Name $name3 -Value $value3 -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $registryPath4 -Name $name4 -Value $value4 -PropertyType STRING -Force | Out-Null
}
ELSE 
{
New-ItemProperty -Path $registryPath1 -Name $namedate1 -Value $date1 -PropertyType STRING -Force | Out-Null
New-ItemProperty -Path $registryPath1 -Name $nametime1 -Value $time1 -PropertyType STRING -Force | Out-Null
$GetCurrentScrubCount = (Get-ItemProperty -Path $registryPath3 -Name ScrubCounts).ScrubCounts
$value3 = $GetCurrentScrubCount + 1
new-ItemProperty -Path $registryPath3 -Name $name3 -Value $value3 -PropertyType DWORD -Force | Out-Null
New-ItemProperty -Path $registryPath4 -Name $name4 -Value $value4 -PropertyType STRING -Force | Out-Null
}

Scrub File to Remove Previous Versions of Office

The Scrub file can be downloaded here. The screen below shows the configuration we used within our setup. Note that the $Remove2016Installs has been set to $false.

[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$RemoveClickToRunVersions = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Remove2016Installs = $false,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Force = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$KeepUserSettings = $false,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$KeepLync = $false,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$NoReboot = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Quiet = $true,

[Parameter()]
[ValidateSet("AllOfficeProducts","MainOfficeProduct","Visio","Project","Lync")]
[string[]]$ProductsToRemove = "AllOfficeProducts",

[Parameter()]
[ValidateSet("O365ProPlusRetail","O365BusinessRetail","VisioProRetail","ProjectProRetail", "SPDRetail", "VisioProXVolume", "VisioStdXVolume", 
"ProjectProXVolume", "ProjectStdXVolume", "InfoPathRetail", "SkypeforBusinessEntryRetail", "LyncEntryRetail", "AccessRuntimeRetail")]
[string]$C2RProductsToRemove = "O365ProPlusRetail",

[Parameter()]
[string]$LogFilePath
)

$validProductIds = @("O365ProPlusRetail","O365BusinessRetail","VisioProRetail","ProjectProRetail", "SPDRetail", "VisioProXVolume", "VisioStdXVolume", 
"ProjectProXVolume", "ProjectStdXVolume", "InfoPathRetail", "SkypeforBusinessEntryRetail", "LyncEntryRetail", "AccessRuntimeRetail")

Note: This is a small subset to the entire file but displayed since it is the critical section where user choices can be modified.

Uninstall File to Remove all Versions and components of Office

The uninstall file can be downloaded here. The screen below shows the configuration we used within our setup. Note that the $Remove2016Installs has been set to $True.

[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$RemoveClickToRunVersions = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Remove2016Installs = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Force = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$KeepUserSettings = $false,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$KeepLync = $false,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$NoReboot = $true,

[Parameter(ValueFromPipelineByPropertyName=$true)]
[bool]$Quiet = $true,

[Parameter()]
[ValidateSet("AllOfficeProducts","MainOfficeProduct","Visio","Project","Lync")]
[string[]]$ProductsToRemove = "AllOfficeProducts",

[Parameter()]
[ValidateSet("O365ProPlusRetail","O365BusinessRetail","VisioProRetail","ProjectProRetail", "SPDRetail", "VisioProXVolume", "VisioStdXVolume", 
"ProjectProXVolume", "ProjectStdXVolume", "InfoPathRetail", "SkypeforBusinessEntryRetail", "LyncEntryRetail", "AccessRuntimeRetail")]
[string]$C2RProductsToRemove = "O365ProPlusRetail",

[Parameter()]
[string]$LogFilePath
)

$validProductIds = @("O365ProPlusRetail","O365BusinessRetail","VisioProRetail","ProjectProRetail", "SPDRetail", "VisioProXVolume", "VisioStdXVolume", 
"ProjectProXVolume", "ProjectStdXVolume", "InfoPathRetail", "SkypeforBusinessEntryRetail", "LyncEntryRetail", "AccessRuntimeRetail")

Note: This is a small subset to the entire file but displayed since it is the critical section where user choices can be modified.

SCCM Deployment

The core steps to setup this deployment can be found here. The only new modification would be that the files listed above would be decompressed to  the same content location and the installation program and uninstall program strings would be updated.

Installation Program
"Office36516x86Installer.cmd"
Installation Program
Powershell.exe -ExecutionPolicy Bypass -File ".\Remove-PreviousOfficeInstalls.ps1"

Make sure you redistribute the content again and let it replicate. At this point you should be all set.

Conclusion

If you would like to use the installer as is listed in the application, the compressed file can be downloaded here. After using the above process, we have seen a drastic improvement in User Experience. I would be happy to hear if you were able to use this process and if it helps your environment.

Note: Do not forget to amend your ADR’s and Software Update Groups to adjust for the new channel updates. I will also call out a caveat that add-ins will additionally cause other issues and hence I am building this without any included.