Resetting all users’ start menu for Office 2019

With the plan to move to Office 2019, I wanted to be able automatically reset the start menu for all users on a computer. In my powershell script that installs Office 2019, I also have it search all local users profiles for their start menu layout and replace old Office 2016 entries with Office 2019.

If ($null -eq (Get-PSDrive HKU -ErrorAction SilentlyContinue)) { New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS | Out-Null }
$LoggedOnUsers = (Get-CimInstance -Class Win32_LoggedOnUser | Select-Object Antecedent -Unique).Antecedent.Name
$HiveList = "HKLM:\SYSTEM\CurrentControlSet\Control\hivelist"
$Hives = Get-Item -Path $HiveList | Select-Object -ExpandProperty Property | Where-Object {$_ -like "*\REGISTRY\USER\S-*" -And $_ -notlike "*_Classes*"}
$Users = (Get-ChildItem $env:SystemDrive\Users -Force -Exclude 'All Users','Public' | Where-Object {$_.PSIsContainer}).Name
ForEach ($Name in $Users) {
	$ntuserpath = "$env:SystemDrive\Users\$Name\ntuser.dat"
	If (Test-Path $ntuserpath) {
		$ntuserpath = '"' + $ntuserpath + '"'
		$RegPath = $null
		If ($LoggedOnUsers -like "*$Name*") {
			# User is logged in, will get HKU path
			ForEach ($Hive in $Hives) {
				$HiveValue = (Get-ItemPropertyValue -Path "$HiveList" -Name "$Hive") -Replace "\\Device\\HarddiskVolume[0-9]*","$env:SystemDrive"
				If ($HiveValue -like "*\$Name\*") { $RegPath = $Hive.ToUpper().Replace("\REGISTRY\USER","HKU"); break }
			}
		}
		If ($null -eq $RegPath) { $RegPath = "HKLM\ntuser"; $runreg = Start-Process -FilePath REG.exe -ArgumentList "LOAD $RegPath $ntuserpath" -PassThru -Wait -WindowStyle Hidden }
		$PSRegPath = $RegPath.Replace("\",":\")
		
		# Reset TileStore for Start Menu refresh
		If ($Name -notlike "Default*") {
			$LocalLayout = "$env:SystemDrive\Users\$Name\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml"
			If ((Test-Path "$LocalLayout") -And ($FixOfficeLayout)) {
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\WINWORD.EXE","Microsoft.Office.WINWORD.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\EXCEL.EXE","Microsoft.Office.EXCEL.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\POWERPNT.EXE","Microsoft.Office.POWERPNT.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\MSPUB.EXE","Microsoft.Office.MSPUB.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\ONENOTE.EXE","Microsoft.Office.ONENOTE.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\OUTLOOK.EXE","Microsoft.Office.OUTLOOK.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("Microsoft.Office.OUTLOOK.EXE.16","Microsoft.Office.OUTLOOK.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace('Version="2"','Version="1"') | Set-Content "$LocalLayout"
				$RegTileStore = Get-ChildItem -Path "$PSRegPath\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudStore\Store\Cache\DefaultAccount" -ErrorAction SilentlyContinue | Where-Object {$_.Name -like "*start.tilegrid*curatedtilecollection*"} | Select -ExpandProperty Name -First 1
				If ($null -ne $RegTileStore) {
					$RegTileStore = $RegTileStore.Substring($RegTileStore.IndexOf("\DefaultAccount\")+16)
					Remove-Item -Path "$PSRegPath\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudStore\Store\Cache\DefaultAccount\$($RegTileStore)" -Force -Recurse -ErrorAction SilentlyContinue
				}
			}
			If ($RegPath -eq "HKLM\ntuser") {
				[gc]::collect()
				$runreg = Start-Process -FilePath REG.exe -ArgumentList "UNLOAD $RegPath" -PassThru -Wait -WindowStyle Hidden; Write-Output "$($runreg.ExitCode)"
			}
		}
	}
} # End of ForEach Users

This searches for all basic Office 2016 application entries in LayoutModification.xml and puts in the Office 2019 entries. It then clears out the registry key that will force the start menu refresh when each user logs back in.

Testing Windows 10 in-place upgrade task sequence

I’ve been trying to make smaller/quicker task sequences for doing Windows 10 in-place upgrades. I wanted to be able to get almost everything done hidden in the background (Setting variable TSDisableProgressUI to True) before the first restart would even prompt.

I noticed by using the step ‘Upgrade Operating System’ it will automatically reboot when the setup requests. I tried increasing the variable SMSTSRebootTimeout so the first prompt would give users enough time if someone was logged on. The problem with this is that there will be more than one prompt which isn’t necessary after the first as the upgrade isn’t done yet. Read more

Windows Defender Offline and WinRE

By default I always use “reagentc /disable” so users with local administrative rights don’t do a reset of their whole computer.  With Windows 10 1703 now out and Defender’s Offline scan a bit more obvious in the Security Center, I noticed it wasn’t working at all.

Advanced scans

I wasn’t realizing that everything is all tied together with Windows RE. My current option seems to just have a PowerShell script to quickly run “reagentc /enable; Start-MpWDOScan” and manually run “reagentc /disable” after it starts back up.