Deploy/Classes/EnvironmentValidator/EnvironmentValidator.psm1
<###################################################
# # # Copyright (c) Microsoft. All rights reserved. # # # ##################################################> #using module ..\Common\Role.psm1 $ErrorActionPreference = "Stop" class Role { } class EnvironmentValidator : Role { static EnvironmentValidatorLite([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator during bootstrap on seed node. .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorLite($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Bootstrap' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorFull([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-deployment when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorFull($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorUpgrade([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-upgrade when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorUpgrade($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Upgrade' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorPreUpdate([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run pre-update when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPreUpdate($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PreUpdate' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorPostUpdate([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run post-update when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPostUpdate($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PostUpdate' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorAddNode([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run during AddNode when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorAddNode($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'AddNode' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorPreAddNode([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run before AddNode when full ECE parameters are available, but new node has not be updated in ECE. .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorPreAddNode($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'PreAddNode' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorRecovery([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator and run during Recovery when full ECE parameters are available .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorRecovery($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Recovery' [EnvironmentValidator]::RunValidators($Parameters, $OperationType) } static EnvironmentValidatorReplayResult([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to replay environment validator results from any node after telemetry pipeline is up .EXAMPLE [EnvironmentValidator]::EnvironmentValidatorReplayResult() #> try { Trace-Execution "Searching for environment validator telemetry events to replay." $PsSession = [EnvironmentValidator]::NewPsSessionAllHosts($Parameters) $sb = { Write-Host ("{0} Importing Module" -f $ENV:COMPUTERNAME) Import-Module AzStackHci.EnvironmentChecker Write-Host ("{0} Reading Events" -f $ENV:COMPUTERNAME) $Events = Get-AzStackHciEnvironmentCheckerEvents -Source Telemetry Write-Host ("{0} Finished reading events" -f $ENV:COMPUTERNAME) if ($Events) { Write-Host ("{0} Environment Validator Telemetry Events found on {1}. Replaying." -f $Events.Count, $ENV:COMPUTERNAME) $Events | Foreach-Object { $params = @{ Source = 'AzStackHciEnvironmentChecker/Telemetry' LogName = 'AzStackHciEnvironmentChecker' Message = $PSITEM.Message EventId = $PSITEM.EventId EventType = $PSITEM.EntryType } Write-ETWLog @params } Write-Host "Replay complete." } else { Write-Host ("No Environment Validator Telemetry Events found on {0}. No Action." -f $ENV:COMPUTERNAME) } } Trace-Execution ("Calling script block") Invoke-Command -Session $PsSession -ScriptBlock $sb Trace-Execution ("Finished calling script block") } catch { Trace-Execution ("Error occurred trying to replay environment validator telemetry events. Error: {0}" -f $_) } } static ValidateBitlocker([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment when full ECE parameters are available. .EXAMPLE [EnvironmentValidator]::ValidateBitlocker($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciBitlocker' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateConnectivity([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment when full ECE parameters are available. .EXAMPLE [EnvironmentValidator]::ValidateConnectivity($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciConnectivity' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateExternalAD([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateExternalAD($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciExternalActiveDirectory' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateHardware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateHardware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciHardware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateNetwork([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateNetwork($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciNetwork' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateObservability([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateObservability($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciObservability' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateMOCStack([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateMOCStack($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciMOCStack' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static ValidateSoftware([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { <# .SYNOPSIS This function is used to run environment validator before deployment. .EXAMPLE [EnvironmentValidator]::ValidateSoftware($Parameters) .PARAMETER Parameters The ECE role parameters for this interface. #> $OperationType = 'Deployment' $ValidatorName = 'AzStackHciSoftware' [EnvironmentValidator]::RunSingleValidator($Parameters, $OperationType, $ValidatorName) } static hidden RunSingleValidator([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType, [string] $ValidatorName) { $ErrorActionPreference = "Stop" [EnvironmentValidator]::ImportEnvironmentValidator() $EnvCheckerModuleBase = Get-Module AzStackHci.EnvironmentChecker -ListAvailable | Select-Object -ExpandProperty ModuleBase Import-Module (Join-Path "$EnvCheckerModuleBase\$ValidatorName" "$ValidatorName.psm1") $cmdLetname = "Test-$ValidatorName" $splat = @{ Parameters = $Parameters OperationType = $OperationType FailFast = $true } $ENV:EnvChkrOp = $OperationType Invoke-Expression "$cmdletName @splat" } static hidden [Array] BuildJobExecution([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType) { $ErrorActionPreference = "Stop" [EnvironmentValidator]::ImportEnvironmentValidator() $EnvCheckerModuleBase = Get-Module AzStackHci.EnvironmentChecker -ListAvailable | Select-Object -ExpandProperty ModuleBase $validators = Get-ChildItem -Path $EnvCheckerModuleBase -Filter AzStackHci* -Directory Trace-Execution "Validators found: $($validators.Name -join ',')" # Gather validator list to run $executionJobs = @() class ExecutionJob { [string]$ModuleName [hashTable]$Params [string]$Command [string]$Name [string]$Description [string]$Result [datetime]$StartTime [datetime]$EndTime [int]$DurationSeconds [psobject[]]$FailedResult [string]$ExecutionDetail } foreach ($validator in $validators) { $validatorModulePath = Get-ChildItem -Path $validator.FullName -Filter "$($Validator.Name)*.psm1" if ($validatorModulePath) { Trace-Execution "Importing Validator $($validatorModulePath.Directory.Name)" Import-Module -Name $validatorModulePath.FullName -Force $module = Get-Module $validatorModulePath.Directory.Name $metaData = $module.ExportedVariables.MetaData.value if ($metaData) { Trace-Execution "Checking if $OperationType is applicable to $($Module.Name)" if ($metaData.OperationType -contains $OperationType) { $validatorCommand = Get-Command -Module $module.Name if ($validatorCommand) { Trace-Execution "Found command $validatorCommand in module $($module.Name) for operation type $OperationType" $executionJobs += New-Object ExecutionJob -Property @{ ModuleName = $module.Name Params = @{ Parameters = $Parameters OperationType = $OperationType FailFast = $false } Command = $validatorCommand.Name Result = 'Queued' Name = if ([string]::IsNullOrEmpty($metaData.UIName)) { 'Azure Stack HCI {0}' -f ($validatorCommand.Name -replace 'Test-AzStackHci','') } else { $metaData.UIName } Description = if ([string]::IsNullOrEmpty($metaData.UIDescription)) { "Test {0} requirements" -f ($validatorCommand.Name -replace 'Test-AzStackHci','') } else { $metaData.UIDescription } } } else { Trace-Execution "No command found for $($validator.Name)" } } else { Trace-Execution "$($Module.Name) not applicable for $OperationType. Continuing." } } else { Trace-Execution "No metadata found for $($module.Name)" } } else { Trace-Execution "No PS module found for $($validator.Name)" } } return $executionJobs } static hidden RunValidators([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string] $OperationType) { # Archive files 'AzStackHciEnvironmentChecker.log', 'AzStackHciEnvironmentProgress.json', 'AzStackHciEnvironmentReport.json', 'AzStackHciEnvironmentReport.xml' | ForEach-Object { Get-ChildItem -Path "$env:LocalRootFolderPath\MASLogs" -Filter $PSITEM -ErrorAction SilentlyContinue | ForEach-Object { Rename-Item -Path $PSITEM.FullName -NewName ($PSITEM.fullname -replace '(\.)', ('_{0}.' -f (Get-Date -Format yyyyMMdd-HHmmss))) } } # Determine which jobs need to run. $executionJobs = [EnvironmentValidator]::BuildJobExecution($Parameters,$OperationType) # Run validator list foreach ($work in $executionJobs) { Trace-Execution ("Running & {0} {1}" -f $work.Command,(($work.params.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ';')) $work.StartTime = [System.DateTime]::UtcNow $work.Result = 'In Progress' [EnvironmentValidator]::WriteProgressFile($executionJobs) $splat = $work.Params $command = $work.Command # Execute validator try { $ENV:EnvChkrOp = $splat.OperationType $result = Invoke-Expression "$command @splat" $work.Result = $result.Result $work.FailedResult = $result.FailedResult | Sort-Object Severity $work.ExecutionDetail = $result.ExecutionDetail } catch { $work.Result = 'Error' $work.ExecutionDetail = "Exception occurred ($($work.Command)): $($_.exception.message)" } finally { $work.EndTime = [System.DateTime]::UtcNow $work.DurationSeconds = [System.Math]::Round(([system.datetime]$work.EndTime - [system.datetime]$work.StartTime).TotalSeconds,0) [EnvironmentValidator]::WriteProgressFile($executionJobs) } } Trace-Execution (Get-AzStackHciEnvironmentCheckerProgress -Path C:\MasLogs\AzStackHciEnvironmentProgress.json | Out-String) # First throw any exceptions $exceptions = $executionJobs | Where-Object { $_.ExecutionDetail -match 'Exception occurred' } if ($exceptions) { throw ("Failing action plan. Validator(s) threw exception(s):`n{0}`nLog file: {1}`nProgress file: {2}" -f ` ($exceptions.ExecutionDetail -join "`r`n"),"$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentChecker.log","$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentProgress.json") } # Second throw if there are failed tests $terminateResult = @('Error','Failed','Critical') $terminatingValidators = $executionJobs | Where-Object {$_.Result -in $terminateResult } if ($terminatingValidators) { throw ("Failing action plan. Valdiator(s) failed test(s):`n{0}`nLog file: {1}`nReport file: {2}" -f ` ([EnvironmentValidator]::FormatResultToString($terminatingValidators.FailedResult)), ` "$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentChecker.log", ` "$env:LocalRootFolderPath\MASLogs\AzStackHciEnvironmentReport.json") } } static hidden WriteProgressFile([array] $ExecutionJobs) { # Write a progress file $progressPath = Join-Path -Path "$($env:LocalRootFolderPath)\MASLogs" -ChildPath AzStackHciEnvironmentProgress.json if (-not (Test-Path -Path (Split-Path $progressPath -Parent))) { New-Item -Path (Split-Path $progressPath -Parent) -ItemType Directory -Force | Out-Null } $ExecutionJobs | Select-Object Name, Description, Result, Command, ModuleName, StartTime, EndTime, DurationSeconds, FailedResult, ExecutionDetail | ConvertTo-Json -Depth 5 | Out-File -FilePath $progressPath } static hidden ImportEnvironmentValidator() { if (-not (Get-Module AzStackHci.EnvironmentChecker -ListAvailable)) { $nugetname = [EnvironmentValidator]::InstallEnvironmentValidator() Trace-Execution "Importing $nugetname" $EnvCheckerNugetPath = Get-ASArtifactPath -NugetName $nugetname $EnvCheckerModulePath = Join-Path $EnvCheckerNugetPath "$nugetname.psd1" Import-Module $EnvCheckerModulePath -ErrorAction Stop -Verbose:$false -Force -Global | Out-Null if (-not (Get-Module $nugetname)) { throw "Unable to import AzStackHci.EnvironmentChecker" } } } static hidden [string] InstallEnvironmentValidator() { $nugetSourceFolderPath = "$env:LocalRootFolderPath\CloudDeployment\NuGetStore" $nugetname = "AzStackHci.EnvironmentChecker" $nugetDestination = "$env:LocalRootFolderPath:\Nugetstore" if (-not (Test-Path "$nugetDestination\$nugetname.1*")) { $nugetExePath = "$env:LocalRootFolderPath\tools\nuget.exe" Trace-Execution "Unpacking $nugetname" Invoke-Expression "$nugetExePath install $nugetName -Source $nugetSourceFolderPath -OutputDirectory $nugetDestination -PackageSaveMode 'nuspec' -Prerelease" } return $nugetname } static hidden [PsObject] ParseResult([array] $Result, [string]$Title, [switch]$FailFast) { <# If no result, set Result to skipped and detail to 'no result' If skipped results exist, set Result to skipped and include If warning results exist, set Result to warning and include If critical results exist, set Result to failed #> $statusResult = '' $detail = @() $executionDetail = '' if ($null -eq $Result) { Trace-Warning ("Warning: checking {0} requirements. Review {1}. Returning." -f $Title, "$($env:LocalRootFolderPath)\MasLogs\AzStackHciEnvironmentChecker.log") $executionDetail = 'Validator generated no results.' $statusResult = 'Informational' } # Check for skips and write to log $skipped = @() $skipped = $Result | Where-Object { $_.Status -eq 'Skipped' } if ($skipped) { Trace-Warning ("{0} validation has skipped results: {1}" -f $Title, ($skipped | Format-List | Out-String)) # if there are no more results then the overall Result is skipped if (-not ($Result | Where-Object { $_.Status -ne 'Skipped' })) { $statusResult = 'Informational' $detail += $skipped $executionDetail = 'All tests were skipped' } } # Check for warnings $warningFailures = @() $warningFailures = $Result | Where-Object { $_.Status -eq 'Failed' -and $_.Severity -eq 'Warning' } if ($warningFailures) { $warningResultString = [EnvironmentValidator]::FormatResultToString($warningFailures) Trace-Warning ("Warning (non-blocking) {0} failures found in validation. {1} Rules failed: {2}" -f $Title,$warningFailures.count,($warningResultString -join "")) $statusResult = 'Warning' $detail += $warningFailures } # Check for critical failures $criticalFailures = @() $criticalFailures = $Result | Where-Object { $_.Status -eq 'Failed' -and $_.Severity -eq 'Critical' } if ($criticalFailures) { Trace-Warning ("Critical (blocking) {0} failures found in validation. {1} Rules failed: " -f $Title, $criticalFailures.Count) $criticalResultString = [EnvironmentValidator]::FormatResultToString($criticalFailures) $terminalFailureMsg = ("{0} requirements not met. Review output and remediate: {1}" -f $Title, ($criticalResultString -join "")) if ($FailFast) { throw $terminalFailureMsg } Trace-Warning $terminalFailureMsg $statusResult = 'Critical' $detail += $criticalFailures } if (-not [string]::IsNullOrEmpty($Result) -and -not $criticalFailures -and -not $warningFailures) { Trace-Execution -Verbose "$Title prerequisites met." $statusResult = 'Succeeded' } return (New-Object -TypeName PSObject -Property @{ Result = $statusResult FailedResult = if ($detail.count -gt 0) { $detail } else { $null } ExecutionDetail = $executionDetail }) } static hidden [string] FormatResultToString([PSObject] $FailedResult) { $failureDetail = @() $failureDetail = $FailedResult | Foreach-Object { Write-Output "`nRule:" $PSITEM | Select-Object -Property * -ExcludeProperty AdditionalData | Format-List | Out-String Write-Output "AdditionalData:" $PSITEM.AdditionalData | Where-Object Status -eq 'Failed' | Format-List | Out-String } return $failureDetail } static hidden [PSCredential] GetLocalAdminCredential([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $cloudRole = $Parameters.Roles["Cloud"].PublicConfiguration # Account info $securityInfo = $cloudRole.PublicInfo.SecurityInfo $localAdmin = $securityInfo.LocalUsers.User | ? Role -EQ $Parameters.Configuration.Role.PrivateInfo.Accounts.BuiltInAdminAccountID $localAdminCredential = $Parameters.GetCredential($localAdmin.Credential) Trace-Execution ("Found credential {0}" -f $localAdminCredential.Username ) return $localAdminCredential } static hidden [PSCredential] GetDomainAdminCredential([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $cloudRole = $Parameters.Roles["Cloud"].PublicConfiguration # Account info $securityInfo = $cloudRole.PublicInfo.SecurityInfo $domainFQDN = $Parameters.Roles["Domain"].PublicConfiguration.PublicInfo.DomainConfiguration.FQDN $domainAdminUser = $securityInfo.DomainUsers.User | ? Role -EQ $Parameters.Configuration.Role.PrivateInfo.Accounts.DomainAdminAccountID $Credential = $Parameters.GetCredential($domainAdminUser.Credential) $domainAdminCredential = New-Object pscredential("$domainFQDN\$($Credential.UserName)", $Credential.Password) return $domainAdminCredential } static hidden [string[]] GetAnswerFile() { # Return the deployment data $deploymentData = Get-Content "$($env:LocalRootFolderPath)\CloudDeployment\Unattended.json" | ConvertFrom-Json return $deploymentData } static hidden [string[]] GetAllHostNicIps([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { # Find the IP for the contect node for PSSession [System.Array]$NodesIps = Get-NetworkMgmtIPv4FromECEForAllHosts -Parameters $Parameters Trace-Execution ("Found host IPs {0}" -f ($NodesIps -join ',')) return $NodesIps.Values } static hidden [string[]] GetHostNicIpByName([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string]$NodeName) { # Find the IP for the contect node for PSSession [System.Array]$NodesIps = Get-NetworkMgmtIPv4FromECEForHost -Parameters $Parameters -HostName $NodeName Trace-Execution ("Found host IP {0} for host {1}" -f ($NodesIps -join ','),$NodeName) return $NodesIps } static hidden [System.Management.Automation.Runspaces.PSSession[]] NewPsSessionAllHosts([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $Nodes = [EnvironmentValidator]::GetAllHostNicIps($Parameters) if ([string]::IsNullOrEmpty(($nodes | Select-Object -first 1))) { # As a back up try the local cluster e.g. during upgrade Trace-Execution "Unable to find nodes in ECE parameters, querying local cluster." $Nodes = Get-ClusterNode | Select-Object -ExpandProperty Name } [EnvironmentValidator]::SetTrustedHosts($Nodes) Trace-Execution ("Making PsSession to {0}" -f ($Nodes -join ',')) [System.Array]$PsSession = $Nodes | ForEach-Object { [EnvironmentValidator]::NewPsSessionWithRetries($PSITEM,$Parameters) } return $PsSession } static hidden [System.Management.Automation.Runspaces.PSSession[]] NewPsSessionByHost([CloudEngine.Configurations.EceInterfaceParameters] $Parameters, [string[]]$Node, [switch]$IncludeLocalHost) { $Ips = @() $Node | ForEach-Object { $Ips += [EnvironmentValidator]::GetHostNicIpByName($Parameters,$PSITEM) } if ($IncludeLocalHost) { $Ips += 'localhost' } [EnvironmentValidator]::SetTrustedHosts($Ips) Trace-Execution ("Making PsSession to {0}" -f ($Ips -join ',')) [System.Array]$PsSession = $Ips | ForEach-Object { [EnvironmentValidator]::NewPsSessionWithRetries($PSITEM,$Parameters) } return $PsSession } static hidden [System.Management.Automation.Runspaces.PSSession] NewPsSessionWithRetries([string]$Node, [CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { function New-PsSessionWithRetries { param ( [string]$node, [pscredential]$Credential, [int]$retries = 3, [int]$waitSeconds = 30 ) For ($i=1; $i -le $retries; $i++) { try { Trace-Execution "Creating PsSession ($i/$retries) to $Node as $($Credential.UserName)..." $PsSession = Microsoft.PowerShell.Core\New-PSSession -ComputerName $Node -Credential $Credential -ErrorAction Stop $computerName = Microsoft.PowerShell.Core\Invoke-Command -Session $PsSession -ScriptBlock { $ENV:COMPUTERNAME } -ErrorAction Stop break } catch { Trace-Execution "Creating PsSession ($i/$retries) to $Node failed: $($_.exception.message)" $errMsg = $_.tostring() Start-Sleep -Seconds $waitSeconds } } if ($computerName -and $PsSession) { Trace-Execution ("PsSession to {0} created after {1} retries. (Remote machine name: {2})" -f $Node, ("$i/$retries"), $computerName) return $PsSession } else { throw "Unable to create a valid session to $Node`: $errMsg" } } # Check if we can connect with domain credentials # Otherwise connect with local credentials $domainCredential = [EnvironmentValidator]::GetDomainAdminCredential($Parameters) if (Test-WSMan -ComputerName $Node -Credential $domainCredential -Authentication Default -ErrorAction SilentlyContinue) { Trace-Execution "Attempting PsSession $Node with domain credentials" $PsSession = New-PsSessionWithRetries -Node $Node -Credential $domainCredential } else { Trace-Execution "Attempting PsSession $Node with local credentials" $localCredential = [EnvironmentValidator]::GetLocalAdminCredential($Parameters) if ($null -eq $localCredential) { throw "Unable to create a valid session to $Node. No local admin credential found." } $PsSession = New-PsSessionWithRetries -Node $Node -Credential $localCredential } if ($PsSession) { return $PsSession } else { throw "Unable to create a valid session to $Node" } } static hidden [string[]] GetNodeContext([CloudEngine.Configurations.EceInterfaceParameters] $Parameters) { $nodeName = @() $executionRoleName = $Parameters.Context.ExecutionContext.Roles.Role.RoleName $nodeName = $Parameters.Context.ExecutionContext.Roles.Role.Nodes.Node.Name if ($null -eq $nodeName) { # Try runtimeParameters $runtimeParameters = $Parameters.RunInformation['RuntimeParameter'] if ($runtimeParameters.ContainsKey('NodeName')) { $nodeName = $runtimeParameters['NodeName'] Trace-Execution "Retrieved node(s): $($nodeName -join ', ') from runtimeParameters." } else { # If node names are not provided, get all nodes associated to the $executionRoleName Trace-Execution "Retrieving ExecutionContext with role name $executionRoleName and node(s): $($nodeName -join ', ')" $nodeName = ($Parameters.Roles[$executionRoleName].PublicConfiguration.Nodes.Node.Name) | Select-Object -Unique } } else { Trace-Execution "Retrieved node(s): $($nodeName -join ', ') from ExecutionContext." } return $nodeName } static hidden SetTrustedHosts([string[]]$Nodes) { $trustedHosts = (Get-Item -Path WSMan:\localhost\Client\TrustedHosts).Value foreach ($node in $nodes) { if ('*' -notin $TrustedHosts -and ($node -notin $TrustedHosts.Split(','))) { Trace-Execution "Adding $node to TrustedHosts" Set-Item WSMan:\localhost\Client\TrustedHosts -Value $node -Concatenate -Force } else { Trace-Execution "TrustedHosts already matches $node. Continuing." } } } } # SIG # Begin signature block # MIInkwYJKoZIhvcNAQcCoIInhDCCJ4ACAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDq/jZazpWco+Zr # +He3cfbhYO2lLi5KB27fFHDwDzoZPqCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU # p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1 # 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm # WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa # +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq # jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw # RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW # MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci # tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG # CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0 # MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk # mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31 # TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2 # kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d # hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM # pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh # JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX # UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir # IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8 # 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A # Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H # tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq # hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 # IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg # Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03 # a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr # rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg # OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy # 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9 # sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh # dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k # A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB # w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn # Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90 # lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w # ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o # ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa # BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG # AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV # HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG # AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl # AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb # C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l # hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6 # I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0 # wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560 # STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam # ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa # J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah # XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA # 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt # Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr # /Xmfwb1tbWrJUnMTDXpQzTGCGXMwghlvAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIOz2Kg6ebfEHOpaU43UEK0N4 # 3T6G+C9gCkIEhau1/S+YMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEALlz/G+882lXAA1jTvfsdDyN3CGfDKAa2hGyCFDfO2XVceaBpjOKXGPrN # rsV/YkgL7LV8mTpr0KRYLJRZC1btECAYB8814te5nih9mp3G/w03kYI+NWyTck31 # Hg0b0ql8cpkbAzPD5ij3QlsNTiz51jONXCiR2rcWGY+GJLGCZNSK+ptvgOzDhJBh # 2mxZpL3l4xuVTWXR79Iduhj3UGMMrvctCI75iwTK00Js7TNC7I+LOYQyPXJoJBxr # XLLkO5d+yYxDzMRszV0NlXbxwKQw7xepp8PudWWbGvg3FKN680qeCNpEYDLGieir # PQBSqlbrOTnrAAYLqlP0yg3gpUva+6GCFv0wghb5BgorBgEEAYI3AwMBMYIW6TCC # FuUGCSqGSIb3DQEHAqCCFtYwghbSAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFRBgsq # hkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCCQ80vhN6eiQcv344XsPJjCbylPQeXQBkExtkvwrbHiXAIGZDfqPGry # GBMyMDIzMDUwNDE1NTkzMC4wMTVaMASAAgH0oIHQpIHNMIHKMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoxMkJDLUUz # QUUtNzRFQjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCC # EVQwggcMMIIE9KADAgECAhMzAAAByk/Cs+0DDRhsAAEAAAHKMA0GCSqGSIb3DQEB # CwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTIyMTEwNDE5MDE0 # MFoXDTI0MDIwMjE5MDE0MFowgcoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMx # JjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjEyQkMtRTNBRS03NEVCMSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAwwGcq9j50rWEkcLSlGZLweUVfxXRaUjiPsyaNVxPdMRs # 3CVe58siu/EkaVt7t7PNTPko/s8lNtusAeLEnzki44yxk2c9ekm8E1SQ2YV9b8/L # OxfKapZ8tVlPyxw6DmFzNFQjifVm8EiZ7lFRoY448vpcbBD18qjYNF/2Z3SQchcs # dV1N9Y6V2WGl55VmLqFRX5+dptdjreBXzi3WW9TsoCEWcYCBK5wYgS9tT2SSSTza # e3jmdw40g+LOIyrVPF2DozkStv6JBDPvwahXWpKGpO7rHrKF+o7ECN/ViQFMZyp/ # vxePiUABDNqzEUI8s7klYmeHXvjeQOq/CM3C/Y8bj3fJObnZH7eAXvRDnxT8R6W/ # uD1mGUJvv9M9BMu3nhKpKmSxzzO5LtcMEh2tMXxhMGGNMUP3DOEK3X+2/LD1Z03u # sJTk5pHNoH/gDIvbp787Cw40tsApiAvtrHYwub0TqIv8Zy62l8n8s/Mv/P764CTq # rxcXzalBHh+Xy4XPjmadnPkZJycp3Kczbkg9QbvJp0H/0FswHS+efFofpDNJwLh1 # hs/aMi1K/ozEv7/WLIPsDgK16fU/axybqMKk0NOxgelUjAYKl4wU0Y6Q4q9N/9Pw # AS0csifQhY1ooQfAI0iDCCSEATslD8bTO0tRtqdcIdavOReqzoPdvAv3Dr1XXQ8C # AwEAAaOCATYwggEyMB0GA1UdDgQWBBT6x/6lS4ESQ8KZhd0RgU7RYXM8fzAfBgNV # HSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5o # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU # aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwG # CCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRz # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNV # HRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IC # AQDY0HkqCS3KuKefFX8/rm/dtD9066dKEleNqriwZqsM4Ym8Ew4QiqOqO7mWoYYY # 4K5y8eXSOHKNXOfpO6RbaYj8jCOcJAB5tqLl5hiMgaMbAVLrl1hlix9sloO45LON # 0JphKva3D6AVKA7P78mA9iRHZYUVrRiyfvQjWxmUnxhis8fom92+/RHcEZ1Dh5+p # 4gzeeL84Yl00Wyq9EcgBKKfgq0lCjWNSq1AUG1sELlgXOSvKZ4/lXXH+MfhcHe91 # WLIaZkS/Hu9wdTT6I14BC97yhDsZWXAl0IJ801I6UtEFpCsTeOyZBJ7CF0rf5lxJ # 8tE9ojNsyqXJKuwVn0ewCMkZqz/cEwv9FEx8QmsZ0ZNodTtsl+V9dZm+eUrMKZk6 # PKsKArtQ+jHkfVsHgKODloelpOmHqgX7UbO0NVnIlpP55gQTqV76vU7wRXpUfz7K # hE3BZXNgwG05dRnCXDwrhhYz+Itbzs1K1R8I4YMDJjW90ASCg9Jf+xygRKZGKHjo # 2Bs2XyaKuN1P6FFCIVXN7KgHl/bZiakGq7k5TQ4OXK5xkhCHhjdgHuxj3hK5AaOy # +GXxO/jbyqGRqeSxf+TTPuWhDWurIo33RMDGe5DbImjcbcj6dVhQevqHClR1OHSf # r+8m1hWRJGlC1atcOWKajArwOURqJSVlThwVgIyzGNmjzjCCB3EwggVZoAMCAQIC # EzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoX # DTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC # 0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VG # Iwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP # 2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/P # XfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361 # VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwB # Sru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9 # X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269e # wvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDw # wvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr # 9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+e # FnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAj # BgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+n # FV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEw # PwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9j # cy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3 # FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAf # BgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBH # hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNS # b29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF # BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0Nl # ckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4Swf # ZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTC # j/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu # 2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/ # GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3D # YXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbO # xnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqO # Cb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I # 6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0 # zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaM # mdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNT # TY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggLLMIICNAIBATCB+KGB0KSBzTCByjEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWlj # cm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBF # U046MTJCQy1FM0FFLTc0RUIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1w # IFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAKOO55cMT4syPP6nClg2IWfajMqkoIGD # MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEF # BQACBQDn/hamMCIYDzIwMjMwNTA0MTkzMzU4WhgPMjAyMzA1MDUxOTMzNThaMHQw # OgYKKwYBBAGEWQoEATEsMCowCgIFAOf+FqYCAQAwBwIBAAICBTIwBwIBAAICEcYw # CgIFAOf/aCYCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgC # AQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQAb+lFGtJKOF99e # fmn/E0/ghoTARGgQti2NSNKftotSYqsx9oz6M1mIEjn/KTiOO+BHcPqn+MFaqr5g # 4PD4wyK2paivDW1k9lbu9MYkdL5AoEXGqnezr0SC74P30xZKCLwnwGPUtoAtbHGr # vXFj8t8f0Ww2mWqcGmfQj1CShC1EmDGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBU # aW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAByk/Cs+0DDRhsAAEAAAHKMA0GCWCGSAFl # AwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcN # AQkEMSIEIFywfdQtXiNMyYHYEKhSiC9GeO896Mjvtr20LWPU974KMIH6BgsqhkiG # 9w0BCRACLzGB6jCB5zCB5DCBvQQgEz0b85vrVU2slZAk4jt1SDEk6IzZAwVCoWwF # 3KzcGuAwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA # AcpPwrPtAw0YbAABAAAByjAiBCCKlhbXxfDFq9bNI7jYvFnFW6Qft8QqjcFUmR/B # knLbxjANBgkqhkiG9w0BAQsFAASCAgChLv0D8KMKbfeaiao2lnW26BLEd6PBb0HN # qLIloP3U3MwJU9mKr40MDvMPjH+ZthjcIvJEXJszz0TA28Bi3C/Vpgcu8i5bczRe # 35zFiGIOcAenaRylwsHPl2Z9IiiAjw03Eq789NgVR+bhZY+fv8tzkEzZYaIGVZef # kOekHhze4QwtD691rDF1bamb95cABw2K0JLjY7oRFa1JFHDfM8pN0Z9KG8N1QvDm # NAV5J1Y+sAR+r68BLnHX3/w1xipWbAOGCECuH2hwgQgIvjhaM3V4lch9By0VDng5 # Tf1wpIkdWTAiIty1ZCL5v3bc6719+HgeVSqmoQWL6iszY+9P7TGDKhdyrVKty4Ze # 7L5MMp+HqmJJKQEqudIcE2WhB4l071PZeJz9Gu1wRTEUWAaZV/hNEwM2XU11c+je # T2CZ6kA53DPpzxEzmiI4uZVzDAV8ft8d+burhonGo4hEUfmet78H0rruCUKxFlIz # lyFq8ibT/UoDDTs/Vbvf9Zg5V+N+sPpMMTUFlyRjpDcyDBe2LaAp1LsMQBK1kEsP # xJzIltaYHEE0uha1Q7/cJlasNUfmlEwJ9e3UfeANQq4pLIJPSC7E9OYRwY9KeYuA # UTXSa0hNbVNVmmhhjXByyHvvcDpIL2e771PBuYrKUAOZmq5+MK6G96aLyBFFGbmE # dt312b09VQ== # SIG # End signature block |