#*********************************************************************** # # Copyright (c) 2018 Microsoft Corporation. All rights reserved. # # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # #********************************************************************** # region Parameter Param( [Parameter(Mandatory = $false)] [String]$tenant ) #endregion Parameter #region Functions function Clear-OlkAutodiscoverCache { $result = 0 try { # get Outlook AutoDiscover xml cache files $cachePath = $env:LOCALAPPDATA + "\Microsoft\Outlook\16" $autoDFiles = Get-ChildItem -Path $cachePath -Filter *.xml -Force -ErrorAction Stop $autoDFilesCount = ($autoDFiles | Measure-Object -ErrorAction Stop).Count } catch { Publish-EventLog -EventId 230 -EntryType Error -Message ($global:resources["EventID-230"]) } if ($autoDFilesCount -gt 0) { Publish-EventLog -EventId 136 -EntryType Information -Message ([string]::Format($global:resources["EventID-136"], $autoDFilesCount)) # Remove files $filesRemoved = 0 $retries = 0 while (($filesRemoved -lt $autoDFilesCount) -and ($retries -le 2)) { foreach ($file in $autoDFiles) { try { if (Test-Path $($cachePath + "\" + $file.Name) ) { Remove-Item $($cachePath + "\" + $file.Name) -Force -Confirm:$false -ErrorAction Stop $filesRemoved++ } } catch { Publish-EventLog -EventId 231 -EntryType Error -Message ([string]::Format($global:resources["EventID-231"], $($file.Name))) } } $retries++ if (($filesRemoved -lt $autoDFilesCount) -and ($retries -le 2)) { Start-Sleep -Seconds 5 } elseif ($filesRemoved -lt $autoDFilesCount) { Publish-EventLog -EventId 234 -EntryType Error -Message ($global:resources["EventID-234"]) $result = 2 } else { $result = 1 } } Publish-EventLog -EventId 137 -EntryType Information -Message ([string]::Format($global:resources["EventID-137"], $filesRemoved)) } return $result } function Clear-OLSFederationProvider { try { $OLSFederationProvider = Get-Item -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Licensing\OLSFederationProvider\" -ErrorAction Stop | Select-Object -ExpandProperty Property } catch { return $false } if ($OLSFederationProvider) { # get property values try { $OLSFederationProviderValues = Get-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Licensing\OLSFederationProvider\" -ErrorAction Stop } catch { return $false } if ($OLSFederationProviderValues) { $clearedOLSFederationProviders = 0 foreach ($key in $OLSFederationProvider) { if ($OLSFederationProviderValues.$key -like "*microsoftonline.de*") { try { Set-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Licensing\OLSFederationProvider\" -Name $key -Value "" -Type String -ErrorAction Stop $clearedOLSFederationProviders++ } catch { Publish-EventLog -EventId 233 -EntryType Error -Message ($global:resources["EventID-233"]) return $false } } } Publish-EventLog -EventId 139 -EntryType Information -Message ([string]::Format($global:resources["EventID-139"], $clearedOLSFederationProviders)) return $true } } } function Get-CleanupStatus { Param( [parameter(Mandatory = $true)] [String] $key ) try { $occtConfig = Get-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -ErrorAction Stop } catch { # OCCT hive does not exist. It is only required for custom configuration, ignore. return $false } if ($occtConfig) { if ($occtConfig.$key -and $occtConfig.$key -gt 0) { return $true } } return $false } function Get-MaxOffset { $defaultMaxOffset = 900 try { $occtConfig = Get-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -ErrorAction Stop } catch { # no custom max offset defined, use default max offset return $defaultMaxOffset } if ($occtConfig) { try { [int]$offset = $occtConfig.MaxOffset } catch { # Offset is not numeric (int), use default value return $defaultMaxOffset } if ($offset -gt 0 -and $offset -lt 59) { Publish-EventLog -EventId 120 -EntryType Information -Message ([string]::Format($global:resources["EventID-120"], $offset.tostring())) try { $offsetSec = ($offset * 60) return $offsetSec } catch { return $defaultMaxOffset } } } return $defaultMaxOffset } function Get-SleepTime { Param( [parameter(Mandatory = $true)] [Int32] $max ) $sleepTime = Get-Random -Minimum 0 -Maximum $max -ErrorAction Stop Publish-EventLog -EventId 118 -EntryType Information -Message ([string]::Format($global:resources["EventID-118"], ($sleepTime))) return $sleepTime; } function Initialize-Resources() { $global:resources = New-Object System.Collections.Generic.Dictionary"[String,String]" # Information messages $global:resources.Add("EventID-100", "OCCT run started") $global:resources.Add("EventID-101", "Computer name: {0}{1}User-SID: {2}") $global:resources.Add("EventID-102", "Tenant: {0}") $global:resources.Add("EventID-103", "Tenant is migrated, check FederationProvider.") $global:resources.Add("EventID-104", "Tenant is not migrated.") $global:resources.Add("EventID-105", "Running Office apps found, ask user to close apps.") $global:resources.Add("EventID-106", "Reset of FederationProvider was successful.") $global:resources.Add("EventID-107", "{0} is still running.") $global:resources.Add("EventID-108", "{0} Office identities found.") $global:resources.Add("EventID-109", "Working on identity {0}") $global:resources.Add("EventID-110", "FederationProvider found: {0}") $global:resources.Add("EventID-111", "BF FederationProvider found.") $global:resources.Add("EventID-112", "FederationProvider does not point to BF.") $global:resources.Add("EventID-113", "No Office identities found.") $global:resources.Add("EventID-114", "Countdown expired, kill {0}") $global:resources.Add("EventID-115", "Updated LastRunTimeSuccess and status in registry.") $global:resources.Add("EventID-116", "Amount of internet connection tries: {0}") $global:resources.Add("EventID-117", "Client cutover is already done.") $global:resources.Add("EventID-118", "Sleep for {0} seconds to avoid throttling.") $global:resources.Add("EventID-119", "Querying OIDC/HRD document from: {0}") $global:resources.Add("EventID-120", "Found MaxOffset in registry: {0} minutes.") $global:resources.Add("EventID-121", "No Office identities found for this user.") $global:resources.Add("EventID-122", "OCCT is disabled by registry key.") $global:resources.Add("EventID-123", "ForceMode enabled, skip throttling sleep.") $global:resources.Add("EventID-124", "BF FederationProvider found in Office identity root hive.") $global:resources.Add("EventID-125", "FederationProvider found in Office identity root hive: {0}") $global:resources.Add("EventID-126", "FederationProvider in Office identity root hive does not point to BF.") $global:resources.Add("EventID-127", "FederationProvider in Office identity root hive was removed successfully.") $global:resources.Add("EventID-128", "Office app automatically reopened: {0}") $global:resources.Add("EventID-129", "Check for tenant cutover against Office HRD endpoint.") $global:resources.Add("EventID-130", "Check for tenant cutover against Azure AD OIDC endpoint.") $global:resources.Add("EventID-131", "Office identity hive removed.") $global:resources.Add("EventID-132", "Device is AAD joined.") $global:resources.Add("EventID-133", "{0} OneDrive for Business accounts found.") $global:resources.Add("EventID-134", "Set NextEmailHRDUpdate for OneDrive account {0}.") $global:resources.Add("EventID-135", "Updated NextEmailHRDUpdate for {0} of {1} OneDrive accounts. Restart OneDrive client.") $global:resources.Add("EventID-136", "{0} Outlook AutoDiscover cache files found.") $global:resources.Add("EventID-137", "{0} Outlook AutoDiscover cache files removed.") $global:resources.Add("EventID-138", "Signed out from {0} BF WAM accounts.") $global:resources.Add("EventID-139", "Cleared {0} BF OLSFederationProviders.") # Error messages $global:resources.Add("EventID-201", "User-SID could not be retrieved.") $global:resources.Add("EventID-202", "No internet connectivity present.") $global:resources.Add("EventID-203", "Existing Process already running.") $global:resources.Add("EventID-204", "Function only allows one or two parameters.") $global:resources.Add("EventID-205", "Reset of FederationProvider was not successful.") $global:resources.Add("EventID-206", "Error removing BF FederationProvider.") $global:resources.Add("EventID-207", "Failed to update LastRunTimeSuccess and status in registry.") $global:resources.Add("EventID-208", "Failed to create LastRunTimeSuccess and status in registry.") $global:resources.Add("EventID-209", "Error message: {0}") $global:resources.Add("EventID-210", "Log file could not be published.") $global:resources.Add("EventID-211", "Error closing app: {0}") $global:resources.Add("EventID-212", "Countdown expired, unable to set DialogResult.") $global:resources.Add("EventID-213", "Continue button clicked, unable to set DialogResult.") $global:resources.Add("EventID-214", "Dialog closed, unable to stop countdown.") $global:resources.Add("EventID-215", "Error checking process {0}") $global:resources.Add("EventID-216", "Error checking FederationProvider.") $global:resources.Add("EventID-217", "OIDC lookup failed. Error Message: {0}{1}") $global:resources.Add("EventID-218", "Dialog: XmlNodeReader could not be initialized.") $global:resources.Add("EventID-219", "Dialog: XamlReader could not be initialized.") $global:resources.Add("EventID-220", "Dialog: Timer could not be initialized.") $global:resources.Add("EventID-221", "Error checking root FederationProvider.") $global:resources.Add("EventID-222", "Error removing BF FederationProvider in Office identity root hive.") $global:resources.Add("EventID-223", "Removal of FederationProvider in Office identity root hive was not successful.") $global:resources.Add("EventID-224", "Office app could not be started: {0}") $global:resources.Add("EventID-225", "Setting status registry key failed.") $global:resources.Add("EventID-226", "Office identity registry hive not found.") $global:resources.Add("EventID-227", "Office identity registry hive could not be removed.") $global:resources.Add("EventID-228", "Error getting AAD Join status.") $global:resources.Add("EventID-229", "Failed to set NextEmailHRDUpdate for OneDrive account {0}.") $global:resources.Add("EventID-230", "Failed to get Outlook AutoDiscover cache files.") $global:resources.Add("EventID-231", "Failed to remove Outlook AutoDiscover cache file: {0}.") $global:resources.Add("EventID-232", "Failed to remove BF WAM accounts.") $global:resources.Add("EventID-233", "Failed to clear OLSFederationProvider.") $global:resources.Add("EventID-234", "Some Outlook AutoDiscover Cache files could not be removed.") $global:resources.Add("LogFilePath", "C:\Temp\OCCT\OCCT_{0}.xml") } function Publish-EventLog { Param( [parameter(Mandatory = $true)] [Int32] $eventId, [parameter(Mandatory = $true)] [String] $entryType, [parameter(Mandatory = $true)] [String] $message ) try { $logfile = $env:TEMP + "\OCCT.log" $EntryDay = Get-Date -Format [yyyy-MM-dd] $EntryTime = Get-Date -Format HH:mm:ss.ffff Write-Output "$EntryDay `t $EntryTime `t [$entryType] `t [$eventId] `t $message" | Out-File $logfile -Append } catch { # The script is running in a context that does not have permissions to create the OCCT Event Log category - thus the catch is empty } } function Remove-BFWamAccount { function Test-BFWAMAccount($accountProperties) { foreach ($keyvaluepair in $accountProperties) { if ($keyvaluepair.key -eq "Authority") { if ($keyvaluepair.value -like "*microsoftonline.de*") { return $true } } } return $false } function AwaitAction($WinRtAction) { $asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and !$_.IsGenericMethod })[0] $netTask = $asTask.Invoke($null, @($WinRtAction)) $netTask.Wait(-1) | Out-Null } function Await($WinRtTask, $ResultType) { $asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | Where-Object { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0] $asTask = $asTaskGeneric.MakeGenericMethod($ResultType) $netTask = $asTask.Invoke($null, @($WinRtTask)) $netTask.Wait(-1) | Out-Null $netTask.Result } if (-not [Windows.Foundation.Metadata.ApiInformation, Windows, ContentType = WindowsRuntime]::IsMethodPresent("Windows.Security.Authentication.Web.Core.WebAuthenticationCoreManager", "FindAllAccountsAsync")) { # This script is not supported on this Windows version return $false } Add-Type -AssemblyName System.Runtime.WindowsRuntime $signedOutBFAccounts = 0 try { $provider = Await ([Windows.Security.Authentication.Web.Core.WebAuthenticationCoreManager, Windows, ContentType = WindowsRuntime]::FindAccountProviderAsync("https://login.microsoft.com", "organizations")) ([Windows.Security.Credentials.WebAccountProvider, Windows, ContentType = WindowsRuntime]) $accounts = Await ([Windows.Security.Authentication.Web.Core.WebAuthenticationCoreManager, Windows, ContentType = WindowsRuntime]::FindAllAccountsAsync($provider, "d3590ed6-52b3-4102-aeff-aad2292ab01c")) ([Windows.Security.Authentication.Web.Core.FindAllAccountsResult, Windows, ContentType = WindowsRuntime]) $accounts.Accounts | ForEach-Object { if (Test-BFWAMAccount($_.Properties)) { # sign out only if it is a BF WAM account AwaitAction ($_.SignOutAsync("d3590ed6-52b3-4102-aeff-aad2292ab01c")) $signedOutBFAccounts++ } } Publish-EventLog -EventId 138 -EntryType Information -Message ([string]::Format($global:resources["EventID-138"], $signedOutBFAccounts)) return $true } catch { Publish-EventLog -EventId 232 -EntryType Error -Message ($global:resources["EventID-232"]) return $false } } function Remove-OfficeIdentityHive { $regOfficeIdentityPath = "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Identity\" try { $identity = Get-Item $regOfficeIdentityPath -ErrorAction Stop } catch { Publish-EventLog -EventId 226 -EntryType Error -Message ($global:resources["EventID-226"]) return $false } if ($identity) { try { Remove-Item $regOfficeIdentityPath -Confirm:$false -ErrorAction Stop Publish-EventLog -EventId 131 -EntryType Information -Message ($global:resources["EventID-131"]) return $true } catch { Publish-EventLog -EventId 227 -EntryType Error -Message ($global:resources["EventID-227"]) return $false } } return $false } function Reset-FederationProvider { try { $identities = Get-ChildItem -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Identity\Identities\" -ErrorAction Stop $identityCount = ($identities | Measure-Object).count Publish-EventLog -EventId 108 -EntryType Information -Message ([string]::Format($global:resources["EventID-108"], $identityCount)) } catch { $identityCount = 0 $identities = $null } if ($identityCount -ne 0) { foreach ($identity in $identities) { Publish-EventLog -EventId 109 -EntryType Information -Message ([string]::Format($global:resources["EventID-109"], $identity)) $federationProvider = $null try { $properties = Get-ItemProperty -Path "Registry::$($identity.Name)" -ErrorAction Stop $federationProvider = $properties.FederationProvider } catch { return $false } # BF Federation provider found, clean Publish-EventLog -EventId 111 -EntryType Information -Message ($global:resources["EventID-111"]) try { Publish-EventLog -EventId 110 -EntryType Information -Message ([string]::Format($global:resources["EventID-110"], $federationProvider)) Set-ItemProperty -Path "Registry::$($identity.Name)" -Name "FederationProvider" -Value "Error" -ErrorAction Stop return $true } catch { Publish-EventLog -EventId 206 -EntryType Error -Message ($global:resources["EventID-206"]) return $false } } } else { # no office identities Publish-EventLog -EventId 113 -EntryType Information -Message ($global:resources["EventID-113"]) return $false } return $false } function Reset-RootFederationProvider { if (CheckRootFederationProvider) { # BF Federation provider found, clean Publish-EventLog -EventId 124 -EntryType Information -Message ($global:resources["EventID-124"]) try { $identity = Get-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Identity\" -ErrorAction Stop $federationProvider = $identity.FederationProvider Publish-EventLog -EventId 125 -EntryType Information -Message ([string]::Format($global:resources["EventID-125"], $federationProvider)) Remove-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Office\16.0\Common\Identity\" -Name "FederationProvider" -ErrorAction Stop return $true } catch { Publish-EventLog -EventId 222 -EntryType Error -Message ($global:resources["EventID-222"]) return $false } } else { Publish-EventLog -EventId 126 -EntryType Information -Message ($global:resources["EventID-126"]) } return $false } function Set-CleanupStatus { Param( [parameter(Mandatory = $true)] [String] $key, [parameter(Mandatory = $false)] [int] $result = 1, [parameter(Mandatory = $false)] [bool] $SetLastRuntime = $false ) try { $occtConfig = Get-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -ErrorAction Stop } catch { # OCCT hive does not exist. Create it. try { New-Item -Path "Registry::HKCU\Software" -Name OCCT -Force if ($SetLastRuntime) { $time = ([datetime]::UtcNow).tostring("yyyy-MM-dd HH:mm:ss tt") Set-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -Name $key -Value $time -Type String -ErrorAction Stop } else { Set-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -Name $key -Value $result -Type DWord -ErrorAction Stop } } catch { Publish-EventLog -EventId 225 -EntryType Error -Message ($global:resources["EventID-225"]) } } if ($occtConfig) { try { if ($SetLastRuntime) { $time = ([datetime]::UtcNow).tostring("yyyy-MM-dd HH:mm:ss tt") Set-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -Name $key -Value $time -Type String -ErrorAction Stop } else { Set-ItemProperty -Path "Registry::HKCU\Software\OCCT\" -Name $key -Value $result -Type DWord -ErrorAction Stop } } catch { Publish-EventLog -EventId 225 -EntryType Error -Message ($global:resources["EventID-225"]) } } } function Show-UserPrompt { Param( [parameter(Mandatory = $true)] [PSObject] $runningApps ) [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null Add-Type -AssemblyName 'PresentationFramework' # get display language try { $lang = $PSUICulture } catch { # use en-US as fallback if user language cannot be retrieved $lang = "en-US" } if ($lang -eq "de-DE") { [xml]$WindowXaml = @'