NetworkValidation/NetworkChecker/NetworkValidationModules/CommonFunctions.psm1
$ErrorActionPreference = 'stop' function Get-ScriptInfo { <# .SYNOPSIS Proivdes the file name and line number for the log file .EXAMPLE $(Get-ScriptInfo) #> [CmdletBinding()] # Only providing the script file name and not the full file path $scriptFileName = (split-path -leaf $MyInvocation.ScriptName ) # Provides the line number in the file $scriptLineNumber = $MyInvocation.ScriptLineNumber $returnData = [string]($scriptFileName + ":" + $scriptLineNumber) return $returnData } function Trace-ToDestination { <# .SYNOPSIS Performs a traceroute to a destination, results will print to the console and logfile .EXAMPLE Trace-ToDestination -DestIP 1.2.3.4 -ConfigJson $ConfigJson #> param( # The IP address to perform traceroute to [Parameter(mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DestIP, # All the configuration data required for AzureStack [Parameter(mandatory = $true)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ConfigJson, # The gateway of the SVI of the BMC switch as this is not calucalated in the ConfigJson [Parameter(mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $BmcGatewayIP ) Write-AzsReadinessLog -Message "Running command: Test-Netconnection -TraceRoute $DestIP -Hops 15" try { $traceRT = Test-NetConnection -TraceRoute $DestIP -WarningAction SilentlyContinue -Hops 15 } catch { Write-AzsReadinessLog -Message $($error[0]) -Type 'Error' -ToScreen } if (![bool]$traceRT.TraceRoute) { Write-AzsReadinessLog -Message "TraceRoute failed to execute" -Type 'Error' -ToScreen } # Perform a grid search by using nested loops to map a traceroute hop to an AzureStack network device $array = [system.collections.arraylist]@() $count = 0 foreach ($hop in $traceRT.TraceRoute) { $count ++ $digitCount = "{0:D2}" -f $count $array += @("HOP_$($digitCount)=$hop") } Write-AzsReadinessLog -Message "TRACEROUTE RESULTS" $array | Where-Object { Write-AzsReadinessLog -Message $_ } return $traceRT } function Invoke-CheckForVnic { <# .SYNOPSIS Check if the specified vNic exists and remove the adapter if it does .EXAMPLE Invoke-CheckForVnic -Name "AzSPreValNic-DVM" #> [CmdletBinding()] param( # The name of the virutal NIC, this is hard coded in the logic of the script [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Name ) try { $adapters = Get-VMNetworkAdapter -ManagementOS | Where-Object { $_.Name -eq $Name } -ErrorAction SilentlyContinue if ($adapters.length -gt 0) { Remove-VMNetworkAdapter -ManagementOS -Name $Name } $returnData = @{ 'success' = $True 'message' = 'cleanup complete' } } catch { $returnData = @{ 'success' = $False 'message' = $_.Excpetion.Message } } return $returnData } function Invoke-CreateNet { <# .SYNOPSIS Create a new network adapter to be used for testing connectivity .EXAMPLE Invoke-CreateNet -Name $nicName -IPAddr $IpInfo.testIPv4Address -SubnetMaskPrefix $IpInfo.IPv4MaskBits -VirtualSwitchName $VirtualSwitchName #> [CmdletBinding()] param( # The name of the virutal NIC, this is hard coded in the logic of the script [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Name, # The IP Address used for sourcing traffic from for network tests [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $IPAddr, # Subnet Mask of the IP Address used for sourcing traffic from for network tests [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $SubnetMaskPrefix, #Virtual Switch Name that will have the new test NIC [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $VirtualSwitchName ) try { # Information required for adding adapters to the virtual switch $virtualSW = Get-VMSwitch -Name $VirtualSwitchName # Adds the new adapter to the vSwitch Add-VMNetworkAdapter -Name "$Name" -SwitchName $virtualSW.name -ManagementOS if ($name -eq 'AzSPreValNic-Public') { Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $name -Untagged Set-VMNetworkAdapterVlan -ManagementOS -VMNetworkAdapterName $name -Access -VlanId 100 } #interfaceInfo required for adding a new IP address to an interface $adapterInfo = Get-NetAdapter | Where-Object { $_.Name -match $Name } Write-AzsReadinessLog -Message $adapterInfo #Apply the IP configurations to the adapter $null = New-NetIPAddress -InterfaceIndex $adapterInfo.ifIndex -IPAddress $IPAddr -PrefixLength $subnetMaskPrefix -ErrorAction Stop Write-AzsReadinessLog -Message "Created Nic with IP $IPAddr mask $subnetMaskPrefix" $returnData = @{ 'success' = $True 'adapterInfo' = $adapterInfo } } catch { $returnData = @{ 'success' = $False 'message' = $_.Exception.Message } } return $returnData } function Invoke-TestNicReady { [CmdletBinding()] param( # IP Address of of test nic [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $IPAddr, # Name of the test nic [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $NicName ) Write-AzsReadinessLog -Message "Testing vNIC $NicName to ensure its ready to pass traffic..." <# The while loop here also serves as a test if the vNIC is ready to pass traffic. Once it moves from tenative to preffered its ready to pass traffic. Check for duplicate IP address: The scenario could be that deployment was started and a failure occured and the OEM now decides to run network validation, however the DVM's IP address will be a duplicate and sourcing traffic from the DVM IP address will fail. #> $addressState = "Tentative" while ($addressState -eq "Tentative") { $addressState = (Get-NetIPAddress -IPAddress $IPAddr).AddressState Write-AzsReadinessLog -Message "Address State($IPAddr)= $addressState" Start-Sleep 2 } if ($addressState -eq "Duplicate") { $returnData = @{ "success" = $false "message" = "Duplicate IP address detected for $IPAddr.`n`tPossible causes: DVM is online. See logs for details." } } else { $returnData = @{ 'success' = $true 'message' = "vNic is ready." } } return $returnData } function Get-PublicVIpInfo { [CmdletBinding()] param( # All the config data required to delploy AzureStack [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ConfigJson ) $vips = $configJson.IPConfigData | Where-Object { $_.Name -match "-VIPS" -and $_.Name -match $cloudId } $broadcastAddr = ($vips.IPv4BroadcastAddress.split(".")) $gateway = $broadcastAddr.clone() $gateway[3] = ([int]$gateway[3] - 2) $gateway = $gateway -join "." $networkAddr = $broadcastAddr.clone() $networkAddr[3] = ([int]$networkAddr[3] - 3) $networkAddr = $networkAddr -join "." $loopback = $broadcastAddr.clone() $loopback[3] = ([int]$loopback[3] - 4) $loopback = $loopback -join "." $loopback = $broadcastAddr.clone() $loopback[3] = ([int]$loopback[3] - 4) $loopback = $loopback -join "." $returnData = @{ "testIPv4Address" = $vips.IPv4LastAddress "IPv4BroadcastAddress" = $vips.IPv4BroadcastAddress "IPv4Mask" = "255.255.255.252" "IPv4MaskBits" = "30" "IPV4Gateway" = $gateway "IPv4NetworkAddress" = $networkAddr "networkName" = "publicVip" } return $returnData } function Get-DvmIpInfo { [CmdletBinding()] param( # Scale Unit Data [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ScaleUnits, # All the config data required to delploy AzureStack [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ConfigJson ) $dvmIp = $ScaleUnits.DeploymentData.DeploymentVM.IpAddress $bmcNet = $ConfigJson.IPConfigData | Where-Object { $_.Name -eq "Rack01-BMCMgmt" } $returnData = @{ "testIPv4Address" = $dvmIp "IPv4BroadcastAddress" = $bmcNet.IPv4BroadcastAddress "IPv4Mask" = $bmcNet.IPv4Mask "IPv4MaskBits" = $bmcNet.IPv4MaskBits "IPV4Gateway" = $bmcNet.IPv4Gateway "IPv4NetworkAddress" = $bmcNet.IPv4NetworkAddress "networkName" = "dvm" } return $returnData } function Invoke-TestNicGateway { [CmdletBinding()] param( # The IP address of the gatway to check if it pings [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $Gateway, # The name of the network we are currently testing [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [string] $NetworkName ) $orginalProgPref = $Global:ProgressPreference $Global:ProgressPreference = 'SilentlyContinue' $ping = $False $count = 0 #while (!$ping -and $count -le 30) { while ($ping -eq $False -and $count -le 4) { $ping = Test-Connection $gateway -count 1 -Quiet if (!$ping) { $message = "Ping to $($networkName) gateway $($gateway) failed, ensure the TOR's are configured and the cabling is correct!" Write-AzsReadinessLog -Message $message } else { $message = "the $($networkName) gateway is reachable." } $count ++ } $returnData = @{ 'success' = $ping 'message' = $message } $Global:ProgressPreference = $orginalProgPref return $returnData } function Invoke-DnsTesting { [CmdletBinding()] param( # Information regarding the vNic used to ping the P2P IP addresses [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [ciminstance] $AdapterInfo, # The customers DNS servers [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Array] $DnsServers, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo, #The name to be resolved by DNS [Parameter(Mandatory = $true)] [String] $HostnameToResolve ) #All dns servers need to be fucntional. Both servers will be tested here $dnsResults = [System.Collections.ArrayList]@() $logMsg = [System.Collections.ArrayList]@() foreach ($resolver in $dnsServers) { #set route to customers DNS server on AzSPreValNic NIC $splat = @{ AdapterInfo = $AdapterInfo IPAddress = $resolver IpInfo = $IpInfo Verbose = $VerbosePreference } $route = Invoke-Routing @splat if (!$route.success) { return $route } #Test if DNS works $dnsResolution = Resolve-DnsName $HostnameToResolve -QuickTimeout -Server $resolver -ErrorAction SilentlyContinue $dnsResults += @{ 'server' = $resolver 'success' = [bool]$dnsResolution 'dns' = $dnsResolution } if ([bool]$dnsResolution -eq $true) { $msg = "PASS: (dvm) DNS Server $resolver using Source IP $($IpInfo.testIPv4Address) resolved domain $HostnameToResolve" Write-AzsReadinessLog -Message $msg } else { $msg = "FAIL: (dvm) DNS Server $resolver using Source IP $($IpInfo.testIPv4Address) FAILED to resolve $HostnameToResolve." $logMsg += $msg Write-AzsReadinessLog -Message $msg -Type 'Error' } #remove the var for the next iteration of the loop Remove-Variable dnsResolution } #check for failures, collect the failed servers and generate test commands $failed = [System.Collections.ArrayList]@() $manualTestCommand = [System.Collections.ArrayList]@() foreach ($nameServer in $dnsResults) { if (!$nameServer.success) { $failed += $nameServer.server $manualTestCommand += "Resolve-DnsName $HostnameToResolve -QuickTimeout -Server $($nameServer.server)" } } # Check the $failed array for failures. If any resolver failed stop the script and return the failed dns server(s) if ($failed) { $returnData = @{ 'success' = $False 'message' = $logMsg 'dnsServers' = $failed 'manualTestCommand' = $manualTestCommand 'sourceIP' = $IpInfo.testIPv4Address 'destinationIP' = $failed 'port' = 'tcp_53/udp_53' 'networkName' = $IpInfo.networkName } } # All DNS servers are working. The results from the first DNS server will be returned back to main for further testing else { $ips = [System.Collections.ArrayList]@() foreach ($result in $dnsResults[0].dns ) { if ($result.ip4address) { $splat = @{ AdapterInfo = $AdapterInfo IPAddress = $result.IP4Address IpInfo = $IpInfo Verbose = $VerbosePreference } $route = Invoke-Routing @splat $ips += $result.IP4Address } } $returnData = @{ 'success' = $True 'ips' = $ips } } return $returnData } function Invoke-NtpTesting { [CmdletBinding()] param( # Information regarding the vNic used to ping the P2P IP addresses [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [ciminstance] $AdapterInfo, # The customers DNS servers [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [String] $TimeServer, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo ) $splat = @{ AdapterInfo = $AdapterInfo IPAddress = $TimeServer IpInfo = $IpInfo Verbose = $VerbosePreference } $route = Invoke-Routing @splat if (!$route.success) { return $route } $count = 0 $result = $false while (!$result) { $timeTest = & w32tm /stripchart /computer:$TimeServer /dataonly /samples:1 $tab = "`t" * 2 $ntpResults = "NTP Query Results`n$tab$($timeTest -join "`n$tab")" if ( ($timeTest -match "error") -Or ($timeTest.length -le 1) ) { $result = $false $count ++ if ($count -gt 4){ break } Start-Sleep 5 } else { $result = $true break } } if ($result) { $timeTest | Where-Object { if ($_ -match "The current time is") { Write-AzsReadinessLog -Message "PASS: (dvm) NTP Server $($TimeServer): $_" } } $returnData = @{ "success" = $result "message" = "PASS: (dvm) NTP Server $TimeServer using Source IP $($IpInfo.testIPv4Address) is working: $($ntpResults)" } } else { $msg = "FAIL: (dvm) NTP Server $TimeServer using Source IP $($IpInfo.testIPv4Address) is not working: $($ntpResults)" Write-AzsReadinessLog -Message $msg -Type 'Error' $manualTestCommand = "& w32tm /stripchart /computer:$($TimeServer) /dataonly /samples:1" $returnData = @{ "success" = $result "networkName" = $($IpInfo.networkName) "message" = $msg "port" = "udp_123" "sourceIP" = $IpInfo.testIPv4Address "destinationIP" = $timeServer "manualTestCommand" = $manualTestCommand } } return $returnData } function Invoke-Routing { [CmdletBinding()] param( # Information regarding the vNic used to ping the P2P IP addresses [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [ciminstance] $AdapterInfo, # The customers DNS servers [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [String] $IPAddress, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo ) # check if the route already exists, if it does do not set it $checkRoute = Get-NetRoute -DestinationPrefix "$($IPAddress)/32" -ErrorAction SilentlyContinue if (![bool]$checkRoute) { Try { #set the route, if failure occurs catch the error $null = New-NetRoute -DestinationPrefix "$($IPAddress)/32" -InterfaceIndex $AdapterInfo.ifIndex -NextHop $IpInfo.IPV4Gateway -routemetric 1 -ErrorAction Stop Write-AzsReadinessLog -Message "Route is now set for IP: $IPAddress" $returnData = @{ "success" = $true } } Catch { Write-AzsReadinessLog -Message $($_.Exception.Message) -Type 'Error' -ToScreen $returnData = @{ "success" = $false "message" = $_.Exception.Message } } } else { Write-AzsReadinessLog -Message "Route is already set for IP: $IPAddress" $returnData = @{ "success" = $true } } return $returnData } function Invoke-WebCheck { [CmdletBinding()] param( # IP's from nslookup of $Domain [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Array] $Ips, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [String] $Domain ) #make web request until successful, or for about 5 minutes. $uri = "https://$($Ips[0])" $ip = $Ips[0] <# #uncomment to spoof failed webcall if ($Domain -match "login"){ $uri = "https://1.2.3.4" } #> Try { $routeInfo = Get-NetRoute -DestinationPrefix $ip"/32" -ErrorAction stop } Catch { $returnData = @{ 'success' = $False 'message' = "routes to $Domain did not get set properly" } return $returnData } $manualTestCommand = ("Invoke-webrequest -Uri $uri -TimeoutSec 5 -MaximumRedirection 0 -Headers @{'HOST'= '$Domain'} -UseBasicParsing ") $count = 0 $webCheck = $False while ($webCheck -eq $False) { #login.microsoftonline.com is fronted by a CDN and the IPs change frequently. I take the first IP fron a nslookup and set a HOST header in order to make the call to a single IP. Try { $check = Invoke-webrequest -Uri $uri -TimeoutSec 5 -MaximumRedirection 0 -Headers @{"HOST" = "$Domain" } -ErrorAction SilentlyContinue -UseBasicParsing -Verbose:$VerbosePreference $statusCode = $check.StatusCode } Catch { $statusCode = $_.Exception.Response.StatusCode.value__ } $count += 1 if (($statusCode -lt 500) -And ($statusCode -ge 300)) { $msg = "PASS: (dvm) Successfull webrequest to: https://$Domain($($ips[0]))" Write-AzsReadinessLog -Message $msg $webCheck = $True $returnData = @{ "success" = $True; "message" = $msg; "networkName" = $IpInfo.networkName } return $returnData } else { $message = "Attempt $($count) of 3 Check connectivity to the internet (https://$Domain $($ips[0])) from the $($IpInfo.networkName) network sourceIP = $($IpInfo.testIPv4Address) destinationIP = $ip port = https/tcp_443 manualTestCommand = $manualTestCommand " } if ($count -eq 3) { $msg = "FAIL: (dvm) Unsuccessfull webrequest using Source IP $($IpInfo.testIPv4Address) to: https://$Domain($($ips[0]))" Write-AzsReadinessLog -Message $msg -Type 'Error' $returnData = @{ "success" = $False "message" = $msg 'manualTestCommand' = $manualTestCommand "sourceIP" = $IpInfo.testIPv4Address "destinationIP" = $ips[0] "port" = "https/tcp_443" "networkName" = $IpInfo.networkName "TraceRoute" = $traceRT } return $returnData } } } function Get-Endpoints { [CmdletBinding()] param( #ARM Endpoing #[Parameter(Mandatory = $True)] #[ValidateNotNullOrEmpty()] #[string] $CloudARMEndpoint, # IP's from nslookup of $Domain [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Array] $Ips, # IP information for the network being tested # [Parameter(Mandatory = $True)] # [ValidateNotNullOrEmpty()] # [System.Collections.Hashtable] # $IpInfo, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [String] $Domain ) try { Write-AzsReadinessLog -Message "Start: Get-Endpoints from ARM for $Domain" $fullUri = "https://" + $Ips[0].TrimEnd('/') + "/metadata/endpoints?api-version=2015-01-01" Write-AzsReadinessLog -Message $fullUri $response = Invoke-RestMethod -Uri $fullUri -UseBasicParsing -TimeoutSec 30 -Headers @{ "HOST" = "$Domain" } -Verbose:$VerbosePreference $checkResponse = @( [bool]$response.graphEndpoint [bool]$response.authentication.loginEndpoint [bool]$response.authentication.audiences[0] ) if ($false -in $checkResponse) { $returnData = @{ success = $false message = "The response from $Domain did not contain the needed URL's for AAD." } $hash = @{ Test = "dvm_webRequests" Result = 'Fail' FailureDetail = $returnData.message outputObject = $null } Write-AzsResult -in $hash $global:ValidationResults += $hash } else { $returnData = @{ success = $true message = "The response from $Domain contains the needed URL's for AAD." EndpointProperties = @{ GraphUri = $response.graphEndpoint LoginUri = $response.authentication.loginEndpoint ManagementServiceUri = $response.authentication.audiences[0] } } } } catch { $exceptionMessage = $($PSItem.Exception.Message) Write-AzsReadinessLog -Message "Could not retrive the AAD Endpoints for $Domain" Write-AzsReadinessLog -Message "Exception Message: $exceptionMessage" $returnData = @{ success = $false message = "Could not retrive the AAD Endpoints from $Domain. Exception: $exceptionMessage " } $hash = @{ Test = "dvm_webRequests" Result = 'Fail' FailureDetail = $returnData.message outputObject = $null } Write-AzsResult -in $hash $global:ValidationResults += $hash return $returnData } return $returnData } function Get-AzureURIs { <# .SYNOPSIS Resolve Azure URIs for a given Azure Service .DESCRIPTION Resolve Azure URIs for a given Azure Service .EXAMPLE Get-AzureURIs -AzureEnvironment AzureCloud .INPUTS AzureEnvironment - string - should be AzureCloud, AzureChinaCloud, AzureGermanCloud .OUTPUTS Hashtable of URIs .NOTES General notes #> [OutputType([Hashtable])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet("AzureCloud", "AzureChinaCloud", "AzureGermanCloud", "AzureUSGovernment", "CustomCloud")] [string] $AzureEnvironment, [Parameter(Mandatory = $false)] [string] $CustomCloudARMEndpoint, # IP's from nslookup of $Domain [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Array] $Ips ) $data = switch ($AzureEnvironment) { 'AzureCloud' { @{ GraphUri = "https://graph.windows.net/" LoginUri = "https://login.microsoftonline.com/" ManagementServiceUri = "https://management.core.windows.net/" ARMUri = "https://management.azure.com/" } } 'AzureChinaCloud' { @{ GraphUri = "https://graph.chinacloudapi.cn/" LoginUri = "https://login.chinacloudapi.cn/" ManagementServiceUri = "https://management.core.chinacloudapi.cn/" ARMUri = "https://management.chinacloudapi.cn/" } } 'AzureUSGovernment' { @{ GraphUri = "https://graph.windows.net/" LoginUri = "https://login.microsoftonline.us/" ManagementServiceUri = "https://management.core.usgovcloudapi.net/" ARMUri = "https://management.usgovcloudapi.net/" } } 'AzureGermanCloud' { @{ GraphUri = "https://graph.cloudapi.de/" LoginUri = "https://login.microsoftonline.de/" ManagementServiceUri = "https://management.core.cloudapi.de/" ARMUri = "https://management.microsoftazure.de/" } } 'CustomCloud' { $endPoints = Get-Endpoints -Ips $Ips -Domain (([System.Uri]$CustomCloudARMEndpoint).Host) $CustomCloudARMEndpointProperties = $endPoints.EndpointProperties @{ GraphUri = $CustomCloudARMEndpointProperties.GraphUri LoginUri = $CustomCloudARMEndpointProperties.LoginUri ManagementServiceUri = $CustomCloudARMEndpointProperties.ManagementServiceUri ARMUri = $CustomCloudARMEndpoint } } default { throw New-Object NotImplementedException("Unknown environment type '$AzureEnvironment'") } } return $data } function Invoke-DnsCheck { [CmdletBinding()] param( # IP's from nslookup of login.microsoftonline.com [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Array] $Routes, # All the configuration data required for AzureStack [Parameter(mandatory = $true)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ConfigJson, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo ) if ($Routes.success -eq $False) { Write-AzsReadinessLog -Message $($Routes.message) -Type 'Error' $traceRT = [System.Collections.ArrayList]@() $Routes.destinationIP | where-object { $traceRT += @{ "destinationIP" = $_ "TraceRoute" = (Trace-ToDestination -configJson $ConfigJson -DestIP $_ -BmcGatewayIP $ipInfo.IPV4Gateway).TraceRoute } } $manualTest = "Commands to manually test DNS resolution:`n`t" $manualTest += $Routes.manualTestCommand -join "`n`t" $returnData = @{ success = $false 'message' = $Routes.message } } else { $returnData = @{ "success" = $true } } return $returnData } function Invoke-WebResults { [CmdletBinding()] param( # IP's from nslookup of login.microsoftonline.com [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Hashtable] $WebCheck, # IP's from nslookup of login.microsoftonline.com [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Array] $Routes, # All the configuration data required for AzureStack [Parameter(mandatory = $true)] [ValidateNotNullOrEmpty()] [PSCustomObject] $ConfigJson, # IP information for the network being tested [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [System.Collections.Hashtable] $IpInfo ) if (!$webCheck.success) { Write-AzsReadinessLog -Message "Performing traceroute to $($routes.ips[0]) using SRC IP $($ipInfo.testIPv4Address)" -Type 'Error' $traceRT = (Trace-ToDestination -configJson $ConfigJson -DestIP $($routes.ips[0]) -BmcGatewayIP $ipInfo.IPV4Gateway).TraceRoute Write-AzsReadinessLog -Message $($webCheck.message) -Type 'Error' } } # SIG # Begin signature block # MIIjhQYJKoZIhvcNAQcCoIIjdjCCI3ICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBFvuWbKIEFV6PI # g+7Y6Gyji/NvAs4KRMTjSEm2zKTGfKCCDYEwggX/MIID56ADAgECAhMzAAACUosz # qviV8znbAAAAAAJSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjEwOTAyMTgzMjU5WhcNMjIwOTAxMTgzMjU5WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDQ5M+Ps/X7BNuv5B/0I6uoDwj0NJOo1KrVQqO7ggRXccklyTrWL4xMShjIou2I # sbYnF67wXzVAq5Om4oe+LfzSDOzjcb6ms00gBo0OQaqwQ1BijyJ7NvDf80I1fW9O # L76Kt0Wpc2zrGhzcHdb7upPrvxvSNNUvxK3sgw7YTt31410vpEp8yfBEl/hd8ZzA # v47DCgJ5j1zm295s1RVZHNp6MoiQFVOECm4AwK2l28i+YER1JO4IplTH44uvzX9o # RnJHaMvWzZEpozPy4jNO2DDqbcNs4zh7AWMhE1PWFVA+CHI/En5nASvCvLmuR/t8 # q4bc8XR8QIZJQSp+2U6m2ldNAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUNZJaEUGL2Guwt7ZOAu4efEYXedEw # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDY3NTk3MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAFkk3 # uSxkTEBh1NtAl7BivIEsAWdgX1qZ+EdZMYbQKasY6IhSLXRMxF1B3OKdR9K/kccp # kvNcGl8D7YyYS4mhCUMBR+VLrg3f8PUj38A9V5aiY2/Jok7WZFOAmjPRNNGnyeg7 # l0lTiThFqE+2aOs6+heegqAdelGgNJKRHLWRuhGKuLIw5lkgx9Ky+QvZrn/Ddi8u # TIgWKp+MGG8xY6PBvvjgt9jQShlnPrZ3UY8Bvwy6rynhXBaV0V0TTL0gEx7eh/K1 # o8Miaru6s/7FyqOLeUS4vTHh9TgBL5DtxCYurXbSBVtL1Fj44+Od/6cmC9mmvrti # yG709Y3Rd3YdJj2f3GJq7Y7KdWq0QYhatKhBeg4fxjhg0yut2g6aM1mxjNPrE48z # 6HWCNGu9gMK5ZudldRw4a45Z06Aoktof0CqOyTErvq0YjoE4Xpa0+87T/PVUXNqf # 7Y+qSU7+9LtLQuMYR4w3cSPjuNusvLf9gBnch5RqM7kaDtYWDgLyB42EfsxeMqwK # WwA+TVi0HrWRqfSx2olbE56hJcEkMjOSKz3sRuupFCX3UroyYf52L+2iVTrda8XW # esPG62Mnn3T8AuLfzeJFuAbfOSERx7IFZO92UPoXE1uEjL5skl1yTZB3MubgOA4F # 8KoRNhviFAEST+nG8c8uIsbZeb08SeYQMqjVEmkwggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVWjCCFVYCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN # BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgBECAdZeB # f2Gc4uwCtZYIWNmJx/k7iJwr5RlgRmRdngIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS # AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN # BgkqhkiG9w0BAQEFAASCAQBQA9yO8hXLwo9CFyVR3wBayc1VJTGEJo633FESWci6 # MDVTnaHKkY4cJ4PNblItpi7GUTS228P959WwVCP7Ayp1CCkg9xW2/9Hr1TYxHC5G # 3fK1i/RIFZS5oWtOtNHfVEn4rf0hAyk/qojjsC8/vITQ1HkVHZNrMVxIqBOQmldA # hcPdYYeCMVjsChJ2h0aBCbNtAK8LckFOyiEgdGovKhS23L60kUctxryBW+vITnIH # 7AWsaM9pO4pVqXEj46eUuF1oBKuyO7F8h7pEpdCFrFkBG9FCjVe5mpCmpEJNf9Me # bbsy4mARlRJfb7dfylT8Hgmr9lbNwW7P2lmPoRq7QaEioYIS5DCCEuAGCisGAQQB # gjcDAwExghLQMIISzAYJKoZIhvcNAQcCoIISvTCCErkCAQMxDzANBglghkgBZQME # AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIDr2uY+m2ZVHcb2Com+Tr0lh1MLqLy/UhVaIs+06 # SUUfAgZhkuEfJhgYEzIwMjExMTE2MDg0OTQzLjkzOVowBIACAfSggdCkgc0wgcox # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p # Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg # RVNOOjEyQkMtRTNBRS03NEVCMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt # cCBTZXJ2aWNloIIOOzCCBPEwggPZoAMCAQICEzMAAAFT0oJyRWxX44sAAAAAAVMw # DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN # MjAxMTEyMTgyNjA1WhcNMjIwMjExMTgyNjA1WjCByjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTJCQy1FM0FFLTc0 # RUIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC167Z0B7Mb+bNCAuXC6EGJsFvgVB6i # 30/jl4gmtUwoQG5z3WpxQHNJj+t2I4fpETQQLoxioHSiR+nbJFRnT3WCjc6UwbGB # MnQJgzgzVXofVPHICimiYNEiHy86PvCEpWT8vb6jfFcaOVoMe+4wN2NqblOWA4o6 # wmEQUFmrbpKZB+cRVJF7WE1L6NDfRiPE9XRfRk+zzZd+MJhs3eWRr7jVdG10vVEl # V3hq0YFzfho7guP3L0bMxikRy3pPe4W6g7Qwf5McxDpM0Aatz9CNkscAPyh4jZOF # 0f7diL1BGET/c4vp8iQTlfKqEhCbt3Fi/Hq54cwXjE1aUxzQva5b/ebtAgMBAAGj # ggEbMIIBFzAdBgNVHQ4EFgQUxqKLc0gqqbMwGUC34HBtrDFWRHUwHwYDVR0jBBgw # FoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov # L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENB # XzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAx # MC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDAN # BgkqhkiG9w0BAQsFAAOCAQEAlGi9eXz6IEQ+xwFzSaJR3JxHsaiegym9DsRXcpfd # 7frlTUZ/5iVkyFq8YXyKoEI+7bVOzJlTf3PF8UdYCy4ysWae6szI+bsz29boYoRl # wSaxdQOg9hwlX1MkfdMnhJ+Iiw35EVYp7rMVjdN9i6aZHldKP/PDQNu+uzEHobKN # LpZsxe+LI/gdxuIiFJDO3O9eTun07xWHfhnIxJ2wS7y5PEHpdxZTQX2nvWR0bjdm # t5r6hy5X0sND1XZiaU2apl1Wb9Ha2bnO4A8lre7wZjS+i8XBVVJaE4LXmcTFeQll # Zldvq8yBmvDo4wvjFOFVckqyBSpCMMJpRqzkodGDxJ06NDCCBnEwggRZoAMCAQIC # CmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRp # ZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIx # NDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3 # DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF # ++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRD # DNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSx # z5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1 # rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16Hgc # sOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB # 4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqF # bVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud # EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYD # VR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv # cHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEB # BE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j # ZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCB # kjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQe # MiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQA # LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUx # vs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GAS # inbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1 # L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWO # M7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4 # pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45 # V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x # 4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEe # gPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKn # QqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp # 3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvT # X4/edIhJEqGCAs0wggI2AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP # cGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoxMkJDLUUzQUUtNzRF # QjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcG # BSsOAwIaAxUAikpN7ccO1k9PKCGCvaQWKyJ50ZuggYMwgYCkfjB8MQswCQYDVQQG # EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG # A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg # VGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOU9X54wIhgPMjAy # MTExMTYwNjM3MThaGA8yMDIxMTExNzA2MzcxOFowdjA8BgorBgEEAYRZCgQBMS4w # LDAKAgUA5T1fngIBADAJAgEAAgEFAgH/MAcCAQACAhFEMAoCBQDlPrEeAgEAMDYG # CisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEA # AgMBhqAwDQYJKoZIhvcNAQEFBQADgYEATmvyESXbHLXzKcvhWUgOObExLX3IafeK # KeAQawX6fIoDRNkzENMAoyfwP6USSMHE/orwf7VIrCCEloW587yfk4pWAIRGN9LO # b+m5HOF2xcnpo0jTE6kZWPc57ViSyiJK5EVBAuN0bQKoNSGOvFfX1TIgwTQqJQun # ItWzfp69aaExggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg # MjAxMAITMwAAAVPSgnJFbFfjiwAAAAABUzANBglghkgBZQMEAgEFAKCCAUowGgYJ # KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCALF7tCiz2r # lgX7Xz1a5at2wSaPpnkusOW4ZqjDds1xXTCB+gYLKoZIhvcNAQkQAi8xgeowgecw # geQwgb0EIFDBCo85tCAICfyXoZBDppodLIMcb2wOH2rEBWiNtY8AMIGYMIGApH4w # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFT0oJyRWxX44sAAAAA # AVMwIgQgl3uKqMQCykGgGYoUA5hTgL739FJU/MI10jnQkcUn6KswDQYJKoZIhvcN # AQELBQAEggEABa155G0SAeUOs1DB3OKccs4I8cDwrQQqekWQ+omla8M6CGrViSy8 # LTMkYadAHZ6tGDN5RldiK4OtZi7KEV5qYdtJsMVToVvPIlT8X2Rb8GDR9Sn4UQ7X # 9ovotLZF/RCLZ5Z2GJoD73xX2UuCTr1Tweo6jAB5fKkLFO+ERLT6lzgsPMVBOB2Z # kaeW73QptwFJrU3kNnPi26kmC+KXO0hqGs63tkES4x1vJEHSPSpM+nSlnnvv79FX # KxFW2z71SmFixzPPGU55eNQrcSBpKlkG3mivP9wtJmnRw/V6bJzj5lCPSOMBABvy # B6RcpwMCso82mVYBkzas5Kj05mq1vPE99A== # SIG # End signature block |