AzStackHciPorts/AzStackHci.Ports.Helpers.psm1
|
Import-LocalizedData -BindingVariable lpTxt -FileName AzStackHci.Ports.Strings.psd1 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 AzStackHciPort { [int[]]$PortNumber [string[]]$Protocol [string]$ProcessName } class AzStackHciPortTarget : HealthModel { # Attribute for performing check [AzStackHciPort]$Port # Additional Attributes for end user interaction [string[]]$Service # short cut property to Service from tags [string[]]$OperationType # short cut property to Operation Type from tags [string[]]$Group # short cut property to group from tags [bool]$System # targets for system checks such as proxy traversal } function Get-AzStackHciPortTarget { <# .SYNOPSIS Retrieve Ports from built target packs .DESCRIPTION Retrieve Ports from built target packs .EXAMPLE PS> Get-AzStackHciPortTarget Get all port targets .EXAMPLE Get-AzStackHciPortTarget -Service ARC | ft Name, Title, Service, OperationType -AutoSize Get all ARC port targets .EXAMPLE PS> Get-AzStackHciPortTarget -Service ARC -OperationType Workload | ft Name, Title, Service, OperationType -AutoSize Get all ARC targets for workloads .EXAMPLE PS> Get-AzStackHciPortTarget -OperationType Workload | ft Name, Title, Service, OperationType -AutoSize Get all targets for workloads .EXAMPLE PS> Get-AzStackHciPortTarget -OperationType ARC -OperationType Update -Additive | ft Name, Title, Service, OperationType -AutoSize Get all ARC targets and all targets for Update .INPUTS Service - String array OperationType - String array Additive - Switch .OUTPUTS PSObject .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]] $Service, [Parameter(Mandatory = $false)] [string[]] $OperationType, [Parameter(Mandatory = $false)] [string[]] $FilePath, [Parameter(Mandatory = $false)] [switch] $Additive, [Parameter(Mandatory = $false)] [switch] $IncludeSystem ) try { Import-AzStackHciPortTarget -FilePath $FilePath $executionTargets = @() # Additive allows the user to "-OR" their parameter values if ($Additive) { Log-Info -Message $lpTxt.Additively if (-not [string]::IsNullOrEmpty($Service)) { Log-Info -Message ($lpTxt.ByService -f ($Service -join ',')) foreach ($svc in $Service) { $executionTargets += $Script:AzStackHciPortTargets | Where-Object { $svc -in $_.Service } } } if (-not [string]::IsNullOrEmpty($OperationType)) { Log-Info -Message ($lpTxt.ByOp -f ($OperationType -join ',')) foreach ($Op in $OperationType) { $executionTargets += $Script:AzStackHciPortTargets | Where-Object { $Op -in $_.OperationType } } } if ([string]::IsNullOrEmpty($OperationType) -and [string]::IsNullOrEmpty($Service)) { $executionTargets += $Script:AzStackHciPortTargets } } else { if ([string]::IsNullOrEmpty($OperationType) -and [string]::IsNullOrEmpty($Service)) { $executionTargets += $Script:AzStackHciPortTargets } elseif (-not [string]::IsNullOrEmpty($Service) -and [string]::IsNullOrEmpty($OperationType)) { Log-Info -Message ($lpTxt.ByService -f ($Service -join ',')) foreach ($svc in $Service) { $executionTargets += $Script:AzStackHciPortTargets | Where-Object { $svc -in $_.Service } } } elseif (-not [string]::IsNullOrEmpty($OperationType) -and [string]::IsNullOrEmpty($Service)) { Log-Info -Message ($lpTxt.ByOp -f ($OperationType -join ',')) foreach ($Op in $OperationType) { $executionTargets += $Script:AzStackHciPortTargets | Where-Object { $Op -in $_.OperationType } } } else { Log-Info -Message ($lpTxt.ByOpAndService -f ($OperationType -join ','), ($Service -join ',')) $executionTargetsByOp = @() foreach ($Op in $OperationType) { $executionTargetsByOp += $Script:AzStackHciPortTargets | Where-Object { $Op -in $_.OperationType } } foreach ($svc in $Service) { $executionTargets += $executionTargetsByOp | Where-Object { $svc -in $_.Service } } } } if ($IncludeSystem) { return $executionTargets } else { return ($executionTargets | Where-Object Service -NotContains 'System') } } catch { throw "Get failed: $($_.exception)" } } function Import-AzStackHciPortTarget { <# .SYNOPSIS Retrieve Ports from built target packs .DESCRIPTION Retrieve Ports from built target packs .EXAMPLE PS C:\> Import-AzStackHciPortTarget Explanation of what the example does .INPUTS URI .OUTPUTS PSObject .NOTES #> [CmdletBinding()] param ( [string[]] $FilePath ) try { $Script:AzStackHciPortTargets = @() if ([string]::IsNullOrEmpty($FilePath)) { $FilePath = "$PSScriptRoot\Targets\*.json" } $targetFiles = Get-ChildItem -Path $FilePath | Select-Object -ExpandProperty FullName if (-not $targetFiles) { throw $lpTxt.NoTargets } Log-Info ("Importing {0}" -f ($targetFiles -join ',')) ForEach ($targetFile in $targetFiles) { try { # TO DO - Add validations: # - protocol should not contain :// $targetPackContent = Get-Content -Path $targetFile | ConvertFrom-Json -WarningAction SilentlyContinue foreach ($target in $targetPackContent) { #Set Name of the individual test/rule/alert that was executed. Unique, not exposed to the customer. $target | Add-Member -MemberType NoteProperty -Name Name -Value ("AzStackHci_Port_{0}" -f $Target.TargetResourceName) -Force $target | Add-Member -MemberType NoteProperty -Name HealthCheckSource -Value $ENV:EnvChkrId -Force $target | Add-Member -MemberType NoteProperty -Name Service -Value $Target.Tags.Service -Force $target | Add-Member -MemberType NoteProperty -Name OperationType -Value $Target.Tags.OperationType -Force $target | Add-Member -MemberType NoteProperty -Name Group -Value $Target.Tags.Group -Force # TO DO: Determine the proper use of TargetResourceID $target.TargetResourceID = (New-Guid).Guid.ToString() $Script:AzStackHciPortTargets += [AzStackHciPortTarget]$target } } catch { Log-Info -Type Warning -Message ($lpTxt.CannotReadTargetFile -f (Split-Path -Path $targetFile -Leaf), $_.Exception.Message) } } } catch { throw "Import failed: $($_.exception)" } } function Get-AzStackHciPortServiceName { <# .SYNOPSIS Retrieve Services from built target packs .DESCRIPTION Retrieve Services from built target packs .EXAMPLE PS C:\> Get-AzStackHciPortServiceName Explanation of what the example does .INPUTS Service .OUTPUTS PSObject .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]] $Service, [Parameter(Mandatory = $false)] [switch] $IncludeSystem ) try { Get-AzStackHciPortTarget -IncludeSystem:$IncludeSystem | Select-Object -ExpandProperty Service | Sort-Object | Get-Unique } catch { throw "Failed to get services names. Error: $($_.Exception.Message)" } } function Get-AzStackHciPortOperationName { <# .SYNOPSIS Retrieve Operation Types from built target packs .DESCRIPTION Retrieve Operation Types from built target packs e.g. Deployment, Update, Secret Rotation. .EXAMPLE PS C:\> Get-AzStackHciPortOperationName Explanation of what the example does .INPUTS Service .OUTPUTS PSObject .NOTES #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string] $OperationType ) try { Get-AzStackHciPortTarget | Select-Object -ExpandProperty OperationType | Sort-Object | Get-Unique } catch { throw "Failed to get services names. Error: $($_.Exception.Message)" } } function Invoke-PortConnection { <# .SYNOPSIS Get port via Get-NetTCPConnection & Get-NetUDPEndpoint .DESCRIPTION Get port via Get-NetTCPConnection & Get-NetUDPEndpoint .EXAMPLE PS C:\> Invoke-NetTCPConnectionEx -Target $Target Explanation of what the example does .INPUTS URI .OUTPUTS Output (if any) .NOTES #> [CmdletBinding()] param ( [Parameter()] [psobject] $Target, [Parameter()] [System.Management.Automation.Runspaces.PSSession[]] $PsSession ) try { $ProgressPreference = 'SilentlyContinue' $target.TimeStamp = [datetime]::UtcNow $Target.HealthCheckSource = $ENV:EnvChkrId # Create ScriptBlock $scriptBlock = { $Target = $args[0] $AdditionalData = @() if ( -not (Get-Command Get-NetTCPConnection, Get-NetUDPEndpoint -ea SilentlyContinue)) { throw "Get-NetTCPConnection and Get-NetUDPEndpoint commands not available. Ensure NetTCPIP module is installed." } foreach ($rule in $target.Port) { foreach ($port in $rule.PortNumber) { # Placeholder AdditionalData $AddData = New-Object -TypeName PSObject -Property @{ Source = $ENV:COMPUTERNAME Resource = $port Status = "In Progress" TimeStamp = [datetime]::UtcNow Detail = $null } # Test the port if ('UDP' -in $rule.protocol) { $udpResult = Get-NetUDPEndpoint -LocalPort $Port -ErrorAction SilentlyContinue } if ('TCP' -in $rule.protocol) { $tcpResult = Get-NetTCPConnection -LocalPort $Port -ErrorAction SilentlyContinue } # Make uniform output if ($tcpResult) { $AddData.Detail += $tcpResult | Select-Object LocalAddress, RemoteAddress, RemotePort, State, OwningProcess, ` @{ 'Label' = 'LocalPort'; Expression = { "TCP:$($_.LocalPort)" } }, ` @{ 'Label' = 'Protocol'; Expression = { 'tcp' } }, ` @{ 'Label' = 'ProcessName'; Expression = { (Get-Process -Id $_.OwningProcess).ProcessName } }, @{ 'Label' = 'Status'; Expression = { if ((Get-Process -Id $_.OwningProcess).ProcessName -eq $rule.ProcessName) { 'Succeeded' } else { 'Failed' } } } $tcpResult | Where-Object Status -EQ 'Failed' | ForEach-Object { Write-Verbose -Verbose "$($_.LocalPort) owned by $($_.ProcessName). Expected $($rule.ProcessName)" } } if ($udpResult) { $AddData.Detail += $udpResult | Select-Object LocalAddress, OwningProcess, ` @{ 'Label' = 'LocalPort'; Expression = { "UDP:$($_.LocalPort)" } }, ` @{ 'Label' = 'Protocol'; Expression = { 'udp' } }, ` @{ 'Label' = 'ProcessName'; Expression = { (Get-Process -Id $_.OwningProcess).ProcessName } }, @{ 'Label' = 'Status'; Expression = { if ((Get-Process -Id $_.OwningProcess).ProcessName -eq $rule.ProcessName) { 'Succeeded' } else { 'Failed' } } } $udpResult | Where-Object Status -EQ 'Failed' | ForEach-Object { Write-Verbose -Verbose "$($_.LocalPort) owned by $($_.ProcessName). Expected $($rule.ProcessName)" } } # Determine success/failure $addData.Status = if ($AddData.Detail.Status -Contains 'Failed') { "Failed" } else { "Succeeded" } $AdditionalData += $AddData } } if ($AdditionalData.Status -contains 'Failed') { $target.Status = 'Failed' Log-Info "$($target.Title) failed:" } else { $target.Status = 'Succeeded' Log-Info "$($target.Title) succeeded:" } $AdditionalData | ForEach-Object { Log-Info ("Port detail {0}: {1}, {2}" -f $_.Status, $_.Resource, (($_.Detail | ConvertTo-Json -Depth 5) -replace "`r`n", " " -replace " ", "")) } $target.AdditionalData = $AdditionalData $Target.HealthCheckSource = $ENV:EnvChkrId return $target } # Run Invoke-Command $icmParam = @{ ScriptBlock = $scriptBlock ArgumentList = $Target } if ($PsSession) { $icmParam += @{ Session = $PsSession } } Invoke-Command @icmParam } catch { throw $_ } finally { $ProgressPreference = 'Continue' } } # SIG # Begin signature block # MIInJwYJKoZIhvcNAQcCoIInGDCCJxQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBFTdHg68nJ4YWA # RpbQ/rGRG0bGqTtpJ9oc+wcQHRqXsKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnDMIIZvwIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJ # KoZIhvcNAQkEMSIEIDuyu0oekbVXvc1Ohg2rrBOBgBiVBlYXOhl5WPegc/Q4MEIG # CisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAdSVUqYx086tHY52k # DYH5BAj4HaNuOT/GSgymAnQKWThmXSffCdzXQK+iCGu5bIr4JPD6yislwb3j8fPA # xabcbtZtgl2mm8j4aTcq4sGTYF0PvbGMvy5PVR6QMM0qhBNxz6xXmdITUzPcPvq4 # 8AfNYMg2T79g7ZPolc3k50NF03RlQkFFQ/8rkO6D4bUYwB6Ed6RgbqEhUnJwp7Xj # YJnsf0ga6quKvYw5VfUCqlTA5P1JM+ALnhvKHMiNDQa/NwBecTyl4s5DGwsIF7sk # HI0IEW2mis/kqJbfAcMlGJdjhzli4WoO9qsXR7FRNJbPoIhhqV+Rz5Lim5C3OyXy # 1Ea8zqGCF5MwghePBgorBgEEAYI3AwMBMYIXfzCCF3sGCSqGSIb3DQEHAqCCF2ww # ghdoAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8 # MIIBOAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCCYWoSSVNuteI4z # No+pFZCtN1G+WKbLC2CpD7g33ShoSQIGaoSBV9RfGBIyMDI2MDgyNzE2MzgzOC43 # NlowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAl # BgNVBAsTHm5TaGllbGQgVFNTIEVTTjo5MjAwLTA1RTAtRDk0NzElMCMGA1UEAxMc # TWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEeowggcgMIIFCKADAgECAhMz # AAACI0/ZYCRTz/4rAAEAAAIjMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l # LVN0YW1wIFBDQSAyMDEwMB4XDTI2MDIxOTE5Mzk1N1oXDTI3MDUxNzE5Mzk1N1ow # gcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsT # HE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQg # VFNTIEVTTjo5MjAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt # U3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIrp # DaeTlZR0rNIJJp+n5SNQBGxbEcpLresmEUL/NJpsW6ZMG5onRA2uap6+5vkNvt9K # Pmq3DAqeMg73b4dcXrvX3Z+6MvsMWi3lYSP8C0Rn9evMUeKYqU3WHqARDA/kjrvC # LNo9blnNIE2losGDmge8BI85m3B01Shn4NAoXeEmXUpm6giVUr6qLtwuOBqTqzmg # 5lxEIysqe4LdqhVrrBENti8pS6PuuQXH0o7Q+wcn+T4udkyCBGF6HgBV1rDKH6g7 # Mo+OVAZQ19J5ZSDKbZT0Itry23SZBfgPEPPr6tqbnSCPWgB/JDpNDuv3o8AMU4oG # BpTv5ykedpkbz11N6BDrJ0FEYjJw7DV1FfZ4oNFHPOIrdyfRZoib/s54azJAqMjM # RC5RMO/QmP/3NDu2u4s46kkP3wElU4ruN7zhLPaFvce9RJPuPWPY3yl4PqiWSkUd # H/VnwnPgX6aStQXsyY8CKtgdHO6dsiDcesMw3AVg3vIGQMDj9Uyj0JjTL2gZSirb # KNsLBOJvP1ViX3ecHdBCJMJP2dbcz5M5YH48ytmkTGrUFIeYo/Mip6EqqtQOgzfc # 8r50QrClgsRPq5erge5BExdZP/+w+5tSdABppQx9CEBlLLbce3HC03d4r35PjAJq # /bBAW3nt5Q7BRbn8MLMwX225rkd7WE2+BwBdqIbXAgMBAAGjggFJMIIBRTAdBgNV # HQ4EFgQU1sCHz2/b2c9j1vBBvVBgLPFWB5cwHwYDVR0jBBgwFoAUn6cVXQBeYl2D # 9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3Nv # ZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy # MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1l # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUB # Af8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQAD # ggIBAIdDB7vPm2ng1nAB/VwH7hz0niy/Dc/paoYEzG2rOdLoN3NTNK1ccJo9mEzj # WDWIoc2eZycuPAu6M4Ro2OFKdQOIBmpCNbllqk4HGBzsSCCGH2T6vvypYB7esnhC # iEFuFIZ1m0qK9NFp5GqaeHLz5OGsqHMJ4TBpqtcmKZnBKl1BBQNuF5Yd7IDEBKq6 # W13ko7Sb9QW87Te196moZcDi0KD9YYQLAqo6MnOlEB88gHrLUfJWuT6+YvmukRtP # DAs61ftbEUYbz5xguT0eNoOTGtoD8diUpBHHWx3Nr7D+C6UvCA6cHJEkoXauvwzs # U0iXCiLrLAWlo1zwDsd7BoaODD+19wTbrQjVd6QaW4A0j0ec405haUjsEoFBtYTa # 16jq+xDVWDwHytNlJ49V2ZcvU8+qqzcpV0UozmRihw8IMz7pUvfYhX3qwRJ/ZPsO # PFqekKDYPZRiPhnWLtzLxTUssMaDnkpazhp/ZFEGMfYy6UeACZbmhsrGJkINCNFq # ugnZcSVdSGKAT0HO+EIVtP8cNja+lWmXkedKlwJLGYvmLmUhP/FsBAwjsu6Hvleu # b4iyV8VY4Y4YyUKn7bioQkSCVcQ/vHCyiU10E2d1eKGHIh59UaUjUNHvEYQuImuT # yJ9VZij1cRsRe/+Vu+noXZHZSyfB5ZyS+rTLUdacscOofp0+MIIHcTCCBVmgAwIB # AgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0 # IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1 # WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O # 1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZn # hUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t # 1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxq # D89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmP # frVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSW # rAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv # 231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zb # r17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYcten # IPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQc # xWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17a # j54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQAB # MCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQU # n6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEw # QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E # b2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/ # MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJ # oEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01p # Y1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYB # BQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9v # Q2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3h # LB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x # 5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74p # y27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1A # oL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbC # HcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB # 9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNt # yo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3 # rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcV # v7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A24 # 5oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lw # Y1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA00wggI1AgEBMIH5oYHRpIHOMIHL # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046OTIwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVADhFYWz6ROJmehmICPUG1iPzMI1q # oIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEm # MCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcN # AQELBQACBQDuOtvdMCIYDzIwMjYwODI3MTU1MzAxWhgPMjAyNjA4MjgxNTUzMDFa # MHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAO46290CAQAwBwIBAAICCKMwBwIBAAIC # EjgwCgIFAO48LV0CAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAK # MAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEAkakbk2W/ # YAMNiEG7u7P/6QaqpMYtaAvXgaGBlSSRU8supBuEC1lpRqUmN9cYv/6n7Ee38Jpi # 1YryxEx1koodwCykDWxZYfhFm8FVBjMidF1UzoMqZxmzJ8PqVEtRTDS4GxfzKzfM # AJpUM7KSmmWkT6VA8JjcuT7lWED4byiOOSETdAVv2vCyIPkdh6qaTfIbqHsNosAg # rNP6QmMktoM/fF4+AAuuqEYnE61TOr3C1CDnZlaOlTpLrA7XI+eASewnJtvSvm5f # orZAzoExPXkvazUQmyZ379CwudYMz2tjYQBt2zsvnRUKUrNdpF2GuNg+08YplCJ8 # aliN6ReT8f/02DGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD # QSAyMDEwAhMzAAACI0/ZYCRTz/4rAAEAAAIjMA0GCWCGSAFlAwQCAQUAoIIBSjAa # BgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEID/fuRUR # DZi1t4VOBD1h/iL+rDvliMHbt1D48Jk0kDmrMIH6BgsqhkiG9w0BCRACLzGB6jCB # 5zCB5DCBvQQglvAzLBFu9waLKeOfCMCpxoPjvJi95splEC+0QBHm7rMwgZgwgYCk # fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiNP2WAkU8/+KwAB # AAACIzAiBCABxSI8ELhjDL53Cn8+/cZNuz6IyZJ35Ni81AwDP9UF2DANBgkqhkiG # 9w0BAQsFAASCAgA/JzS3JHGk6EZXSS+FQJ5U1u3nJSLCYSdz9Ugp/Xb5llOJcNG1 # 0hSKOl9NwhTQHMW5FdQEkts7bFlXugpm6jlrYIs8NjdDTleo4mCevIxkyXo25dc1 # La1nCg9U0LzpLP6RQEFnsj8GFZWTRHtTQMkeFm3AM1I25IyNQyhxmzPRSNhNvNHL # sg0hJiCReM2UA7QH2M2JuiJucOtiL4Sf4cjze7Uexh4N3RUPUrz5ZzyFt8Vtzbqw # oySjWsmWli7YKn9mBgiPbEBGq93Dy9DHITzlFpR+9ftT3DYQud0XAy/LNqrl1zUj # bTDZ2XqMuMzolkVTsALYi8qHQmx78aw/xc43B2Qq1m+l6eR2xjtDTSMNcAiDhy0Y # 4F16kK146b4W/Yoj4KSI0k/CRLTC6B0UexAApjTaCmjR2MKmJU3ogJhIemqB1dvJ # oXzDtBFots6628HJmWkR1VsFzBeHHssGvK5g4fKVBVMKf+kwnvbAjoKIv+mSyDyH # OnMamzNrC+pIn7ib3MMs1su2g7tVJSdAnH0EKTMd0YyvGNKXXqezmX5sJUlbk5eq # gs8kiIoYRZ1SMmWkrAyrFE/IeFVKuHNKM5y7gx8ssIrapM3GehvhlVAm9NlkJKZu # VmRrv+hX403VNKAWldabMCCKWbIhdA75fabXKpBjHeIbZZIU4QHh/3ZIqw== # SIG # End signature block |