Obs/scripts/SetupHelper.psm1
|
##------------------------------------------------------------------ ## <copyright file="SetupHelper.psm1" company="Microsoft"> ## Copyright (C) Microsoft. All rights reserved. ## </copyright> ##------------------------------------------------------------------ #region Constants $global:ObsArtifactsPaths = @{ ## PackageContentPath value will be set by Set-ObsPackageContentPath function. ObsExtSetupScripts = @{ PackageName = "Microsoft.AzureStack.Observability.ObsExtSetupScripts" ChildPath = "content" PackageContentPath = $null } GMA = @{ PackageName = "Microsoft.AzureStack.Observability.GenevaMonitoringAgent" ChildPath = "content" PackageContentPath = $null } TestObservability = @{ PackageName = "Microsoft.AzureStack.Observability.TestObservability" ChildPath = "content" PackageContentPath = $null } ObservabilityDeployment = @{ PackageName = "Microsoft.AzureStack.Observability.ObservabilityDeployment" ChildPath = $null ## Explicitly set to null as the package has files in content as well as lib folder. PackageContentPath = $null } FDA = @{ PackageName = "Microsoft.AzureStack.Observability.FDA.FleetDiagnosticsAgent" ChildPath = "content\FleetDiagnosticsAgent" PackageContentPath = $null } MAWatchDog = @{ PackageName = "Microsoft.AzureStack.Solution.Diagnostics.HCIWatchdog" ChildPath = "MAWatchdog" PackageContentPath = $null } SBCClient = @{ PackageName = "Microsoft.AzureStack.Services.SupportBridgeController.Client" ChildPath = $null PackageContentPath = $null } ObservabilityAgent = @{ PackageName = "Microsoft.AzureStack.SupportBridge.LogCollector.WinService" ChildPath = "lib" PackageContentPath = $null } UtcExporter = @{ PackageName = "Microsoft.Windows.Utc.Exporters.GenevaExporter" ChildPath = "runtimes\win10-x64\native" PackageContentPath = $null } NetworkObservability = @{ PackageName = "Microsoft.AS.Network.Observability.Extension" ChildPath = "content" PackageContentPath = $null } WatsonAgent = @{ PackageName = "AzureEdgeWatsonAgent-retail-amd64" ChildPath = "lib\native" PackageContentPath = $null } } #endregion Constants #region Functions #region Pre-installation validation functions function Assert-NoObsGMAProcessIsRunning { param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile ## Step 1: Check if MAWatchdog is running? If yes, then stop and unregister the service. if (Get-Service $MiscConstants.ObsServiceDetails.WatchdogAgent.Name -ErrorAction SilentlyContinue) { ## Unregister watchdog agent service Write-Log "[$functionName] Found already running watchdog service. Trying to stop and unregister the service." -LogFile $LogFile Stop-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.WatchdogAgent.Name ` -LogFile $LogFile $sleepSeconds = 30 Write-Log "[$functionName] Letting the process sleep for $sleepSeconds second(s), so that any child processes of the service can shutdown gracefully." -LogFile $logFile Start-Sleep -Seconds $sleepSeconds Unregister-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.WatchdogAgent.Name ` -LogFile $LogFile } else { Write-Log "[$functionName] No registered watchdog service found." -LogFile $LogFile } ## Step 2: Now check if MA host processes from a TelemetryAndDiagnostics extension are still running or not. $stoppedProcesses = $false $runningMAHostProcesses = @() $runningMAHostProcesses += Get-Process ` -Name $MiscConstants.GMAHostProcessNameRegex ` -ErrorAction SilentlyContinue ` | Where-Object { $_.Path -match $MiscConstants.GMAHostProcessFullPathRegex -and (-not $_.HasExited) } Write-Log "[$functionName] Count of already running MonAgentHost process = $($runningMAHostProcesses.Count)." -LogFile $LogFile foreach ($hostProcess in $runningMAHostProcesses) { ## Step 3: If yes, stop them $procId = $hostProcess.Id $procName = $hostProcess.Name $procPath = $hostProcess.Path Write-Log "[$functionName] $($hostProcess | Stop-Process -Force -PassThru | Out-String)" Write-Log "[$functionName] Stopped the process $procName with id: $procId and path: $procPath." $stoppedProcesses = $true } if ($stoppedProcesses) { $sleepSeconds = 60 Write-Log "[$functionName] Sleeping for $sleepSeconds second(s), so that child GMA processes can shutdown gracefully." -LogFile $logFile Start-Sleep -Seconds $sleepSeconds } Write-Log "[$functionName] Exiting." -LogFile $LogFile return $true } function Assert-SufficientDiskSpaceAvailableForGMACache { param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile $ObsFolderName = $(Get-CacheDirectories).ObservabilityVolume if (-not (Test-Path $ObsFolderName -PathType Container)) { Write-Log "[$functionName] Checking diskspace as $ObsFolderName folder does not exist." -LogFile $LogFile $availableDiskspaceOnSysDrive = ((Get-Volume -DriveLetter $MiscConstants.systemDriveLetter).SizeRemaining) / 1GB Write-Log "[$functionName] Available diskspace on $($MiscConstants.systemDriveLetter) is $availableDiskspaceOnSysDrive GB." -LogFile $LogFile if ($availableDiskspaceOnSysDrive -lt $MiscConstants.AvailableDiskSpaceLimitInGB) { ## Update error message with disk space size so we know in the status and Portal. $ErrorConstants.InsufficientDiskSpaceForGMACache.Message = $ErrorConstants.InsufficientDiskSpaceForGMACache.Message -f $availableDiskspaceOnSysDrive, $MiscConstants.systemDriveLetter return $ErrorConstants.InsufficientDiskSpaceForGMACache.Name } } else { Write-Log "[$functionName] As $ObsFolderName folder exists already, skip the diskspace check." -LogFile $LogFile } Write-Log "[$functionName] Exiting." -LogFile $LogFile return $true } function Invoke-PreInstallationValidation { param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name $validationFunctionNames = ( $MiscConstants.ValidationFunctionNames.AssertNoObsGMAProcessIsRunning, $MiscConstants.ValidationFunctionNames.AssertSufficientDiskSpaceAvailableForGMACache ) Write-Log "[$functionName] Performing pre-installation validation." -LogFile $logFile foreach($validationFunction in $validationFunctionNames) { $validationResult = (Invoke-Expression "$validationFunction -LogFile `'$logFile`'") if ($validationResult -ne $true) { Write-Log "[$functionName] $validationFunction - $($ErrorConstants.$validationResult.Message)" ` -LogFile $LogFile -Level $MiscConstants.Level.Error throw $validationResult } Write-Log "[$functionName] $validationFunction - $validationResult" -LogFile $LogFile } Write-Log "[$functionName] Pre-installation validation completed successfully." -LogFile $logFile } #endregion Pre-installation validation functions #region GCS functions function Get-CloudName { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile ## Default Cloud. $gcsCloudName = $MiscConstants.CloudNames.AzurePublicCloud[1] $arcAgentResourceInfo = Get-ArcAgentResourceInfo -LogFile $logFile if ($null -ne $arcAgentResourceInfo -and (Confirm-IsStringNotEmpty $arcAgentResourceInfo.cloud)) { $gcsCloudName = $arcAgentResourceInfo.cloud Write-Log "[$functionName] CloudName from arc agent show = $gcsCloudName." -LogFile $LogFile } else { ## Check if any cloud value is passed through Config settings. $publicSettings = Get-HandlerConfigSettings if (Confirm-IsStringNotEmpty $publicSettings.cloudName) { $gcsCloudName = $publicSettings.cloudName Write-Log "[$functionName] CloudName from publicSetting = $($publicSettings.cloudName)." -LogFile $LogFile } } Write-Log "[$functionName] Exiting. GcsCloudName: $gcsCloudName." -LogFile $LogFile return $gcsCloudName } function Confirm-IsPpeEnvironment { Param ( [Parameter(Mandatory=$True)] [System.String] $CloudName, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering. Params: $($PSBoundParameters | ConvertTo-Json -Compress)" -LogFile $LogFile $isPpeEnvironment = $false ## Check if the environment is PPE. $isSddcTestDevice = Test-RegKeyExists -Path $MiscConstants.SddcRegKey.Path -Name $MiscConstants.SddcRegKey.Name -LogFile $LogFile -GetValueIfExists if($null -ne $isSddcTestDevice -and $isSddcTestDevice -ne 0) { $isPpeEnvironment = $true Write-Log "[$functionName] SddcTestDevice reg key is present, setting isPpeEnvironment to true." -LogFile $LogFile } elseif (Test-RegKeyExists -Path $MiscConstants.CIRegKey.Path -Name $MiscConstants.CIRegKey.Name -LogFile $LogFile) { $isPpeEnvironment = $true Write-Log "[$functionName] CI reg key is present, setting isPpeEnvironment to true." -LogFile $LogFile } elseif ($CloudName -in $MiscConstants.CloudNames.AzureCanary -or $CloudName -in $MiscConstants.CloudNames.AzurePPE) { $isPpeEnvironment = $true Write-Log "[$functionName] CloudName ($CloudName) is in PPE/Canary, setting isPpeEnvironment to true." -LogFile $LogFile } Write-Log "[$functionName] Exiting. IsPpeEnvironment: $isPpeEnvironment" -LogFile $LogFile return $isPpeEnvironment } function Get-GcsEnvironmentName { Param ( [Parameter(Mandatory=$True)] [System.String] $CloudName, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering. Params: $($PSBoundParameters | ConvertTo-Json -Compress)" -LogFile $LogFile ## Default environment value. $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Prod ## Check if environment is PPE(SDDC, CI). $isEnvPpe = Confirm-IsPpeEnvironment -CloudName $CloudName -LogFile $LogFile ## Check for ARCA if (Get-IsArcAEnvironment) { $gcsEnvironmentName = $MiscConstants.GCSEnvironment.ArcAProd Write-Log "[$functionName] ArcA environment detected, setting gcsEnvironmentName to ArcAProd." -LogFile $LogFile } ## Check for CloudName and set environment accordingly. elseif ($CloudName -in $MiscConstants.CloudNames.AzureUSGovernmentCloud) { $gcsEnvironmentName = if ($isEnvPpe) { $MiscConstants.GCSEnvironment.PpeFairfax } else { $MiscConstants.GCSEnvironment.Fairfax } Write-Log "[$functionName] CloudName is USGovernmentCloud, PPE: $isEnvPpe, setting gcsEnvironmentName to $gcsEnvironmentName." -LogFile $LogFile } elseif ($CloudName -in $MiscConstants.CloudNames.AzureChinaCloud) { $gcsEnvironmentName = if ($isEnvPpe) { $MiscConstants.GCSEnvironment.PpeMooncake } else { $MiscConstants.GCSEnvironment.Mooncake } Write-Log "[$functionName] CloudName is AzureChinaCloud, PPE: $isEnvPpe, setting gcsEnvironmentName to $gcsEnvironmentName." -LogFile $LogFile } elseif ($CloudName -in $MiscConstants.CloudNames.USNat) { $gcsEnvironmentName = if ($isEnvPpe) { $MiscConstants.GCSEnvironment.PpeUSNat } else { $MiscConstants.GCSEnvironment.USNat } Write-Log "[$functionName] CloudName is USNat, PPE: $isEnvPpe, setting gcsEnvironmentName to $gcsEnvironmentName." -LogFile $LogFile } elseif ($CloudName -in $MiscConstants.CloudNames.USSec) { $gcsEnvironmentName = if ($isEnvPpe) { $MiscConstants.GCSEnvironment.PpeUSSec } else { $MiscConstants.GCSEnvironment.USSec } Write-Log "[$functionName] CloudName is USSec, PPE: $isEnvPpe, setting gcsEnvironmentName to $gcsEnvironmentName." -LogFile $LogFile } elseif ($isEnvPpe) { $gcsEnvironmentName = $MiscConstants.GCSEnvironment.Ppe Write-Log "[$functionName] PPE environment detected for CloudName ($CloudName), setting gcsEnvironmentName to Ppe." -LogFile $LogFile } else { Write-Log "[$functionName] Defaulting gcsEnvironmentName to Prod." -LogFile $LogFile } Write-Log "[$functionName] Exiting. GcsEnvironmentName = $gcsEnvironmentName" -LogFile $LogFile return $gcsEnvironmentName } function Get-GcsRegionName { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile ## Defaulted to eastus region. $gcsRegionName = "eastus" $arcAgentResourceInfo = Get-ArcAgentResourceInfo -LogFile $logFile if ($null -ne $arcAgentResourceInfo -and (Confirm-IsStringNotEmpty $arcAgentResourceInfo.location)) { $gcsRegionName = $arcAgentResourceInfo.location Write-Log "[$functionName] RegionName from arc agent show = $gcsRegionName." -LogFile $LogFile } else { ## Check if any region value is passed through Config settings, if yes than use that $publicSettings = Get-HandlerConfigSettings if (Confirm-IsStringNotEmpty $publicSettings.region) { $gcsRegionName = $publicSettings.region Write-Log "[$functionName] RegionName from publicSetting = $gcsRegionName." -LogFile $LogFile } } Write-Log "[$functionName] Exiting. GCSRegionName: $gcsRegionName." -LogFile $LogFile return $gcsRegionName } #endregion GCS functions #region Misc functions function Get-CacheDirectories { Param () $gmaCacheLocation = Join-Path -Path $env:SystemDrive -ChildPath "GMACache" return [ordered] @{ GMACache = $gmaCacheLocation DiagnosticsCache = Join-Path -Path $gmaCacheLocation -ChildPath "DiagnosticsCache" HealthCache = Join-Path -Path $gmaCacheLocation -ChildPath "HealthCache" JsonDropLocation = Join-Path -Path $gmaCacheLocation -ChildPath "JsonDropLocation" MonAgentHostCache = Join-Path -Path $gmaCacheLocation -ChildPath "MonAgentHostCache" MetricsCache = Join-Path -Path $gmaCacheLocation -ChildPath "MetricsCache" TelemetryCache = Join-Path -Path $gmaCacheLocation -ChildPath "TelemetryCache" ObservabilityVolume = Join-Path -Path $env:SystemDrive -ChildPath "Observability" } } function New-CacheDirectories { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Creating cache directories." -LogFile $LogFile $cacheDirectories = Get-CacheDirectories foreach ($directory in $cacheDirectories.Values) { New-Directory ` -Path $directory ` -LogFile $logFile } Write-Log "[$functionName] Created cache directories." -LogFile $LogFile return $cacheDirectories } function New-Directory { param ( [Parameter(Mandatory=$True)] [System.String] $Path, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Directory to create is '$Path'." -LogFile $LogFile if (Test-Path $Path -PathType Container) { Write-Log "[$functionName] Directory '$Path' exists already." -LogFile $LogFile } else { New-Item ` -Path $Path ` -ItemType "Directory" ` -Force ` -Verbose:$False ` | Out-Null Write-Log "[$functionName] Directory '$Path' created." -LogFile $LogFile } } function Get-WatchdogStatusFile { param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $watchdogStatusDirectory = Join-Path -Path $(Get-LogFolderPath) -ChildPath "WatchdogStatus" New-Directory -Path $watchdogStatusDirectory -LogFile $LogFile return Join-Path -Path $watchdogStatusDirectory -ChildPath $MiscConstants.WatchdogStatusFileName } function Get-IsArcAEnvironment { return (Test-RegKeyExists -Path $MiscConstants.ArcARegKey.Path -Name $MiscConstants.ArcARegKey.Name -GetValueIfExists -LogFile $LogFile) -eq $true } function Get-Sha256Hash { Param ( [Parameter(Mandatory=$true)] [System.String] $ClearString ) $hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256') $hash = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($ClearString)) $hashString = [System.BitConverter]::ToString($hash) $hashString = $hashString.Replace('-', '') return $hashString } #endregion Misc functions #region UTC setup functions Function Initialize-UTCSetup { [CmdletBinding()] Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name try { Write-Log ` -Message "[$functionName] Initializing UTC setup." ` -LogFile $logFile #region Stop diagtrack service Stop-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -LogFile $logFile #endregion Stop diagtrack service ## Create the UTC exporter destination folder (if not present) and copy the UtcGenevaExporter dll in it. Write-Log ` -Message "[$functionName] Create folder for UTCExporterdll and place the respective binary in it." ` -LogFile $logFile $utcExporterDllName = $MiscConstants.UtcExporterDllName $utcExporterSourcePath = Join-Path -Path (Get-UtcExporterPackageContentPath) -ChildPath $utcExporterDllName $utcExporterDestinationDirectory = $MiscConstants.UtcExporterDestinationDirectory New-Directory ` -Path $utcExporterDestinationDirectory ` -LogFile $logFile Copy-Item ` -Path $utcExporterSourcePath ` -Destination $utcExporterDestinationDirectory ` -Force ` | Out-Null $utcExporterDestinationPath = Join-Path -Path $utcExporterDestinationDirectory -ChildPath $utcExporterDllName if (Test-Path $utcExporterDestinationPath) { Write-Log ` -Message "[$functionName] Successfully copied '$utcExporterDllName' to '$utcExporterDestinationPath'." ` -LogFile $logFile } else { Write-Log ` -Message "[$functionName] Failed to copy '$utcExporterDllName' to '$utcExporterDestinationPath'." ` -LogFile $logFile ` -Level $MiscConstants.Level.Error throw $ErrorConstants.CannotCopyUtcExporterDll.Name } #region Create reg keys New-RegKey ` -Path $MiscConstants.DiagTrackExportersRegKeyPath ` -LogFile $logFile ` -CreatePathOnly New-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -LogFile $logFile ` -CreatePathOnly New-RegKey ` -Path $MiscConstants.DiagTrackRegKey.Path ` -Name $MiscConstants.DiagTrackRegKey.Name ` -PropertyType $MiscConstants.DiagTrackRegKey.PropertyType ` -Value $MiscConstants.DiagTrackRegKey.Value ` -LogFile $logFile New-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -Name $MiscConstants.GenevaExporterRegKey.Name ` -PropertyType $MiscConstants.GenevaExporterRegKey.PropertyType ` -Value $utcExporterDestinationPath ` -LogFile $logFile New-RegKey ` -Path $MiscConstants.TestHooksRegKey.Path ` -Name $MiscConstants.TestHooksRegKey.Name ` -PropertyType $MiscConstants.TestHooksRegKey.PropertyType ` -Value $MiscConstants.TestHooksRegKey.Value ` -LogFile $logFile New-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -Name $MiscConstants.GenevaNamespaceRegKey.Name ` -PropertyType $MiscConstants.GenevaNamespaceRegKey.PropertyType ` -Value $MiscConstants.GenevaNamespaceRegKey.Value ` -LogFile $logFile #endregion Create reg keys #region Start diagtrack service Start-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -LogFile $logFile #endregion Start diagtrack service Write-Log ` -Message "[$functionName] Successfully initialized UTC setup." ` -LogFile $logFile } finally { if ((Get-Service $MiscConstants.ObsServiceDetails.DiagTrack.Name).Status -eq "Stopped") { Write-Log ` -Message "[$functionName] Starting $($MiscConstants.ObsServiceDetails.DiagTrack.Name) service after it was stopped." ` -LogFile $LogFile Start-Service ` -Name $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -ErrorAction SilentlyContinue ` -Verbose:$false ` | Out-Null } } } Function Clear-UTCSetup { [CmdletBinding()] Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name try { Write-Log ` -Message "[$functionName] Cleaning up UTC related artifacts." ` -LogFile $logFile #region Stop diagtrack service Stop-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -LogFile $LogFile #endregion Stop diagtrack service #region Remove UtcExporter dll Write-Log ` -Message "[$functionName] Removing UTCExporterdll file and its folder." ` -LogFile $LogFile $utcExporterDestinationPath = Join-Path -Path $MiscConstants.UtcExporterDestinationDirectory -ChildPath $MiscConstants.UtcExporterDllName if (Test-Path -Path $utcExporterDestinationPath) { Remove-Item ` -Path $utcExporterDestinationPath ` -Force ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] Removed file '$utcExporterDestinationPath'." ` -LogFile $LogFile if ((Get-ChildItem -Path $MiscConstants.UtcExporterDestinationDirectory | Measure-Object).Count -eq 0) { Remove-Item ` -Path $MiscConstants.UtcExporterDestinationDirectory ` -Force ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] Removed directory '$($MiscConstants.UtcExporterDestinationDirectory)'." ` -LogFile $LogFile } Write-Log ` -Message "[$functionName] Removed UTCExporterdll file '$utcExporterDestinationPath' and its folder path '$($MiscConstants.UtcExporterDestinationDirectory)'." ` -LogFile $logFile } else { Write-Log ` -Message "[$functionName] UTCExporter dll does not exists at path '$utcExporterDestinationPath'. Nothing to remove." ` -LogFile $logFile } #endregion Remove UtcExporter dll #region Remove reg keys Remove-RegKey ` -Path $MiscConstants.DiagTrackRegKey.Path ` -Name $MiscConstants.DiagTrackRegKey.Name ` -LogFile $logFile Remove-RegKey ` -Path $MiscConstants.TestHooksRegKey.Path ` -Name $MiscConstants.TestHooksRegKey.Name ` -LogFile $logFile Remove-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -Name $MiscConstants.GenevaExporterRegKey.Name ` -LogFile $logFile Remove-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -Name $MiscConstants.GenevaNamespaceRegKey.Name ` -LogFile $logFile Remove-RegKey ` -Path $MiscConstants.GenevaExporterRegKey.Path ` -LogFile $logFile ` -RemovePathOnly #endregion Remove reg keys #region Start diagtrack service Start-ServiceForObservability ` -ServiceName $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -LogFile $LogFile #endregion Start diagtrack service Write-Log ` -Message "[$functionName] Cleaned up artifacts related to UTC setup." ` -Logfile $logFile } finally { if ((Get-Service $MiscConstants.ObsServiceDetails.DiagTrack.Name).Status -eq "Stopped") { Write-Log ` -Message "[$functionName] Starting $($MiscConstants.ObsServiceDetails.DiagTrack.Name) service after it was stopped." ` -LogFile $LogFile Start-Service ` -Name $MiscConstants.ObsServiceDetails.DiagTrack.Name ` -ErrorAction SilentlyContinue ` -Verbose:$false ` | Out-Null } } } #endregion UTC setup functions #region VCRuntime setup function function Install-VCRuntime { [CmdletBinding()] Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $Logfile <# Validate if there is an already installed version of VCRedist package and if yes, whether it is higher than the one we are installing. If higher, then skip installation. #> foreach ($regKeyPath in $MiscConstants.VCRedistRegKeys.Paths) { $installedVCRedistVersion = Test-RegKeyExists -Path $regKeyPath -Name $MiscConstants.VCRedistRegKeys.Name -GetValueIfExists -LogFile $Logfile if ($null -ne $installedVCRedistVersion) { $installedVCRedistVersion = $installedVCRedistVersion.Replace('v', '') # For e.g. If the version value comes to be "v14.32.31332.00" and to compare it with the file version we need to remove the character 'v'. Write-Log "[$functionName] VCRedist is already installed with version: $installedVCRedistVersion." -LogFile $Logfile break } } $vcRedistFilePath = Join-Path -Path (Get-VCRuntimePackageContentPath) -ChildPath $MiscConstants.VCRuntimeExeName $currentVCRedistFileVersion = (Get-Item $vcRedistFilePath).VersionInfo.FileVersion Write-Log "[$functionName] Current VCRedist file ($vcRedistFilePath) version is $currentVCRedistFileVersion." -LogFile $Logfile if ($null -eq $installedVCRedistVersion -or $installedVCRedistVersion -lt $currentVCRedistFileVersion) { $vcRedistInstallationLogFile = Join-Path $(Get-LogFolderPath) -ChildPath $MiscConstants.VCRedistInstallationLogFileName Write-Log "[$functionName] Either the VCRedist is not installed or the installed version is less than current version. Thus, installing VCRedist using following command - $vcRedistFilePath /install /quiet /norestart /log $vcRedistInstallationLogFile" -LogFile $LogFile $vcInstall = Start-Process -File $vcRedistFilePath -ArgumentList "/install /quiet /norestart /log $vcRedistInstallationLogFile" -Wait -NoNewWindow -PassThru <# Exit codes descriptions (https://learn.microsoft.com/en-us/windows/win32/msi/error-codes): 0 = Install succeeded. 3010 = A restart is required to complete the install (Machine reboot is pending). #> ## Update the error message with Exit code so that it can be visible on the Portal. $ErrorConstants.VCRedistInstallFailed.Message = $ErrorConstants.VCRedistInstallFailed.Message -f $vcInstall.ExitCode if ($vcInstall.ExitCode -ne 0 -and $vcInstall.ExitCode -ne 3010) { Write-Log ` -Message "[$functionName] $($ErrorConstants.VCRedistInstallFailed.Message)" ` -LogFile $LogFile ` -Level $MiscConstants.Level.Error throw $ErrorConstants.VCRedistInstallFailed.Name } Write-Log "[$functionName] VC Runtime $vcRedistFilePath successfully installed." -LogFile $LogFile } else { Write-Log "[$functionName] VCRedist is already installed with version $installedVCRedistVersion which is either equal or higher than current vcredist file version of $currentVCRedistFileVersion. Thus, skipping the installation." -LogFile $Logfile } Write-Log "[$functionName] Exiting." -LogFile $Logfile } #endregion VCRuntime setup function #region Registry functions function New-RegKey { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [System.String] $Path, [Parameter(Mandatory=$False)] [System.String] $Name, [Parameter(Mandatory=$False)] [System.String] $PropertyType, [Parameter(Mandatory=$False)] [System.String] $Value, [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$False)] [System.Management.Automation.SwitchParameter] $CreatePathOnly ) $functionName = $MyInvocation.MyCommand.Name if ($CreatePathOnly) { if (-not (Test-Path -Path $Path)) { New-Item ` -Path $Path ` -Force ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] Created RegKey path ($Path)." ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] RegKey path ($Path) exists already." ` -LogFile $LogFile } } else { $currentValue = Test-RegKeyExists -Path $Path -Name $Name -GetValueIfExists if ($currentValue -ne $Value) { $out = New-ItemProperty ` -Path $Path ` -Name $Name ` -PropertyType $PropertyType ` -Value $Value ` -Force if ([System.String]::IsNullOrEmpty($currentValue)) { Write-Log ` -Message "[$functionName] Created registry key with path ($Path), name ($Name) and value ($Value). Output: $out" ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Updated registry key with path ($Path), name ($Name) and value ($Value). (Previous value was '$currentValue'.)" ` -LogFile $LogFile } } else { Write-Log ` -Message "[$functionName] RegKey path ($Path) and name ($Name) and value ($Value) exists already." ` -LogFile $LogFile } } } Function Remove-RegKey { [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [System.String] $Path, [Parameter(Mandatory=$False)] [System.String] $Name, [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$False)] [System.Management.Automation.SwitchParameter] $RemovePathOnly ) $functionName = $MyInvocation.MyCommand.Name if ($RemovePathOnly) { if (Test-Path -Path $Path) { Remove-Item ` -Path $Path ` -Force ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] Path ($Path) removed successfully." ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Path ($Path) does not exists. Nothing to remove" ` -LogFile $LogFile } } else { if (Test-RegKeyExists -Path $Path -Name $Name -LogFile $LogFile) { Remove-ItemProperty ` -Path $Path ` -Name $Name ` -Force ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] RegKey path ($Path) and name ($Name) removed successfully." ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] RegKey path ($Path) and name ($Name) does not exists. Nothing to remove" ` -LogFile $LogFile } } } #endregion Registry functions #region Scheduled task functions function Enable-ObsScheduledTask { [CmdletBinding()] param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log ` -Message "[$functionName] Enabling ObsScheduledTask ($($MiscConstants.ObsScheduledTaskDetails.TaskName))." ` -LogFile $LogFile $taskObject = ScheduledTasks\Get-ScheduledTask ` -TaskPath $MiscConstants.ObsScheduledTaskDetails.TaskPath ` -TaskName $MiscConstants.ObsScheduledTaskDetails.TaskName ` -ErrorAction $MiscConstants.ErrorActionPreference.SilentlyContinue if ($null -eq $taskObject) { Write-Log ` -Message "[$functionName] No scheduled task with name ($($MiscConstants.ObsScheduledTaskDetails.TaskName)) was found to enable." ` -LogFile $LogFile } else { ScheduledTasks\Enable-ScheduledTask ` -InputObject $taskObject ` -ErrorAction $MiscConstants.ErrorActionPreference.Stop ` -Verbose:$false ` | Out-Null Write-Log ` -Message "[$functionName] Successfully enabled obs scheduled task with name $($taskObject.TaskName) at path $($taskObject.TaskPath)." ` -LogFile $LogFile } } function Disable-ObsScheduledTask { [CmdletBinding()] Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Disabling ObsScheduledTask ($($MiscConstants.ObsScheduledTaskDetails.TaskName))." -LogFile $LogFile $taskObject = ScheduledTasks\Get-ScheduledTask ` -TaskPath $MiscConstants.ObsScheduledTaskDetails.TaskPath ` -TaskName $MiscConstants.ObsScheduledTaskDetails.TaskName ` -ErrorAction $MiscConstants.ErrorActionPreference.SilentlyContinue if ($null -eq $taskObject) { Write-Log "[$functionName] No scheduled task with name $($MiscConstants.ObsScheduledTaskDetails.TaskName) was found to disable." -LogFile $LogFile } else { ScheduledTasks\Disable-ScheduledTask ` -InputObject $taskObject ` -ErrorAction $MiscConstants.ErrorActionPreference.Stop ` -Verbose:$false ` | Out-Null Write-Log "[$functionName] Successfully disabled obs scheduled task with name $($taskObject.TaskName) at path $($taskObject.TaskPath)." -LogFile $LogFile } } function Remove-ObsScheduledTask { param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name $trimmedTaskPath = $MiscConstants.ObsScheduledTaskDetails.TaskPath.TrimEnd('\') $tasks = ScheduledTasks\Get-ScheduledTask -TaskPath "$trimmedTaskPath\*" -ErrorAction $MiscConstants.ErrorActionPreference.SilentlyContinue if ($tasks) { foreach($task in $tasks) { if($task.TaskName -eq $MiscConstants.ObsScheduledTaskDetails.TaskName) { ScheduledTasks\Unregister-ScheduledTask -TaskName $task.TaskName -TaskPath $task.TaskPath -Confirm:$false | Out-Null Write-Log "[$functionName] Successfully removed scheduled task $($task.TaskName) from path $($task.TaskPath)." -LogFile $LogFile } } } else { Write-Log "[$functionName] Either the path '$trimmedTaskPath' doesn`'t exists or no scheduled tasks found to delete." -LogFile $LogFile } } #endregion Scheduled task functions #region Windows service functions function Register-ServiceForObservability { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [System.String] $ServiceName, [Parameter(Mandatory=$True)] [System.String] $ServiceDisplayName, [Parameter(Mandatory=$True)] [System.String] $ServiceBinaryFilePath, [Parameter(Mandatory=$False)] [System.String] $ServiceStartupType = $MiscConstants.WinServiceStartupTypes.Manual, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name try { Write-Log "[$functionName] Starting registration of service '$ServiceName'." -LogFile $logFile Write-Log "[$functionName] Configuring service '$ServiceName' from path '$ServiceBinaryFilePath'" -LogFile $LogFile if (Get-Service $ServiceName -ErrorAction SilentlyContinue) { Write-Log "[$functionName] Service '$ServiceName' already registered." -LogFile $LogFile } else { New-Service ` -Name $ServiceName ` -BinaryPathName $ServiceBinaryFilePath ` -DisplayName $ServiceDisplayName ` -StartupType $ServiceStartupType ` -ErrorAction Stop ` -Verbose:$false ` | Out-Null } Write-Log "[$functionName] Registration of service '$ServiceName' with display name '$ServiceDisplayName' completed." -LogFile $logFile } catch { Write-Log "[$functionName] $($ErrorConstants.CannotRegisterService.Message) Service Name: '$ServiceName'. Exception: $_" ` -LogFile $LogFile -Level $MiscConstants.Level.Error throw $ErrorConstants.CannotRegisterService.Name } } function Start-ServiceForObservability { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [System.String] $ServiceName, [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$False)] [int] $Retries = $MiscConstants.Retries ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Starting service '$ServiceName'." -LogFile $logFile # Start MA Watchdog Agent Service $retryCount = $Retries $serviceStatus = (Get-Service $ServiceName).Status if ($serviceStatus -eq "Running") { Write-Log "[$functionName] Service '$ServiceName' running already." -LogFile $LogFile return } while(($serviceStatus -ne "Running") -and ($retryCount -gt 0)) { Start-Service $ServiceName ` -WarningAction SilentlyContinue ` -WarningVariable $startSvcWarn if ($null -ne $startSvcWarn) { Write-Log "[$functionName] $startSvcWarn" ` -Level $MiscConstants.Level.Warning -LogFile $LogFile } Write-Log "[$functionName] Waiting for service '$ServiceName' to start..." -LogFile $LogFile Start-Sleep -Seconds 5 $serviceStatus = (Get-Service $ServiceName).Status $retryCount-- } if ($serviceStatus -ne "Running") { Write-Log "[$functionName] $($ErrorConstants.CannotStartService.Message) Service Name: '$ServiceName'" ` -LogFile $LogFile -Level $MiscConstants.Level.Error throw $ErrorConstants.CannotStartService.Name } Write-Log "[$functionName] Successfully started service '$ServiceName'." -LogFile $LogFile } Function Switch-ObsServiceStartupType { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [System.String] $ServiceName, [Parameter(Mandatory=$True)] [System.String] $StartupType, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering. Params: $($PSBoundParameters | ConvertTo-Json -Compress)" -LogFile $LogFile $service = Get-Service $ServiceName -ErrorAction SilentlyContinue if ($null -eq $service) { Write-Log "[$functionName] Service '$ServiceName' not found." -LogFile $LogFile return } $out = Set-Service -Name $ServiceName -StartupType $StartupType -PassThru Write-Log "[$functionName] Start type for service ($($out.ServiceName))= $($out.StartType)." -LogFile $LogFile Write-Log "[$functionName] Exiting." -LogFile $LogFile } function Stop-ServiceForObservability { [CmdletBinding()] param ( [Parameter(Mandatory=$True)] [System.String] $ServiceName, [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$False)] [int] $Retries = $MiscConstants.Retries ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Stopping service '$ServiceName'." -LogFile $logFile # Stop MA Watchdog Agent Service $retryCount = $Retries $service = Get-Service $serviceName -ErrorAction SilentlyContinue if ($null -eq $service) { Write-Log "[$functionName] Service '$ServiceName' not found." -LogFile $LogFile return } $serviceStatus = $service.Status if ($serviceStatus -eq "Stopped") { Write-Log "[$functionName] Service '$ServiceName' stopped already." -LogFile $LogFile return } while (($serviceStatus -ne "Stopped") -and ($retryCount -gt 0)) { Stop-Service $ServiceName ` -WarningAction SilentlyContinue ` -WarningVariable $stopSvcWarn if ($null -ne $stopSvcWarn) { Write-Log "[$functionName] $stopSvcWarn" ` -Level $MiscConstants.Level.Warning -LogFile $LogFile } Write-Log "[$functionName] Waiting for service '$ServiceName' to stop..." -LogFile $LogFile Start-Sleep -Seconds 5 $serviceStatus = (Get-Service $ServiceName).Status $retryCount-- } if ($serviceStatus -ne "Stopped") { Write-Log "[$functionName] $($ErrorConstants.CannotStopService.Message) Service Name: '$ServiceName'" ` -LogFile $LogFile -Level $MiscConstants.Level.Error throw $ErrorConstants.CannotStopService.Name } Write-Log "[$functionName] Successfully stopped service '$ServiceName'." -LogFile $LogFile } Function Unregister-ServiceForObservability { [CmdletBinding()] Param ( [Parameter(Mandatory=$True)] [System.String] $ServiceName, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Unregistering service '$ServiceName'." -LogFile $LogFile if (Get-Service $ServiceName -ErrorAction SilentlyContinue) { Stop-ServiceForObservability -ServiceName $ServiceName -LogFile $LogFile } Write-Log "[$functionName] $(sc.exe delete $ServiceName -Verbose)" -LogFile $LogFile Write-Log "[$functionName] Successfully unregistered service '$ServiceName'." -LogFile $LogFile } #endregion #region logman Function Initialize-LogmanTraceSession { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log ` -Message "[$functionName] Entering." ` -LogFile $LogFile $logmanCreateResult = $null if (Get-Command logman -ErrorAction SilentlyContinue) { $sessionsExistsResult = logman query $MiscConstants.Logman.TraceName if ($sessionsExistsResult[1] -eq "Error:" -and $sessionsExistsResult[2] -eq "Data Collector Set was not found.") { <# Reference: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/logman-create-trace --v: This flag removes the versioning added by default in the etl files. The version is removed because we need only one file to be present. -ow: This flag overwrites the existing file, when the current session is stopped and a new one is started.There is another -a (i.e. append) flag but after adding that, it fails to start the session and so for time being we are using this flag. As we don't expect the customers to be disabling the mandatory extensions oftenly. #> $logmanCreateResult += logman create trace $MiscConstants.Logman.TraceName -f bincirc -o $MiscConstants.Logman.OutputFilePath -max $MiscConstants.Logman.MaxLogFileSizeInMB --v -ow foreach ($guid in $MiscConstants.Logman.ComponentProviderGuids.Values) { $logmanCreateResult += logman update trace $MiscConstants.Logman.TraceName -p "{$guid}" } Write-Log ` -Message "[$functionName] Successfully created logman trace session for Obs components with Output file path of $($MiscConstants.Logman.OutputFilePath) and max log file size of $($MiscConstants.Logman.MaxLogFileSizeInMB) MB. Results = $($logmanCreateResult | Out-String)" ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Logman trace session for Obs components exists already. Result = $($sessionsExistsResult | Out-String)" ` -LogFile $LogFile } } else { Write-Log ` -Message "[$functionName] Logman command is not available in the OS." ` -LogFile $LogFile } Write-Log ` -Message "[$functionName] Exiting." ` -LogFile $LogFile } Function Start-LogmanTraceSession { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log ` -Message "[$functionName] Entering." ` -LogFile $LogFile if (Get-Command logman -ErrorAction SilentlyContinue) { $logmanStartResult = logman start $MiscConstants.Logman.TraceName Write-Log ` -Message "[$functionName] Started logman trace session for Obs components. Result = $($logmanStartResult | Out-String)" ` -LogFile $LogFile $logmanQueryResult = logman query $MiscConstants.Logman.TraceName Write-Log ` -Message "[$functionName] Logman query result = $($logmanQueryResult | Out-String)" ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Logman command is not available in the OS." ` -LogFile $LogFile } Write-Log ` -Message "[$functionName] Exiting." ` -LogFile $LogFile } Function Stop-LogmanTraceSession { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log ` -Message "[$functionName] Entering." ` -LogFile $LogFile if (Get-Command logman -ErrorAction SilentlyContinue) { $logmanStopResult = logman stop $MiscConstants.Logman.TraceName Write-Log ` -Message "[$functionName] Logman trace session for Obs components stopped successfully. Result = $($logmanStopResult | Out-String)" ` -LogFile $LogFile $logmanQueryResult = logman query $MiscConstants.Logman.TraceName Write-Log ` -Message "[$functionName] Logman query result = $($logmanQueryResult | Out-String)" ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Logman command is not available in the OS." ` -LogFile $LogFile } Write-Log ` -Message "[$functionName] Exiting." ` -LogFile $LogFile } Function Remove-LogmanTraceSession { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log ` -Message "[$functionName] Entering." ` -LogFile $LogFile if (Get-Command logman -ErrorAction SilentlyContinue) { $sessionsExistsResult = logman query $MiscConstants.Logman.TraceName <# Checking the length because if the session exists, it will give an output in array format (as shown below), so we just save it in a variable and count the length of the result. If the session is present it will have this below output. ``` PS C:\sapanc00\FDA\Microsoft.FleetDiagnosticsAgent.Core.2.4.20230613.720> logman query sapancTest Name: sapancTest Status: Stopped Root Path: C:\ Segment: Off Schedules: On Segment Max Size: 100 MB Run as: SYSTEM Name: sapancTest\sapancTest Type: Trace Append: Off Circular: On Overwrite: On Buffer Size: 8 Buffers Lost: 0 Buffers Written: 0 Buffer Flush Timer: 0 Clock Type: Performance File Mode: File The command completed successfully. ``` But if the session is not present, then output should be as below, where the length of array is just 3. ``` PS C:\sapanc00\FDA\Microsoft.FleetDiagnosticsAgent.Core.2.4.20230613.720> logman query sapancTest Error: Data Collector Set was not found. ``` #> if ($sessionsExistsResult.Length -gt 10) { $logmanDeleteResult = logman delete $MiscConstants.Logman.TraceName Write-Log ` -Message "[$functionName] Successfully deleted logman trace session for Obs components. Result = $($logmanDeleteResult | Out-String)" ` -LogFile $LogFile } else { Write-Log ` -Message "[$functionName] Logman trace session for Obs components does not exist. SessionExistsResult = $($sessionsExistsResult | Out-String)" ` -LogFile $LogFile } } else { Write-Log ` -Message "[$functionName] Logman command is not available in the OS." ` -LogFile $LogFile } Write-Log ` -Message "[$functionName] Exiting." ` -LogFile $LogFile } #endregion logman #region Observability Symlinks function Get-SymlinkPaths { $symLinkPaths = @{ DiagnosticsInitializer = @{ SymLink = "C:\Program Files\WindowsPowerShell\Modules\DiagnosticsInitializer" Destination = Join-Path $(Get-ObsAgentPackageContentPath) -ChildPath "DiagnosticsInitializer" } SBRPClient = @{ SymLink = "C:\Program Files\SBRPClient" Destination = Get-SBCClientPackageContentPath } ObservabilityAgent = @{ SymLink = "C:\Program Files\ObsAgent" Destination = Get-ObsAgentPackageContentPath } TestObservability = @{ SymLink = "C:\Program Files\WindowsPowerShell\Modules\TestObservability" Destination = Get-TestObservabilityPackagePath } } return $symLinkPaths } function Add-ObservabilitySymLinks { Param ( [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$False)] [System.Management.Automation.SwitchParameter] $TestObsOnly ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile $allSymLinkPaths = Get-SymlinkPaths if ($TestObsOnly) { $symLinks = $allSymLinkPaths.GetEnumerator() | Where-Object {$_.Name -ieq "TestObservability"} } else { $symLinks = $allSymLinkPaths.GetEnumerator() } $symLinks | ForEach-Object { $symLinkPath = $_.Value.SymLink $destination = $_.Value.Destination if (-not (Test-Path $symLinkPath)) { if (-not (Test-PathIsSymLink -Path $symLinkPath -LogFile $LogFile)) { Write-Log "[$functionName] Adding symlink $symLinkPath to path $destination." -LogFile $LogFile Write-Log "[$functionName] $(cmd /c mklink /d "$symLinkPath" "$destination")" -LogFile $LogFile } else { Write-Log "[$functionName] Symlink $symLinkPath to path $destination already exists." -LogFile $LogFile } } else { Write-Log "[$functionName] Actual folder path to the symlinkPath ($symLinkPath) already exists." -LogFile $LogFile } } Write-Log "[$functionName] Exiting." -LogFile $LogFile } function Remove-ObservabilitySymLinks { Param ( [Parameter(Mandatory=$False)] [int] $Retries = $MiscConstants.Retries, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile $retryCount = $Retries $success = $false $sleepSeconds = 10 $allSymLinkPaths = Get-SymlinkPaths while ($retryCount -gt 0 -and -not $success) { try { $allSymLinkPaths.GetEnumerator() | ForEach-Object { $symLinkPath = $_.Value.SymLink $destination = $_.Value.Destination if (Test-PathIsSymLink -Path $symLinkPath -LogFile $logFile) { Write-Log "[$functionName] Removing symlink $symLinkPath to path $destination." -LogFile $LogFile Write-Log "[$functionName] $(cmd /c rmdir "$symLinkPath")" -LogFile $LogFile } } $success = $true ## Check if the symlinks are removed successfully foreach ($component in $allSymLinkPaths.GetEnumerator()) { $symLinkPath = $component.Value.SymLink $destination = $component.Value.Destination if (Test-PathIsSymLink -Path $symLinkPath -LogFile $logFile) { Write-Log ` -Message "[$functionName] Failed to remove symlink $symLinkPath." ` -LogFile $LogFile ` -Level $MiscConstants.Level.Error $success = $false } } if (-not $success) { Write-Log ` -Message "[$functionName] Retrying after $sleepSeconds seconds." ` -LogFile $LogFile ` -Level $MiscConstants.Level.Error Start-Sleep -Seconds $sleepSeconds } } catch { Write-Log ` -Message "[$functionName] Removing symlinks failed with error $_. Retrying after $sleepSeconds seconds." ` -LogFile $LogFile ` -Level $MiscConstants.Level.Error Start-Sleep -Seconds 10 } $retryCount-- } if ($retryCount -eq 0 -and -not $success) { $errMsg = "[$functionName] Failed to remove symlinks." Write-Log ` -Message $errMsg ` -LogFile $LogFile ` -Level $MiscConstants.Level.Error } Write-Log "[$functionName] Exiting." -LogFile $LogFile } function Test-PathIsSymLink { Param ( [Parameter(Mandatory=$True)] [System.String] $Path, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name if (Test-Path $Path) { if ((Get-Item $Path -ErrorAction SilentlyContinue).LinkType -eq "SymbolicLink") { Write-Log ` -Message "[$functionName] Path $Path is a SymLink." ` -LogFile $LogFile return $True } } Write-Log ` -Message "[$functionName] Path $Path is not a SymLink." ` -LogFile $LogFile return $False } #endregion Observability Symlinks #region Identity Parameters from runtime settings function Get-IdenityParametersFromPublicSettings { Param ( [Parameter(Mandatory=$False)] [System.Object] $IdentityParamsToFetch, [Parameter(Mandatory=$False)] [System.String] $LogFile ) $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." -LogFile $LogFile $retrievedIdParams = @{} ## Check if any cloud value is passed through Config settings, if yes than use that $publicSettings = Get-HandlerConfigSettings foreach ($idParam in $IdentityParamsToFetch.Values) { ## Check if any identity value is passed through Config settings, if yes than use that if (Confirm-IsStringNotEmpty $publicSettings.$idParam) { Write-Log "[$functionName] $idParam value from publicSetting = $($publicSettings.$idParam)." -LogFile $LogFile $retrievedIdParams[$idParam] = $publicSettings.$idParam } else { Write-Log "[$functionName] $idParam not found in public settings." -LogFile $LogFile $retrievedIdParams[$idParam] = [System.String]::Empty } } Write-Log "[$functionName] Exiting. $($retrievedIdParams | ConvertTo-Json -Compress)." -LogFile $LogFile return $retrievedIdParams } #endregion Identity Parameters from runtime settings #region Package Content Path functions function Get-ObsNugetStorePath { [CmdletBinding()] Param () $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." if (-not $global:ObsNugetStorePath) { Write-Log "[$functionName] global:ObsNugetStorePath is not set. Setting it." if (Get-Command Set-ObsNugetStorePath -ErrorAction Ignore) { Write-Log "[$functionName] Set-ObsNugetStorePath function is available. Invoking it." $obsStoreRootPath = Set-ObsStoreRootFolderPath Set-ObsNugetStorePath -ObsStoreRootPath $obsStoreRootPath } else { Write-Log "[$functionName] Set-ObsNugetStorePath function is not available. Setting ObsNugetStorePath manually (i.e. relative to the script path)." <# Split-Path levels w.r.t. ObsExtSetupScripts package's folder structure: L0 - $PSScriptRoot = Helpers folder L1 - Split-Path = content L2 - Split-Path = Package's name (i.e. Microsoft.AzureStack.Observability.ObsExtSetupScripts.*) L3 - Split-Path = ExtVersion folder (i.e. X.X.X.X) #> $global:ObsNugetStorePath = $PSScriptRoot ` | Split-Path ` | Split-Path ` | Split-Path Write-Log "[$functionName] ObsNugetStorePath is set to= $($global:ObsNugetStorePath)" } } Write-Log "[$functionName] Exiting. ObsNugetStorePath = $($global:ObsNugetStorePath)" return $global:ObsNugetStorePath } function Set-ObsPackageContentPath { [CmdletBinding()] Param () $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." $nugetStorePath = Get-ObsNugetStorePath foreach($nuget in $global:ObsArtifactsPaths.GetEnumerator()) { $nugetInfo = $nuget.Value Write-Log "[$functionName] Setting content path for package ($($nugetInfo.PackageName))." $packageArtifact = Get-Package ` -Name $nugetInfo.PackageName ` -Destination $nugetStorePath $nugetInfo.PackageContentPath = Join-Path -Path ([System.IO.Path]::GetDirectoryName($packageArtifact.Source)) -ChildPath $nugetInfo.ChildPath Write-Log "[$functionName] Content path of package ($($nugetInfo.PackageName)) = $($nugetInfo.PackageContentPath)" } Write-Log "[$functionName] Exiting. Successfully set the package content paths." } function Get-ExtensionVersion { [CmdletBinding()] Param () $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." $extensionVersion = "" try { if ($global:extensionRootLocation) { $extensionVersion = ($global:extensionRootLocation).Split("\")[-1] if ($extensionVersion -eq "content") { # If extensionVersion is content, then Standalone observability pipeline is being used. Set extension version to Standalone<standalone nuget version>. $standaloneFolderName = ($global:extensionRootLocation).Split("\")[-2] $standalonePattern = "Standalone.*" $match = [System.Text.RegularExpressions.Regex]::Match($standaloneFolderName, $standalonePattern) if ($match.Success) { $extensionVersion = $match.Value } } } else { Write-Log "[$functionName] Extension root location is not set. Getting extension version from ObsNugetStore version path." $extensionVersion = (Get-ObsNugetStorePath).Split("\")[-1] } } catch { Write-Log ` -Message "[$functionName] Getting extension version failed with error $_" ` -Level $MiscConstants.Level.Error } Write-Log "[$functionName] Exiting. ExtensionVersion = $extensionVersion" return $extensionVersion } function Stop-ExternalMonitoringAgentProcesses { [CmdletBinding()] Param () $functionName = $MyInvocation.MyCommand.Name Write-Log "[$functionName] Entering." $externalMonitoringProcesses = Get-Process "Mon*" -ErrorAction SilentlyContinue | Where-Object ` { $_.Path -like "*Microsoft.Azure.Geneva.GenevaMonitoring*" } if ($externalMonitoringProcesses) { foreach ($process in $externalMonitoringProcesses) { try { Write-Log -Message "Found external monitoring agent process: $($process.Name) at $($process.Path) with Id: $($process.Id). Stopping process." Stop-Process -Id $process.Id -Force -Confirm:$false } catch { Write-Log -Message "Stopping external monitoring agent process: $($process.Name) at $($process.Path) with Id: $($process.Id) failed with error $_" -Level $MiscConstants.Level.Error } } } } function Get-FDAPackageContentPath { $global:ObsArtifactsPaths.FDA.PackageContentPath } Function Get-GmaPackageContentPath { $global:ObsArtifactsPaths.GMA.PackageContentPath } Function Get-ObsAgentPackageContentPath { $global:ObsArtifactsPaths.ObservabilityAgent.PackageContentPath } function Get-ObservabilityDeploymentPackagePath { $global:ObsArtifactsPaths.ObservabilityDeployment.PackageContentPath } function Get-VCRuntimePackageContentPath { "$($global:ObsArtifactsPaths.ObservabilityDeployment.PackageContentPath)\content\VS17" } function Get-SBCClientPackageContentPath { $global:ObsArtifactsPaths.SBCClient.PackageContentPath } function Get-TestObservabilityPackagePath { $global:ObsArtifactsPaths.TestObservability.PackageContentPath } function Get-UtcExporterPackageContentPath { $global:ObsArtifactsPaths.UtcExporter.PackageContentPath } function Get-WatchdogPackageContentPath { $global:ObsArtifactsPaths.MAWatchdog.PackageContentPath } function Get-ObsExtSetupScriptsPackageContentPath { $global:ObsArtifactsPaths.ObsExtSetupScripts.PackageContentPath } function Get-NetworkObservabilityPackagePath { $global:ObsArtifactsPaths.NetworkObservability.PackageContentPath } function Get-WatsonAgentPackagePath { $global:ObsArtifactsPaths.WatsonAgent.PackageContentPath } #endregion Package Content Path functions function Write-Log { [CmdletBinding()] Param ( [Parameter(Mandatory=$True, ValueFromPipeline)] [System.String] $Message, [Parameter(Mandatory=$False)] [ValidateSet("INFO","WARNING","ERROR","FATAL","DEBUG","VERBOSE")] [System.String] $Level = "INFO", [Parameter(Mandatory=$False)] [System.String] $LogFile, [Parameter(Mandatory=$false)] [System.Management.Automation.SwitchParameter] $WriteToConsole ) if ($global:LogFile -and ([System.String]::IsNullOrWhiteSpace($LogFile) -or [System.String]::IsNullOrEmpty($LogFile))) { $LogFile = $global:LogFile } $dateTimeStamp = [System.DateTime]::UtcNow.ToString("u") $formattedMessage = "$dateTimeStamp : $Level : $Message" if ($WriteToConsole -or (-not $LogFile)) { if (Get-Command Trace-Execution -ErrorAction Ignore) { Trace-Execution $formattedMessage } switch($Level.toUpper()) { "INFO" { Write-Host $formattedMessage break; } "DEBUG" { Write-Debug $formattedMessage break; } "VERBOSE" { Write-Verbose $formattedMessage break; } "WARNING" { Write-Warning $formattedMessage break; } "ERROR" { Write-Error $formattedMessage break; } "FATAL" { Write-Error $formattedMessage break; } } } if ($LogFile) { Out-File -FilePath $LogFile -InputObject $formattedMessage -Append -Encoding utf8 } } #endregion Functions #region Exports # Pre-installation validation functions Export-ModuleMember -Function Invoke-PreInstallationValidation ## GCS functions Export-ModuleMember -Function Get-CloudName Export-ModuleMember -Function Confirm-IsPpeEnvironment Export-ModuleMember -Function Get-GcsEnvironmentName Export-ModuleMember -Function Get-GcsRegionName ## Misc functions Export-ModuleMember -Function Get-CacheDirectories Export-ModuleMember -Function New-CacheDirectories Export-ModuleMember -Function New-Directory Export-ModuleMember -Function Get-WatchdogStatusFile Export-ModuleMember -Function Get-IsArcAEnvironment Export-ModuleMember -Function Get-Sha256Hash Export-ModuleMember -Function Stop-ExternalMonitoringAgentProcesses ## UTC setup functions Export-ModuleMember -Function Initialize-UTCSetup Export-ModuleMember -Function Clear-UTCSetup ## Registry functions Export-ModuleMember -Function New-RegKey Export-ModuleMember -Function Remove-RegKey # VCRuntime setup function Export-ModuleMember -Function Install-VCRuntime ## Scheduled task functions Export-ModuleMember -Function Enable-ObsScheduledTask Export-ModuleMember -Function Disable-ObsScheduledTask Export-ModuleMember -Function Remove-ObsScheduledTask ## Windows service functions Export-ModuleMember -Function Register-ServiceForObservability Export-ModuleMember -Function Start-ServiceForObservability Export-ModuleMember -Function Switch-ObsServiceStartupType Export-ModuleMember -Function Stop-ServiceForObservability Export-ModuleMember -Function Unregister-ServiceForObservability ## logman functions Export-ModuleMember -Function Initialize-LogmanTraceSession Export-ModuleMember -Function Start-LogmanTraceSession Export-ModuleMember -Function Stop-LogmanTraceSession Export-ModuleMember -Function Remove-LogmanTraceSession # DiagnosticsInitializer Symlink functions Export-ModuleMember -Function Get-SymlinkPaths Export-ModuleMember -Function Add-ObservabilitySymLinks Export-ModuleMember -Function Remove-ObservabilitySymLinks Export-ModuleMEmber -Function Test-PathIsSymLink ## Identity Parameters from runtime settings Export-ModuleMember -Function Get-IdenityParametersFromPublicSettings ## Package Content Path functions Export-ModuleMember -Function Get-ObsNugetStorePath Export-ModuleMember -Function Set-ObsPackageContentPath Export-ModuleMember -Function Get-FDAPackageContentPath Export-ModuleMember -Function Get-GmaPackageContentPath Export-ModuleMember -Function Get-ObsAgentPackageContentPath Export-ModuleMember -Function Get-ObservabilityDeploymentPackagePath Export-ModuleMember -Function Get-VCRuntimePackageContentPath Export-ModuleMember -Function Get-SBCClientPackageContentPath Export-ModuleMember -Function Get-TestObservabilityPackagePath Export-ModuleMember -Function Get-UtcExporterPackageContentPath Export-ModuleMember -Function Get-WatchdogPackageContentPath Export-ModuleMember -Function Get-ObsExtSetupScriptsPackageContentPath Export-ModuleMember -Function Get-NetworkObservabilityPackagePath Export-ModuleMember -Function Get-ExtensionVersion Export-ModuleMember -Function Get-WatsonAgentPackagePath #endregion Exports # SIG # Begin signature block # MIIncQYJKoZIhvcNAQcCoIInYjCCJ14CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDvMcEkW00tdJ/9 # 5UcvRjulYHkl2McWkyGBOE8EgVJfm6CCDMkwggYEMIID7KADAgECAhMzAAACHPrN # xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1 # OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP # oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC # /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf # rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j # qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT # xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT # DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw # YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl # MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC # AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN # rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK # 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK # Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY # BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu # uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE # msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz # 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6 # U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO # 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD # 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC # EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX # DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ # Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq # lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo # 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv # QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a # 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1 # FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO # GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7 # ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ # uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS # CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm # VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3 # SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E # BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX # LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB # Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP # oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw # TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC # AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D # 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY # nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI # vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6 # aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w # PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7 # RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK # /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK # YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw # YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT # Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn+MIIZ+gIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIG+A1ITGynsygCPvulwhHom/OPBNgWVgltYjVnmn45I6MEIGCisG # AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAUnhCxdockR7gLqEK1Ukw # ZdntDbJwc3oHCNAVjzVQRlfbEbnxsVCI0SV+l9gKCLB+RwhCU46TWq6IrEeZlcIG # w6KNGzJ9BvplxqGMPFr24EEVNbiVoUT5EQplGYxMJ+ehVfdyGTxS5H/xUYrA0Zxv # 6FhpLzvg/Gz3t8WUCnwf5FOx0xPRnMBHjg4u2OeTDDhdv+6tfhTexxiRK4hQay7y # YVKUhrw0G+44ozi5LZH9mtcxc1zwunPOcJdlPMN7A3rEr0WzX3fLa8tHeFGpv9s9 # xW894oyORANIJxttk7KU0CXNR9RsbZmvTYmcaT1JwkmqGXiaiquQFM/hWPverqfG # YqGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kwgheF # AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIB # QQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDtAZADLqxdR02+X/Rd # gPYAekSNLfQ4nWSxY4NuI1rPhAIGaeugEigDGBMyMDI2MDUwMzE0MzEwOS45ODda # MASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjoyQTFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIFEKAD # AgECAhMzAAACEKvN5BYY7zmwAAEAAAIQMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgxMloXDTI2MTExMzE4 # NDgxMlowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr # BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG # A1UECxMeblNoaWVsZCBUU1MgRVNOOjJBMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEAjcc4q057ZwIgpKu4pTXWLejvYEduRf+1mIpbiJEMFWWmU2xp # ip+zK7xFxKGB1CclUXBU0/ZQZ6LG8H0gI7yvosrsPEI1DPB/XccGCvswKbAKckng # OuGTEPGk7K/vEZa9h0Xt02b7m2n9MdIjkLrFl0pDriKyz0QHGpdh93X6+NApfE1T # L24Vo0xkeoFGpL3rX9gXhIOF59EMnTd2o45FW/oxMgY9q0y0jGO0HrCLTCZr50e7 # TZRSNYAy2lyKbvKI2MKlN1wLzJvZbbc//L3s1q3J6KhS0KC2VNEImYdFgVkJej4z # ZqHfScTbx9hjFgFpVkJl4xH5VJ8tyJdXE9+vU0k9AaT2QP1Zm3WQmXedSoLjjI7L # WznuHwnoGIXLiJMQzPqKqRIFL3wzcrDrZeWgtAdBPbipglZ5CQns6Baj5Mb6a/EZ # C9G3faJYK5QVHeE6eLoSEwp1dz5WurLXNPsp0VWplpl/FJb8jrRT/jOoHu85qRcd # YpgByU9W7IWPdrthmyfqeAw0omVWN5JxcogYbLo2pANJHlsMdWnxIpN5YwHbGEPC # uosBHPk2Xd9+E/pZPQUR6v+D85eEN5A/ZM/xiPpxa8dJZ87BpTvui7/2uflUMJf2 # Yc9ZLPgEdhQQo0LwMDSTDT48y3sV7Pdo+g5q+MqnJztN/6qt1cgUTe9u+ykCAwEA # AaOCAUkwggFFMB0GA1UdDgQWBBSe42+FrpdF2avbUhlk86BLSH5kejAfBgNVHSME # GDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1l # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsG # AQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB # Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDAN # BgkqhkiG9w0BAQsFAAOCAgEAvs4rO3oo8czOrxPqnnSEkUVq718QzlrIiy7/EW7J # mQXsJoFxHWUF0Ux0PDyKFDRXPJVv29F7kpJkBJJmcQg5HQV7blUXIMWQ1qX0KdtF # QXI/MRL77Z+pK5x1jX+tbRkA7a5Ft7vWuRoAEi02HpFH5m/Akh/dfsbx8wOpecJb # YvuHuy4aG0/tGzOWFCxMMNhGAIJ4qdV87JnY/uMBmiodlm+Gz357XWW5tg3HrtNZ # XuQ0tWUv26ud4nGKJo/oLZHP75p4Rpt7dMdYKUF9AuVFBwxYZYpvgk12tfK+/yOw # q84/fjXVCdM83Qnawtbenbk/lnbc9KsZom+GnvA4itAMUpSXFWrcRkqdUQLN+JrG # 6fPBoV8+D8U2Q2F4XkiCR6EU9JzYKwTuvL6t3nFuxnkLdNjbTg2/yv2j3WaDuCK5 # lSPgsndIiH6Bku2Ui3A0aUo6D9z9v+XEuBs9ioVJaOjf/z+Urqg7ESnxG0/T1dKc # i7vLQ2XNgWFYO+/OlDjtGoma1ijX4m14N9qgrXTuWEGwgC7hhBgp3id/LAOf9BST # WA5lBrilsEoexXBrOn/1wM3rjG0hIsxvF5/YOK78mVRGY6Y7zYJ+uXt4OTOFBwad # Pv8MklreQZLPnQPtiwop4rlLUYaPCiD4YUqRNbLp8Sgyo9g0iAcZYznTuc+8Q8ZI # rgwwggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEB # CwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD # VQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAe # Fw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGm # TOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H # ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc # wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62A # W36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1w # jjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCG # MFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ # 1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP # 8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFz # ymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHz # NgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3 # xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsG # AQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/ # LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG # DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYB # BQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G # A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw # VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j # cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF # BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQEL # BQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfC # cTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF # vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l # 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn # 8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5m # O0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyx # TkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4 # S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9 # y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM # +Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhw # RNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCCAkEC # AQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjoyQTFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa # AxUAOsyf2b6riPKnnXlIgIL2f53PUsKggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO2hUrgwIhgPMjAyNjA1MDMw # NDUxMDRaGA8yMDI2MDUwNDA0NTEwNFowdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA # 7aFSuAIBADAKAgEAAgIP2wIB/zAHAgEAAgITyzAKAgUA7aKkOAIBADA2BgorBgEE # AYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYag # MA0GCSqGSIb3DQEBCwUAA4IBAQC8BD/jbO+hYI1cLwigLLq4vh+KzNiSSPNrYpjK # 81EmkYyZs5aX5AMRAnKzZrP4zsNFkviAjAjcDcR62MFounzMKMdJW5L/Ak/LXwXt # M34DfDJQcdZIA42apu7Gxus4gBy4l6dU2LN+j4ltCPCRJhdMPexSSf+OQbx8kO01 # Je3+DWhdgn9pdujhIj8ifSldthlXtNLStB9fWFll8TzvJx6wr8KKMuvcau4DRbnp # b8VFIDpJYNDEwTAhl9aTGcKSTtPlNK0OXC56AgmwyMbDcy10gMlKbnqlX+/gE2Na # W74yfYadFJ7YPleyZRz57qTs137kpL9OZd3T9tyb2gqNzRj0MYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIQq83kFhjvObAA # AQAAAhAwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQg4pcVTfv15MwGwVg4G+qd4vzLRxFCWlogK7AQ # 4h1VQvowgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCDD1SHufsjzY59S1iHU # QY9hnsKSrJPg5a9Mc4YnGmPHxjCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAACEKvN5BYY7zmwAAEAAAIQMCIEIIT70NF/aOTBRnL/Ym5d # CJI5L5fhiGpS2xzTAo0XyaBPMA0GCSqGSIb3DQEBCwUABIICACFsrwqwmSoW1lgM # MjIynjOyoGiAeBP6B0rFHrpvhlwAL5yr2xqIypxAj79/XqFT9ufmCQh5sQjgx7sK # yCplZJIxhfgeUn0uQvK2jHCGZW6AznVJeXLB2K1UtOFVyHprt8eZ6g0ox9x11jRC # wEbMB850VH1mvLTEQBi0ro+2jb7OMx8x9ytQ1WCgCHD9cYGFkXNtvZ+o/rdDnau/ # 1PdFdqCXrUXwrF7KckugXLDvf+6k79Qw8jic+mbBzybUFQqzmISGAB1VHgdlH8Dw # KJ4Y8AoYeRoy5H/un5vcGsFAE3UBNcsiSoJw5FUB43ddMET6HQCfc9x1tLN/MiSP # qgv49+CyI7yhh0Haw1zoksSeLiGaJn3KQFM3dfAiXOwzOpMtGjCwXOTAlqS+0ATt # 14GwTnZPvRC46LZbBsgCCKeNQa6Let46VCdHU65HaIhibHctznR8bZxL2rBZ2juN # z3nMiPKn+qR6Nu/eblneZn1F07bRxdUco1gBUZQcAm50vLMENieF/8O+rIpZy4Zt # QM1LWoOXA94HB/kE7RaNOCreL3fbztBw2HmkPEAy78IoqWK+GcYI0O1V7Qge0uG0 # BKUvFJxvh71k25X8vxOf7kjCC5ca92hFYHLxsyTK9xdEjwKVwokahovC7rWZGALZ # r39C2hSSlV2QP23jGs2/4f9YUq0y # SIG # End signature block |