AzStackHciNetwork/AzStackHci.Network.Helpers.psm1
class HealthModel { # Attributes for Azure Monitor schema [string]$Name #Name of the individual test/rule/alert that was executed. Unique, not exposed to the customer. [string]$Title #User-facing name; one or more sentences indicating the direct issue. [string]$Severity #Severity of the result (Critical, Warning, Informational, Hidden) – this answers how important the result is. Critical is the only update-blocking severity. [string]$Description #Detailed overview of the issue and what impact the issue has on the stamp. [psobject]$Tags #Key-value pairs that allow grouping/filtering individual tests. For example, "Group": "ReadinessChecks", "UpdateType": "ClusterAware" [string]$Status #The status of the check running (i.e. Failed, Succeeded, In Progress) – this answers whether the check ran, and passed or failed. [string]$Remediation #Set of steps that can be taken to resolve the issue found. [string]$TargetResourceID #The unique identifier for the affected resource (such as a node or drive). [string]$TargetResourceName #The name of the affected resource. [string]$TargetResourceType #The type of resource being referred to (well-known set of nouns in infrastructure, aligning with Monitoring). [datetime]$Timestamp #The Time in which the HealthCheck was called. [psobject[]]$AdditionalData #Property bag of key value pairs for additional information. [string]$HealthCheckSource #The name of the services called for the HealthCheck (I.E. Test-AzureStack, Test-Cluster). } class AzStackHciNetworkTarget : HealthModel {} Import-LocalizedData -BindingVariable lnTxt -FileName AzStackHci.Network.Strings.psd1 function Test-MgmtIpRange { [CmdletBinding()] param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end Management IP Range")] [System.Net.IPAddress] $EndingAddress, [int[]] $port = @(5986, 5985, 22), [int] $Timeout = 1000, [int] $Minimum = 5, [int] $Maximum = 255 ) try { # Check same subnet $AdditionalData = @() $TestMgmtSubnet = TestMgmtSubnet -StartingAddress $StartingAddress -EndingAddress $EndingAddress $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'CustomerNetwork' Resource = 'CustomerSubnet' Detail = if ($TestMgmtSubnet) { $lnTxt.TestMgmtSubnetPass -f $StartingAddress, $EndingAddress } else { $lnTxt.TestMgmtSubnetFail -f $StartingAddress, $EndingAddress } Status = if ($TestMgmtSubnet) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } # Check range size $TestMgmtRangeSize = TestMgmtRangeSize -StartingAddress $StartingAddress -EndingAddress $EndingAddress -Minimum $Minimum -Maximum $Maximum $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'CustomerNetwork' Resource = 'CustomerRange' Detail = if ($TestMgmtRangeSize) { $lnTxt.TestMgmtRangeSizePass -f $Minimum, $Maximum } else { $lnTxt.TestMgmtRangeSizeFail -f $Minimum, $Maximum } Status = if ($TestMgmtRangeSize) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } # Get IP in Range $MgmtIpRange = GetMgmtIpRange -StartingAddress $StartingAddress -EndingAddress $EndingAddress foreach ($Ip in $MgmtIpRange) { $result = @{} $result += @{ 'Ping' = Test-NetConnection -ComputerName $Ip -InformationLevel Quiet -WarningAction SilentlyContinue } foreach ($p in $port) { $result += @{ $p = IsTcpPortInUse -Ip $ip -Port $p -Timeout $Timeout } } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = $Ip Resource = 'ICMP/SSH/WINRM' Detail = ($result.Keys | ForEach-Object { "{0}:{1}" -f $psitem,$result[$psitem] }) -join ', ' Status = if ($result.Values -contains $true) { 'Failed' } else { 'Succeeded' } TimeStamp = [datetime]::UtcNow } $msg = $lnTxt.ActiveHostCheck -f $ip, (($result.Keys | ForEach-Object { "{0}:{1}" -f $psitem,$result[$psitem] }) -join ', ') $Type = if ($result.Values -contains $true) { 'Warning' } else { 'Info' } Log-Info $msg -Type $Type } $instanceResult = New-Object AzStackHciNetworkTarget $instanceResult.Name = 'AzStackHci_Network_Test_Management_IP_Range' $instanceResult.Title = 'Test Management IP Range' $instanceResult.Severity = 'Critical' $instanceResult.Description = 'Checking no hosts respond on IP range' $instanceResult.Remediation = 'https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-tool-prerequisites#network-requirements' $instanceResult.TargetResourceID = 'ManagementIPRange' $instanceResult.TargetResourceName = 'ManagementIPRange' $instanceResult.TargetResourceType = 'Network Range' $instanceResult.Timestamp = [datetime]::UtcNow $instanceResult.HealthCheckSource = $ENV:EnvChkrId $instanceResult.Status = if ($AdditionalData.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $instanceResult.AdditionalData = $AdditionalData return $instanceResult } catch { throw $_ } } # Initial tests to determine if Mgmt IP of new Node is OK function TestMgmtIPForNewNode { [CmdletBinding()] param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting infra IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end infra IP Range")] [System.Net.IPAddress] $EndingAddress, [System.Management.Automation.Runspaces.PSSession] $PsSession, [Hashtable] $NodeToManagementIPMap, [String] $FirstAdapterName, [String] $IntentName ) try { $sb = { $env:COMPUTERNAME ( Get-NetIPConfiguration | Where-Object { $_.IPv4DefaultGateway -ne $null -and $_.NetAdapter.Status -ne "Disconnected" } ).IPv4Address.IPAddress } $NewNodeData = Invoke-Command $PsSession -ScriptBlock $sb $NodeName = $NewNodeData[0] $NodeManagementIPAddress = $NewNodeData[1] Log-Info "Node Name retrieved from PSSession: $NodeName" Log-Info "Node Management IP Address retrieved from PSSession: $NodeManagementIPAddress" # Check node management IP is not in infra pool range Log-Info "Starting Test Mgmt IP is not in Infra IP Pool for $($psSession.ComputerName)" $ip = [system.net.ipaddress]::Parse($NodeManagementIPAddress).GetAddressBytes() [array]::Reverse($ip) $ip = [system.BitConverter]::ToUInt32($ip, 0) $from = [system.net.ipaddress]::Parse($StartingAddress).GetAddressBytes() [array]::Reverse($from) $from = [system.BitConverter]::ToUInt32($from, 0) $to = [system.net.ipaddress]::Parse($EndingAddress).GetAddressBytes() [array]::Reverse($to) $to = [system.BitConverter]::ToUInt32($to, 0) $AdditionalData = @() $mgmtIPOutsideRange = ($ip -le $from) -or ($ip -ge $to) if ($mgmtIPOutsideRange) { $TestMgmtIPInfraRangeDetail = $lnTxt.TestMgmtIPInfraRangePass -f $NodeManagementIPAddress, $StartingAddress, $EndingAddress } else { $TestMgmtIPInfraRangeDetail = $lnTxt.TestMgmtIPInfraRangeFail -f $NodeManagementIPAddress, $StartingAddress, $EndingAddress Log-Info $TestMgmtIPInfraRangeDetail -Type Warning } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = $PsSession.ComputerName Resource = 'NewNodeManagementIP' Detail = $TestMgmtIPInfraRangeDetail Status = if ($mgmtIPOutsideRange) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } # Check that no management IPs are the same (Mgmt IP shouldn't conflict with existing node) Log-Info "Starting Test for No Mgmt IPs are the same for any Nodes" $duplicateIPs = $false $numDuplicates = $NodeToManagementIPMap.GetEnumerator() | Group-Object Value | ? { $_.Count -gt 1 } if ($numDuplicates -ne $null) { $duplicateIPs = $true Log-Info 'Duplicate IPs found for Node Management IPs' -Type Warning } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'NodeAndManagementIPMapping' Resource = 'NodeManagementIPs' Detail = if ($duplicateIPs) { 'Duplicate IPs found for Node Management IPs' } else { 'No Duplicate IPs found for Node Management IPs' } Status = if ($duplicateIPs) { 'Failed' } else { 'Succeeded' } TimeStamp = [datetime]::UtcNow } # Check that host name exists, and the name and mgmt IP both match current node Log-Info "Starting Test to check if Mgmt IP is on a different node as $NodeName" Log-Info "Starting simultaneous Test to check if HostName and Mgmt IP Match for $NodeName" $ipOnAnotherNode = $false $NodeNameAndIPMatches = $false $nodeNameForIP = $null foreach ($NodeIP in $NodeToManagementIPMap.GetEnumerator()) { Write-Host "$($NodeIP.Name): $($NodeIP.Value)" if ($NodeIP.Name -eq $NodeName) { if ($NodeIP.Value -eq $NodeManagementIPAddress) { $NodeNameAndIPMatches = $true $nodeNameForIP = $NodeIP.Name } } else { if ($NodeIP.Value -eq $NodeManagementIPAddress) { $ipOnAnotherNode = $true $nodeNameForIP = $NodeIP.Name } } } if ($ipOnAnotherNode) { $CheckMgmtIPNotOnOtherNodeDetail = $lnTxt.CheckMgmtIPNotOnOtherNodeFail -f $NodeManagementIPAddress, $nodeNameForIP Log-Info $CheckMgmtIPNotOnOtherNodeDetail -Type Warning } else { $CheckMgmtIPNotOnOtherNodeDetail = $lnTxt.CheckMgmtIPNotOnOtherNodePass -f $NodeManagementIPAddress, $nodeNameForIP } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'NodeAndManagementIPMapping' Resource = 'NodeNameAndManagementIP' Detail = $CheckMgmtIPNotOnOtherNodeDetail Status = if ($ipOnAnotherNode) { 'Failed' } else { 'Succeeded' } TimeStamp = [datetime]::UtcNow } if ($NodeNameAndIPMatches) { $CheckMgmtIPOnNewNodeDetail = $lnTxt.CheckMgmtIPOnNewNodePass -f $NodeManagementIPAddress, $nodeNameForIP } else { $CheckMgmtIPOnNewNodeDetail = $lnTxt.CheckMgmtIPOnNewNodeFail -f $NodeManagementIPAddress, $nodeNameForIP Log-Info $CheckMgmtIPOnNewNodeDetail -Type Warning } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'NodeAndManagementIPMapping' Resource = 'NewNodeNameAndManagementIP' Detail = $CheckMgmtIPOnNewNodeDetail Status = if ($NodeNameAndIPMatches) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } # Check that New Node has the first physical adapter and the physical adapter has the mgmt IP Log-Info "Starting Test to see if $FirstAdapterName on $NodeName has the correct Mgmt IP" $adapterSB = { param($adapterName) $returnDict = @{} $returnDict["GetNetIPAddressOutput"] = Get-NetIPAddress $returnDict["GetNetAdapterOutput"] = Get-NetAdapter $AdapterIPObject = Get-NetIPAddress -InterfaceAlias $adapterName -AddressFamily IPv4 -ErrorAction SilentlyContinue if ($AdapterIPObject -eq $null) { $returnDict["Result"] = $false $returnDict["AdapterName"] = $adapterName return $returnDict } $returnDict["Result"] = $true $returnDict["AdapterName"] = $adapterName $returnDict["AdapterIP"] = $AdapterIPObject.IPAddress return $returnDict } $AdapterContainsMgmtIP = $false $physicalAdapterExists = $false $VirtualNICName = "vManagement($IntentName)" try { $NewNodeAdapterData = Invoke-Command $PsSession -ScriptBlock $adapterSB -ArgumentList $FirstAdapterName Log-Info "Data found for New Node Adapter ($FirstAdapterName): $($NewNodeAdapterData | Out-String)" if ($NewNodeAdapterData['Result'] -eq $false) { Log-Info "Physical Adapter Not Found" Log-Info "Get-NetIPAddress output: $($NewNodeAdapterData['GetNetIPAddressOutput'] | Out-String)" Log-Info "Get-NetAdapter output: $($NewNodeAdapterData['GetNetAdapterOutput'] | Out-String)" } elseif ($NewNodeAdapterData['Result'] -eq $true -and $NewNodeAdapterData['AdapterIP'] -eq $NodeManagementIPAddress) { Log-Info "Physical Adapter found with Correct IP: $($NewNodeAdapterData['AdapterIP'] | Out-String)" $physicalAdapterExists = $true $AdapterContainsMgmtIP = $true $CheckAdapterContainsIPDetail = $lnTxt.CheckAdapterContainsIPPass -f $FirstAdapterName, $NodeManagementIPAddress } else { Log-Info "Physical Adapter found but with incorrect IP" Log-Info "Get-NetIPAddress output: $($NewNodeAdapterData['GetNetIPAddressOutput'] | Out-String)" Log-Info "Get-NetAdapter output: $($NewNodeAdapterData['GetNetAdapterOutput'] | Out-String)" } # In certain cases, new node will be set up with the vNIC instead and need to check that for mgmt IP if (!$physicalAdapterExists) { Log-Info "Physical Adapter does not exist or mgmt IP is wrong. Checking Virtual Adapter" -Type Warning $NewNodeVirtualAdapterData = Invoke-Command $PsSession -ScriptBlock $adapterSB -ArgumentList $VirtualNICName Log-Info "Data found for New Node Virtual Adapter ($VirtualNICName): $($NewNodeVirtualAdapterData | Out-String)" if ($NewNodeVirtualAdapterData['Result'] -eq $false) { Log-Info "Virtual Adapter Not Found" Log-Info "Get-NetIPAddress output: $($NewNodeVirtualAdapterData['GetNetIPAddressOutput'] | Out-String)" Log-Info "Get-NetAdapter output: $($NewNodeVirtualAdapterData['GetNetAdapterOutput'] | Out-String)" } elseif ($NewNodeVirtualAdapterData['Result'] -eq $true -and $NewNodeVirtualAdapterData['AdapterIP'] -eq $NodeManagementIPAddress) { Log-Info "Virtual Adapter found with Correct IP: $($NewNodeVirtualAdapterData['AdapterIP'] | Out-String)" $AdapterContainsMgmtIP = $true $CheckAdapterContainsIPDetail = $lnTxt.CheckAdapterContainsIPPass -f $VirtualNICName, $NodeManagementIPAddress } else { Log-Info "Virtual Adapter found but with incorrect IP" Log-Info "Get-NetIPAddress output: $($NewNodeVirtualAdapterData['GetNetIPAddressOutput'] | Out-String)" Log-Info "Get-NetAdapter output: $($NewNodeVirtualAdapterData['GetNetAdapterOutput'] | Out-String)" } } } catch { Log-Info "Exception thrown when checking New Node Adapter: $_" -Type Warning } if (!$AdapterContainsMgmtIP) { $CheckAdapterContainsIPDetail = $lnTxt.CheckAdapterContainsIPFail -f $FirstAdapterName, $VirtualNICName, $NodeManagementIPAddress Log-Info $CheckAdapterContainsIPDetail -Type Warning } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = 'NewNodeAdapter' Resource = 'NewNodeAdapterIP' Detail = $CheckAdapterContainsIPDetail Status = if ($AdapterContainsMgmtIP) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } $instanceResult = New-Object AzStackHciNetworkTarget $instanceResult.Name = 'AzStackHci_Network_Test_New_Node_Validity' $instanceResult.Title = 'Test New Node Configuration Validity' $instanceResult.Severity = 'Critical' $instanceResult.Description = 'Checking New Node values' $instanceResult.Remediation = 'https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-tool-checklist' $instanceResult.TargetResourceID = 'NodeNameMgmtIPConfiguration' $instanceResult.TargetResourceName = 'NodeNameMgmtIPConfiguration' $instanceResult.TargetResourceType = 'NodeMgmtIPConfiguration' $instanceResult.Timestamp = [datetime]::UtcNow $instanceResult.HealthCheckSource = $ENV:EnvChkrId $instanceResult.Status = if ($AdditionalData.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $instanceResult.AdditionalData = $AdditionalData return $instanceResult } catch { throw $_ } } function TestMgmtSubnet { <# .SYNOPSIS Ensure Start and End IPs are on the same subnet. #> param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end Management IP Range")] [System.Net.IPAddress] $EndingAddress ) try { $start = $StartingAddress -replace "\.[0-9]{1,3}$", "" $end = $EndingAddress -replace "\.[0-9]{1,3}$", "" if ($start -eq $end) { Log-info "Subnet start: $start and end: $end" return $true } else { return $false } } catch { throw "Failed to check subnet. Error: $_" } } function GetMgmtIpRange { param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end Management IP Range")] [System.Net.IPAddress] $EndingAddress ) try { $first3 = $StartingAddress -replace "\.[0-9]{1,3}$", "" $start = $StartingAddress -split "\." | Select-Object -Last 1 $end = $EndingAddress -split "\." | Select-Object -Last 1 $range = $start..$end | ForEach-Object { ([System.Net.IPAddress]("{0}.{1}" -f $first3, $PSITEM)).IPAddressToString } Log-info "Start: $start and end: $end gives range: $($range -join ',')" return $range } catch { throw "Failed to get Mgmt range. Error: $_" } } function TestMgmtRangeSize { <# .SYNOPSIS Ensure IP range is within boundaries. #> param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end Management IP Range")] [System.Net.IPAddress] $EndingAddress, [int] $Minimum = 5, [int] $Maximum = 16 ) try { $start = $StartingAddress -split "\." | Select-Object -Last 1 $end = $EndingAddress -split "\." | Select-Object -Last 1 $hostCount = ($start..$end).count Log-info "Start: $start and end: $end gives host count: $hostcount" if ($hostCount -gt $Maximum -or $hostCount -lt $Minimum) { return $false } else { return $true } } catch { throw "Failed to check range size. Error: $_" } } function IsTcpPortInUse { param( [System.Net.IPAddress] $Ip, [int] $Port = 5986, [int] $Timeout = 500 ) try { $tcpClient = New-Object System.Net.Sockets.TcpClient $portOpened = $tcpClient.ConnectAsync($ip, $p).Wait($timeout) $tcpClient.Dispose() return ($portOpened -contains $true) } catch { throw "Failed to check TCP ports. Error: $_" } } function TestNetworkIntentStatus { <# .SYNOPSIS This test is run in the AddNode context only. This test validates if the intents configured on the existing cluster and the new node to be added are not in errored state. .DESCRIPTION This test performs the following Validations: 1) Check the ATC Intent status on existing nodes are successfully allocated 2) Check if NetworkATC service is running on the new node 3) Check if the existing nodes have storage intent configured in them. .PARAMETERS [System.Management.Automation.Runspaces.PSSession] $PsSession #> [CmdletBinding()] param ( [System.Management.Automation.Runspaces.PSSession] $PsSession ) try { Log-Info "Checking ATC Intent status on existing nodes and if NetworkATC service is running on the new node." $AdditionalData = @() # Get the names of all nodes with an Up Status $activeNodes = (Get-ClusterNode | Where-Object {$_.State -eq "Up"}).Name Log-Info "Active nodes: $($activeNodes | Out-String)" # Get all intents on the active nodes $intents = Get-NetIntentStatus | Where-Object {$activeNodes -contains $_.Host} # Checks the intent status on the existing nodes. foreach ($intent in $intents) { $intentHealthy = $true if ($intent.ConfigurationStatus -ne "Success" -or $intent.ProvisioningStatus -ne "Completed") { $intentHealthy = $false $TestNetworkIntentStatusDetail = $lnTxt.TestNetworkIntentStatusFail -f $intent.Host, $intent.ConfigurationStatus, $intent.ProvisioningStatus Log-Info $TestNetworkIntentStatusDetail -Type Warning } else { $intentHealthy = $true $TestNetworkIntentStatusDetail = $lnTxt.TestNetworkIntentStatusPass -f $intent.Host, $intent.ConfigurationStatus, $intent.ProvisioningStatus } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = $intent.Host Resource = 'AddNodeIntentStatusCheck' Detail = $TestNetworkIntentStatusDetail Status = if ($intentHealthy) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } } Log-Info "Checking if the storage intent is configured on the existing cluster before add node." $storageIntent = $intents | Where-Object {$_.IsStorageIntentSet -eq $true} try { $source = Get-Cluster } catch { $source = $Env:COMPUTERNAME Log-Info "Error getting the cluster, we could be running this test in standalone mode on $($source)" } if ($null -eq $storageIntent) { $TestNetworkIntentStatusDetail = $lnTxt.TestStorageIntentNotConfigured -f $source Log-Info $TestNetworkIntentStatusDetail -Type Warning } else { $TestNetworkIntentStatusDetail = $lnTxt.TestStorageIntentConfigured -f $source Log-Info $TestNetworkIntentStatusDetail -Type Success } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = $source Resource = 'AddNodeStorageIntentCheck' Detail = $TestNetworkIntentStatusDetail Status = if ($null -eq $storageIntent) { 'Failed' } else { 'Succeeded' } TimeStamp = [datetime]::UtcNow } # Check if NetworkATC service is running on the new node $sb = { $retVal = New-Object psobject -Property @{ Pass = $true Status = [string]::Empty } $atcFeature = Get-WindowsFeature -Name NetworkATC if ($atcFeature.Installstate -eq "Installed") { $atcService = Get-Service NetworkATC -ErrorAction SilentlyContinue $retVal.Status = "Feature Installed Service $($atcService.Status)" } elseif ($atcFeature.Installstate -eq "Available") { $retVal.Status = "Feature Available" } else { $retVal.Pass = $false } return $retVal } $NetworkATCStatus = Invoke-Command $PsSession -ScriptBlock $sb $ATCStatusHealthy = $true if (!$NetworkATCStatus.Pass) { # NetworkATC feature not Installed, not Available on the system $ATCStatusHealthy = $false $TestNetworkATCServiceDetail = $lnTxt.TestNetworkATCFeatureNotInSystem -f $psSession.ComputerName Log-Info $TestNetworkATCServiceDetail -Type Warning } elseif (-not (($NetworkATCStatus.Status -eq 'Feature Installed Service Running') -or ($NetworkATCStatus.Status -eq 'Feature Available'))) { # NetworkATC feature installed but service not 'Running', or feature not available $ATCStatusHealthy = $false $TestNetworkATCServiceDetail = $lnTxt.TestNetworkATCFeatureServiceStatus -f $NetworkATCStatus.Status, $psSession.ComputerName Log-Info $TestNetworkATCServiceDetail -Type Warning } else { $ATCStatusHealthy = $true $TestNetworkATCServiceDetail = $lnTxt.TestNetworkATCFeatureServiceStatus -f $NetworkATCStatus.Status, $psSession.ComputerName Log-Info $TestNetworkATCServiceDetail -Type Success } $AdditionalData += New-Object -TypeName PSObject -Property @{ Source = $PsSession.ComputerName Resource = 'AddNodeNewNodeNetworkATCServiceCheck' Detail = $TestNetworkATCServiceDetail Status = if ($ATCStatusHealthy) { 'Succeeded' } else { 'Failed' } TimeStamp = [datetime]::UtcNow } $instanceResult = New-Object AzStackHciNetworkTarget $instanceResult.Name = 'AzStackHci_Network_Test_Network_AddNode_Intent_and_NetworkATC_Service' $instanceResult.Title = 'Test Network intent on existing node and if NetworkATC service is running on new node' $instanceResult.Severity = 'Critical' $instanceResult.Description = 'Fails if Network intent is unhealthy on existing nodes and NetworkATC Service is not running on new node' $instanceResult.Remediation = 'https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-tool-checklist' $instanceResult.TargetResourceID = 'NetworkIntentNetworkATCService' $instanceResult.TargetResourceName = 'NetworkIntentNetworkATCService' $instanceResult.TargetResourceType = 'NetworkIntentNetworkATCServiceType' $instanceResult.Timestamp = [datetime]::UtcNow $instanceResult.HealthCheckSource = $ENV:EnvChkrId $instanceResult.Status = if ($AdditionalData.Status -contains 'Failed') { 'Failed' } else { 'Succeeded' } $instanceResult.AdditionalData = $AdditionalData return $instanceResult } catch { throw $_ } } # SIG # Begin signature block # MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCB82mjZWU7bQQL # bBrR72SgivNopRMeSfjeyVz9Bi2HUKCCDXYwggX0MIID3KADAgECAhMzAAADTrU8 # 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 # /Xmfwb1tbWrJUnMTDXpQzTGCGgowghoGAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp # Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB # BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO # MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIAjMcD14T89tZSEfsGTpBXbw # BOEWzNTC/84q31B25VT+MEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A # cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB # BQAEggEAMjatUty3e9nIYmXiur7QV+826IFtrmrASCBg9LcumNGjp/fmsk+2jyLZ # D5y8Z8U/tAfqZ8xBW+9vtFQMxJ1WgDBQztHWXj5NlgVVWhaWgyEZ2+nFRBJeObLy # J6olPiHTLFyLyxQJCezpUoWp0TvQtHvaDwv8Gg1bHTPJ2PR0Dh+cQvE9CUsRZu4W # laoRU2Vvst1Eim16+IFizlBu0YA4S3TvVL0ZiyBrqrB3XL+FqdvwIU12e+FcVxFD # B/7xP1VS4Cww4uFzkqeSZw92my76wfvEhKDV48GCDorPg7Tx9jKPD9Vx8sPPg9ph # 0fnLWr0WnS03qigknrsny5ai4hcaeKGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCC # F3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsq # hkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl # AwQCAQUABCAGYCzji5AzWzPWHvNJsU07ocdIVkFvvRv5rRFlylnHGwIGZQQDzWyA # GBMyMDIzMDkyMjA4MzExOS4yNjhaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0w # NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wg # ghHqMIIHIDCCBQigAwIBAgITMwAAAdIhJDFKWL8tEQABAAAB0jANBgkqhkiG9w0B # AQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD # VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMzA1MjUxOTEy # MjFaFw0yNDAyMDExOTEyMjFaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25z # MScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0wNUUwLUQ5NDcxJTAjBgNV # BAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQDcYIhC0QI/SPaT5+nYSBsSdhBPO2SXM40Vyyg8Fq1T # PrMNDzxChxWUD7fbKwYGSsONgtjjVed5HSh5il75jNacb6TrZwuX+Q2++f2/8CCy # u8TY0rxEInD3Tj52bWz5QRWVQejfdCA/n6ZzinhcZZ7+VelWgTfYC7rDrhX3TBX8 # 9elqXmISOVIWeXiRK8h9hH6SXgjhQGGQbf2bSM7uGkKzJ/pZ2LvlTzq+mOW9iP2j # cYEA4bpPeurpglLVUSnGGQLmjQp7Sdy1wE52WjPKdLnBF6JbmSREM/Dj9Z7okxRN # UjYSdgyvZ1LWSilhV/wegYXVQ6P9MKjRnE8CI5KMHmq7EsHhIBK0B99dFQydL1vd # uC7eWEjzz55Z/DyH6Hl2SPOf5KZ4lHf6MUwtgaf+MeZxkW0ixh/vL1mX8VsJTHa8 # AH+0l/9dnWzFMFFJFG7g95nHJ6MmYPrfmoeKORoyEQRsSus2qCrpMjg/P3Z9WJAt # FGoXYMD19NrzG4UFPpVbl3N1XvG4/uldo1+anBpDYhxQU7k1gfHn6QxdUU0TsrJ/ # JCvLffS89b4VXlIaxnVF6QZh+J7xLUNGtEmj6dwPzoCfL7zqDZJvmsvYNk1lcbyV # xMIgDFPoA2fZPXHF7dxahM2ZG7AAt3vZEiMtC6E/ciLRcIwzlJrBiHEenIPvxW15 # qwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFCC2n7cnR3ToP/kbEZ2XJFFmZ1kkMB8G # A1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUy # MFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4w # XAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy # dHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwG # A1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQD # AgeAMA0GCSqGSIb3DQEBCwUAA4ICAQCw5iq0Ey0LlAdz2PcqchRwW5d+fitNISCv # qD0E6W/AyiTk+TM3WhYTaxQ2pP6Or4qOV+Du7/L+k18gYr1phshxVMVnXNcdjecM # tTWUOVAwbJoeWHaAgknNIMzXK3+zguG5TVcLEh/CVMy1J7KPE8Q0Cz56NgWzd9ur # G+shSDKkKdhOYPXF970Mr1GCFFpe1oXjEy6aS+Heavp2wmy65mbu0AcUOPEn+hYq # ijgLXSPqvuFmOOo5UnSV66Dv5FdkqK7q5DReox9RPEZcHUa+2BUKPjp+dQ3D4c9I # H8727KjMD8OXZomD9A8Mr/fcDn5FI7lfZc8ghYc7spYKTO/0Z9YRRamhVWxxrIsB # N5LrWh+18soXJ++EeSjzSYdgGWYPg16hL/7Aydx4Kz/WBTUmbGiiVUcE/I0aQU2U # /0NzUiIFIW80SvxeDWn6I+hyVg/sdFSALP5JT7wAe8zTvsrI2hMpEVLdStFAMqan # FYqtwZU5FoAsoPZ7h1ElWmKLZkXk8ePuALztNY1yseO0TwdueIGcIwItrlBYg1Xp # Pz1+pMhGMVble6KHunaKo5K/ldOM0mQQT4Vjg6ZbzRIVRoDcArQ5//0875jOUvJt # Yyc7Hl04jcmvjEIXC3HjkUYvgHEWL0QF/4f7vLAchaEZ839/3GYOdqH5VVnZrUIB # QB6DTaUILDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZI # hvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # MjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAy # MDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRp # bWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC # AQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25Phdg # M/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPF # dvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6 # GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBp # Dco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50Zu # yjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3E # XzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0 # lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1q # GFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ # +QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PA # PBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkw # EgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxG # NSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARV # MFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj # cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAK # BggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC # AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX # zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v # cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI # KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j # b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG # 9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0x # M7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmC # VgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449 # xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wM # nosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDS # PeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2d # Y3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxn # GSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+Crvs # QWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokL # jzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL # 6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNN # MIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkRDMDAtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCJ # ptLCZsE06NtmHQzB5F1TroFSBqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA6Le8iDAiGA8yMDIzMDkyMjA3MTEw # NFoYDzIwMjMwOTIzMDcxMTA0WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDot7yI # AgEAMAcCAQACAiIVMAcCAQACAhQhMAoCBQDouQ4IAgEAMDYGCisGAQQBhFkKBAIx # KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI # hvcNAQELBQADggEBAIX3iWYeNwmEkF0AgEnhcb1lXq5+ON1FrmRtRGAk6dA72cRG # LRWERZdI+EWS+t2TAweOW5L4eWYukhTu5yDM6y1p69WgV9b0QmZI90CiqqqNVl9J # cNbh8fjH24Gxe4u5WbVKRR6pVp0IIUEQ91zkHaaBCCySsjKVx8giknb0JYfP4Ou4 # XSKkObcUbdvfzqqlnVpnhUW0zKmcgqJrSdP3wwKqpNIKG47q6iGR4nGI8wMMho0H # JGM9p4YMtLQO0KZkXNGccH/y94cOUhH+OUbjFZAZIHD975oN7mUfjLPrdIQTcvwA # DAxYrXC3cEZbEQmfJoY5J28KJcVv7hDqzzAKJ2cxggQNMIIECQIBATCBkzB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAdIhJDFKWL8tEQABAAAB0jAN # BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G # CSqGSIb3DQEJBDEiBCBTkR2uW/9jjBRyHVAfRuvCL5SrqVD/WeLW1pSzqI8rvzCB # +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIMeAIJPf30i9ZbOExU557GwWNaLH # 0Z5s65JFga2DeaROMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTACEzMAAAHSISQxSli/LREAAQAAAdIwIgQgo/VOETe+zJRyyikx/HdHN03ONGrb # FbBfi0eUT2omsFUwDQYJKoZIhvcNAQELBQAEggIAMN82x3BCVxePcyxRBOZJUD6i # VakrJk/zI0qaeaIVl7dsYLYbWHuhsUwsPrvl0rnJr2H7ZS1oTXg7RvQ7O7f3ayKP # WkZ2nEWiAz2fjX3aMzl+rsHIwm03UXOdlDzh50sKDUV0uYcPv7ca4tyGEilbX/Va # jEnXi65xTWAvLI0diELe6o1uky4JvaidoJQGlYTM7OFEmttwvx3+0ek11S10/+On # IbsWAuY8oa52rgfEdkD+aAV5A/WHOXW/XLEIEY6Lv6ckgt2tJNARhg+KhlPd/AIv # EKvpueFEEI69SCHaUHCQCdkqBo4gr0oGG+N8SGjYrtPjxw0reKOhm/iBO9BpDKDy # mZHLN2EDOQDtiu510UmOXQt4NW9jjzw5iT6xxof9ZvnMgmEa5xSn837/On7XU5V5 # ejQ0nvPHNam5Dq+RbTb4qvAayWz4vbEWTDdSLuZ9Uhhvwj1ALL7FwnGJ0R5Ytf66 # elFmhwhHL4pDBgu2W8tPH1H9VJPC5P8vbvxEJGsX0k75TeBk0h3brrnJHQHDWxvb # +ipwG6M1Cn/b16zK+GX81wqbm/RW1EngrZhpmD7Jmp86nInSPb3/VyFgottftzAv # 2QjiOGH10hm8QPIutHb+Sm22CtNRu7ZzcZpTwuIPdZCODNUL0z6l6qxVNZaXoV6D # 7mF6Pbz5B2Z9q8nytCQ= # SIG # End signature block |