Functions/Invoke-AnalyzeAzureConnectivity.ps1

function Invoke-AnalyzeAzureConnectivity {
    <#
    .Synopsis
    Analyzes the connectifity to O365 and Azure Endpoints.
 
    .Description
    Analyzes the connectifity to O365 and Azure Endpoints according to https://docs.microsoft.com/en-us/office365/enterprise/urls-and-ip-address-ranges.
 
    Returns array of Messages with four properties:
 
    - Testname: Name of the Tets
    - Type: Information, Warning or Error
    - Issue: Description of the issue
    - Possible Cause: Tips on how to solve the issue.
 
    .Example
    # Displays a deep analyisis of the currently found issues in the system.
    Invoke-AnalyzeAzureConnectivity
 
    #>

    [alias("Invoke-AnalyzeO365Connectivity")]
    [CmdletBinding()]
    param(
        [ValidateSet("Common","Exchange","Skype","SharePoint","All")] 
        [String]
        $UrlSet = "Common",
        [Switch]
        $OnlyRequired
        )
    
    Write-Verbose "Conenctivity Tests to Azure Endpoints in $UrlSet category, which are Required=$OnlyRequired."
    $data = New-Object System.Collections.Generic.List[PSCustomObject]
    $possibleErrors = @()
    $results = New-Object System.Collections.Generic.List[pscustomobject]
    Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls" -percentComplete 0
        
    $EndpointsObjs = Get-AzureO365UrlEndpoint -Path ((Get-Item $PSScriptRoot).Parent.FullName)
    $EndpointsObjs = $EndpointsObjs | Where-Object { ($_.serviceArea -eq $UrlSet -or $UrlSet -eq "All") -and ($OnlyRequired -eq $false -or $_.required -eq $true)}
    Write-Progress -Activity "Connectivity Tests" -status "Load TestUrls finisehed" -percentComplete 100
    Write-Verbose "Found $($EndpointsObjs.length) endpoints to check"
    $j = 0
    foreach($EndpointsObj in $EndpointsObjs){
        Write-Progress -Activity "Connectivity Tests" -status "Building urls for $($EndpointsObj.serviceArea) with id $($EndpointsObj.id)" -percentComplete ($j / $EndpointsObjs.length*100)
        if($null -ne $EndpointsObj.tcpPorts){
            Add-Member -InputObject $EndpointsObj -MemberType NoteProperty -Name tcpPorts -Value "443"
        }
        foreach($Port in $EndpointsObj.tcpPorts.Split(',')){
            switch ($Port) {
                80 {$Protocol = "http://"; $UsePort = "";$TestType="HTTP"; break}
                443 {$Protocol = "https://"; $UsePort = "";$TestType="HTTP"; break}
                default {$Protocol = ""; $UsePort = $Port;$TestType="TCP"; break}
                }
            if($EndpointsObj.PSObject.Properties.Name -match "notes"){
                $Notes = " - " + $EndpointsObj.notes
            } else {
                $Notes = ""
            }
            foreach($url in $EndpointsObj.urls){
                if($TestType -eq "HTTP"){
                    $ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($Protocol + $url) -Path ((Get-Item $PSScriptRoot).Parent.FullName)
                } else {
                    $ExpectedResult = Get-AzureEndpointExpectedResult -TestType $TestType -Url ($url + ":" + $UsePort) -Path ((Get-Item $PSScriptRoot).Parent.FullName)
                }
                if($url -notmatch "\*"){
                    $data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $url; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; ExpectedStatusCode = $ExpectedResult.ActualStatusCode; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; IgnoreCertificateValidationErrors=$ExpectedResult.HasError; Blocked=$ExpectedResult.Blocked; Verbose=$false }) 
                } else {
                    $staticUrls = Get-UrlWildCardLookup -Url $url -Path ((Get-Item $PSScriptRoot).Parent.FullName)
                    if($staticUrls){
                        foreach($staticUrl in $staticUrls){
                            $data.Add([PSCustomObject]@{ TestType = $TestType; TestUrl = $staticUrl; UsePort = $UsePort; Protocol = $Protocol; UrlPattern = $url; ExpectedStatusCode = $ExpectedResult.ActualStatusCode; Description = "$($EndpointsObj.serviceAreaDisplayName)$Notes"; PerformBluecoatLookup=$false; IgnoreCertificateValidationErrors=$ExpectedResult.HasError; Blocked=$ExpectedResult.Blocked; Verbose=$false }) 
                        }
                    } else {

                        $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Warning" -Issue "Could not check connectivity to $url and Port $Port because no static url for this wildcard url was found." -PossibleCause $Cause
                    }
                }
            }
            <#if($EndpointsObj.PSObject.Properties.Name -match "ips"){
                foreach($ip in $EndpointsObj.ips){
                    $firstip = $ip.Split("/")[0]
                    $data.Add(@{ TestUrl = ($Protocol + $firstip + $UsePort); UrlPattern = ($Protocol + $firstip + $UsePort); ExpectedStatusCode = 403; Description = "$($EndpointsObj.serviceAreaDisplayName) - $Notes - Need communication $Protocol to $ip"; PerformBluecoatLookup=$false; Verbose=$false })
                }
            }#>

        }
    }
    
    $possibleErrors = $possibleErrors | Group-Object -Property @("Type", "Issue") | ForEach-Object{ $_.Group | Select-Object * -First 1} 
    $i = 1
    $dataObjs = $data | Group-Object -Property @("TestUrl","TestType","UsePort") | ForEach-Object{ $_.Group | Select-Object * -First 1} 
    ForEach($dataObj in $dataObjs) {
        Write-Progress -Activity "Connectivity Tests" -status "Processing $($d.TestUrl)" -percentComplete ($i / $dataObjs.count*100)
        if($dataObj.TestType -eq "HTTP"){
            $connectivity = Get-HttpConnectivity -TestUrl ($dataObj.Protocol + $dataObj.TestUrl) -Method "GET" -UrlPattern ($dataObj.Protocol + $dataObj.UrlPattern) -ExpectedStatusCode $dataObj.ExpectedStatusCode -Description $dataObj.Description -PerformBluecoatLookup $dataObj.PerformBluecoatLookup -IgnoreCertificateValidationErrors:$dataObj.IgnoreCertificateValidationErrors
        } else {
            $connectivity = Get-TcpConnectivity -TestHostname $dataObj.TestUrl -TestPort $dataObj.UsePort -HostnamePattern ($dataObj.UrlPattern + ":" + $dataObj.UsePort) -ExpectedStatusCode $dataObj.ExpectedStatusCode -Description $dataObj.Description
        }
        $results.Add($connectivity)
        if ($connectivity.Blocked -eq $true -and $dataObj.Blocked -eq $false) {
            $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Connection blocked `n $($connectivity)" -PossibleCause "Firewall is blocking connection to '$($connectivity.UnblockUrl)'."
        }
        if ($connectivity.Resolved -eq $false) {
            $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "DNS name not resolved `n $($connectivity)" -PossibleCause "DNS server not correctly configured."
        }
        if ($connectivity.ActualStatusCode -ne $connectivity.ExpectedStatusCode) {
            if($connectivity.ActualStatusCode -eq 407){
                $Cause = "Keep in mind that the proxy has to be set in WinHTTP.`nWindows 1709 and newer: Set the proxy by using netsh or WPAD. --> https://docs.microsoft.com/en-us/windows/desktop/WinHttp/winhttp-autoproxy-support `nWindows 1709 and older: Set the proxy by using 'netsh winhttp set proxy ?' --> https://blogs.technet.microsoft.com/netgeeks/2018/06/19/winhttp-proxy-settings-deployed-by-gpo/ "
             } else {
                $Cause = "Interfering Proxy server can change HTTP status codes."
             }
            $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Returned Status code '$($connectivity.ActualStatusCode)' is not expected '$($connectivity.ExpectedStatusCode)'`n $($connectivity)" -PossibleCause $Cause
        }
        if ($null -ne $connectivity.ServerCertificate -and $connectivity.ServerCertificate.HasError -and -not $dataObj.IgnoreCertificateValidationErrors) {
            $possibleErrors += New-AnalyzeResult -TestName "Connectivity" -Type "Error" -Issue "Certificate Error when connecting to $($connectivity.TestUrl)`n $(($connectivity.ServerCertificate))" -PossibleCause "Interfering Proxy server can change Certificate or not the Root Certificate is not trusted."
        }
        $i += 1
    }
    Write-Progress -Completed -Activity "Connectivity Tests"
    
    # No errors detected, return success message
    if ($possibleErrors.Count -eq 0) {
        $possibleErrors += New-AnalyzeResult -TestName "All" -Type Information -Issue "All tests went through successfully." -PossibleCause ""
    }

    return $possibleErrors
}
# SIG # Begin signature block
# MIIXxQYJKoZIhvcNAQcCoIIXtjCCF7ICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU3XW4t2pn+iDtFQa1g2p+rfc1
# TlugghL4MIID7jCCA1egAwIBAgIQfpPr+3zGTlnqS5p31Ab8OzANBgkqhkiG9w0B
# AQUFADCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIG
# A1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhh
# d3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcg
# Q0EwHhcNMTIxMjIxMDAwMDAwWhcNMjAxMjMwMjM1OTU5WjBeMQswCQYDVQQGEwJV
# UzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xMDAuBgNVBAMTJ1N5bWFu
# dGVjIFRpbWUgU3RhbXBpbmcgU2VydmljZXMgQ0EgLSBHMjCCASIwDQYJKoZIhvcN
# AQEBBQADggEPADCCAQoCggEBALGss0lUS5ccEgrYJXmRIlcqb9y4JsRDc2vCvy5Q
# WvsUwnaOQwElQ7Sh4kX06Ld7w3TMIte0lAAC903tv7S3RCRrzV9FO9FEzkMScxeC
# i2m0K8uZHqxyGyZNcR+xMd37UWECU6aq9UksBXhFpS+JzueZ5/6M4lc/PcaS3Er4
# ezPkeQr78HWIQZz/xQNRmarXbJ+TaYdlKYOFwmAUxMjJOxTawIHwHw103pIiq8r3
# +3R8J+b3Sht/p8OeLa6K6qbmqicWfWH3mHERvOJQoUvlXfrlDqcsn6plINPYlujI
# fKVOSET/GeJEB5IL12iEgF1qeGRFzWBGflTBE3zFefHJwXECAwEAAaOB+jCB9zAd
# BgNVHQ4EFgQUX5r1blzMzHSa1N197z/b7EyALt0wMgYIKwYBBQUHAQEEJjAkMCIG
# CCsGAQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwPwYDVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NybC50aGF3dGUuY29tL1Ro
# YXd0ZVRpbWVzdGFtcGluZ0NBLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCDAOBgNV
# HQ8BAf8EBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAXBgNVBAMTEFRpbWVTdGFtcC0y
# MDQ4LTEwDQYJKoZIhvcNAQEFBQADgYEAAwmbj3nvf1kwqu9otfrjCR27T4IGXTdf
# plKfFo3qHJIJRG71betYfDDo+WmNI3MLEm9Hqa45EfgqsZuwGsOO61mWAK3ODE2y
# 0DGmCFwqevzieh1XTKhlGOl5QGIllm7HxzdqgyEIjkHq3dlXPx13SYcqFgZepjhq
# IhKjURmDfrYwggSjMIIDi6ADAgECAhAOz/Q4yP6/NW4E2GqYGxpQMA0GCSqGSIb3
# DQEBBQUAMF4xCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRTeW1hbnRlYyBDb3Jwb3Jh
# dGlvbjEwMC4GA1UEAxMnU3ltYW50ZWMgVGltZSBTdGFtcGluZyBTZXJ2aWNlcyBD
# QSAtIEcyMB4XDTEyMTAxODAwMDAwMFoXDTIwMTIyOTIzNTk1OVowYjELMAkGA1UE
# BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTQwMgYDVQQDEytT
# eW1hbnRlYyBUaW1lIFN0YW1waW5nIFNlcnZpY2VzIFNpZ25lciAtIEc0MIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAomMLOUS4uyOnREm7Dv+h8GEKU5Ow
# mNutLA9KxW7/hjxTVQ8VzgQ/K/2plpbZvmF5C1vJTIZ25eBDSyKV7sIrQ8Gf2Gi0
# jkBP7oU4uRHFI/JkWPAVMm9OV6GuiKQC1yoezUvh3WPVF4kyW7BemVqonShQDhfu
# ltthO0VRHc8SVguSR/yrrvZmPUescHLnkudfzRC5xINklBm9JYDh6NIipdC6Anqh
# d5NbZcPuF3S8QYYq3AhMjJKMkS2ed0QfaNaodHfbDlsyi1aLM73ZY8hJnTrFxeoz
# C9Lxoxv0i77Zs1eLO94Ep3oisiSuLsdwxb5OgyYI+wu9qU+ZCOEQKHKqzQIDAQAB
# o4IBVzCCAVMwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAO
# BgNVHQ8BAf8EBAMCB4AwcwYIKwYBBQUHAQEEZzBlMCoGCCsGAQUFBzABhh5odHRw
# Oi8vdHMtb2NzcC53cy5zeW1hbnRlYy5jb20wNwYIKwYBBQUHMAKGK2h0dHA6Ly90
# cy1haWEud3Muc3ltYW50ZWMuY29tL3Rzcy1jYS1nMi5jZXIwPAYDVR0fBDUwMzAx
# oC+gLYYraHR0cDovL3RzLWNybC53cy5zeW1hbnRlYy5jb20vdHNzLWNhLWcyLmNy
# bDAoBgNVHREEITAfpB0wGzEZMBcGA1UEAxMQVGltZVN0YW1wLTIwNDgtMjAdBgNV
# HQ4EFgQURsZpow5KFB7VTNpSYxc/Xja8DeYwHwYDVR0jBBgwFoAUX5r1blzMzHSa
# 1N197z/b7EyALt0wDQYJKoZIhvcNAQEFBQADggEBAHg7tJEqAEzwj2IwN3ijhCcH
# bxiy3iXcoNSUA6qGTiWfmkADHN3O43nLIWgG2rYytG2/9CwmYzPkSWRtDebDZw73
# BaQ1bHyJFsbpst+y6d0gxnEPzZV03LZc3r03H0N45ni1zSgEIKOq8UvEiCmRDoDR
# EfzdXHZuT14ORUZBbg2w6jiasTraCXEQ/Bx5tIB7rGn0/Zy2DBYr8X9bCT2bW+IW
# yhOBbQAuOA2oKY8s4bL0WqkBrxWcLC9JG9siu8P+eJRRw4axgohd8D20UaF5Mysu
# e7ncIAkTcetqGVvP6KUwVyyJST+5z3/Jvz4iaGNTmr1pdKzFHTx/kuDDvBzYBHUw
# ggUnMIIED6ADAgECAhAJT00SLqoJkIvAj67NF8OqMA0GCSqGSIb3DQEBCwUAMHIx
# CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3
# dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJ
# RCBDb2RlIFNpZ25pbmcgQ0EwHhcNMTYwNjA2MDAwMDAwWhcNMTkwNjExMTIwMDAw
# WjBkMQswCQYDVQQGEwJDSDESMBAGA1UECBMJU29sb3RodXJuMREwDwYDVQQHDAhE
# w6RuaWtlbjEWMBQGA1UEChMNYmFzZVZJU0lPTiBBRzEWMBQGA1UEAxMNYmFzZVZJ
# U0lPTiBBRzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+YpjWmBGJ6
# 6p3mACb/iu1w1oUOFAPZVNSZ8nPOY2MNtzi8d2RRSf16+VVSBhy4wv5sg0QAu76I
# 1B5mwWA73gjDERH4LRvisNLrd5cR/CyS1DLZvHY01g7Ck7MtNSekjPEHIc6LFK/4
# 5gQ28nAPcanR2wo+RPGxu34QXKg3ceBH92POm1GDGGUMsTjP7ME7ZOeLKLScJD/V
# rmMH/B6K7ApfAF2O/szxFXrEo+5VcloWoCRHmbFe7nLnAC8k5I63ZBmiSi6EBc89
# ID+XaVWLYvVCNwI/PVEanmDxBG9SAxRnJtcUAYg62S84ClXNj2y53xPUbdZvz3mC
# RTivIlhjH9ECAwEAAaOCAcUwggHBMB8GA1UdIwQYMBaAFFrEuXsqCqOl6nEDwGD5
# LfZldQ5YMB0GA1UdDgQWBBR6hPT/LYCRb+slld/aUoR4eQYCQDAOBgNVHQ8BAf8E
# BAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwdwYDVR0fBHAwbjA1oDOgMYYvaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwNaAz
# oDGGL2h0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtY3MtZzEu
# Y3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBz
# Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4
# MHYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEF
# BQcwAoZCaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkFz
# c3VyZWRJRENvZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN
# AQELBQADggEBAI5wXkMjGctA2E/fchGVptw2Qzdp1a3C1ApX4STqxhkKaQMMJao7
# cHarrQctdjRo2YHEsEsPpOKpQcB2gEUnhWInaghmq618MC/UYZtL/hUcGraEhRO6
# PEDoM/2Xz1+EJJbgmS812YOih1xXrbzfgKE3Zl01VsoNjPvsD4XtEuD0Utjrwsh/
# Qy3gD9Wb925oYOuIz9hp1+jmnQu7hlRaVr7TtxR4aTtTqQdAv35FKPqJdXXUZ9Y9
# otWAWBgWb8YFqMTw6gig3EUORB+MyPXN/zCdwrbAcXlrMIPHhKsvJ6UkxfQkfb4Z
# oztVtMUBChHanEVcX4bVFQwNnDVcrlt8w6IwggUwMIIEGKADAgECAhAECRgbX9W7
# ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa
# Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD
# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l
# qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT
# eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH
# CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+
# bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo
# LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB
# yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow
# eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA
# AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK
# BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j
# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s
# DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS
# dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6
# r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo
# +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz
# sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq
# aGxEMrJmoecYpJpkUe8xggQ3MIIEMwIBATCBhjByMQswCQYDVQQGEwJVUzEVMBMG
# A1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEw
# LwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29kZSBTaWduaW5nIENB
# AhAJT00SLqoJkIvAj67NF8OqMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3AgEMMQow
# CKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC
# AQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJBDEWBBQYNGHaeHDomC2/9j23
# jhDijSjlADANBgkqhkiG9w0BAQEFAASCAQAV6KG8elmKbn9Uhwjs6ufaktEjs5vU
# Efjn+f2UuV84F7GQ/joa2134Uws0Jkww8ngfsneD8QjB20wBgJ3RD0TxunH2urfp
# R1jOn1kaOaN/rjg8F7xzzsgGBZdAQ6YTnsGVBRn/C2XrsvDzH6K0AaODRzQgwrRh
# veWmCZ/CKW+bHJX/abggSV9IwtASF1QYtnpZq7ROV4iwGeE8xWrNAcEcaGmGiGbt
# oBjpeXE0G3fECaYaHOahlTF9m/eH5vfPo13jB/7ZvpbCMy7pPkkUoRfiE231DfQH
# 9Ux8Ppu/+t3GO9d1stSwrKSW/9Q6Xep9KBSwaUCcux6Y0SPjF+qsKOAkoYICCzCC
# AgcGCSqGSIb3DQEJBjGCAfgwggH0AgEBMHIwXjELMAkGA1UEBhMCVVMxHTAbBgNV
# BAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMTAwLgYDVQQDEydTeW1hbnRlYyBUaW1l
# IFN0YW1waW5nIFNlcnZpY2VzIENBIC0gRzICEA7P9DjI/r81bgTYapgbGlAwCQYF
# Kw4DAhoFAKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkF
# MQ8XDTE5MDEwOTE1MjgxNFowIwYJKoZIhvcNAQkEMRYEFL1RSY0BoBjZmQVpT+8E
# tOdMuvVPMA0GCSqGSIb3DQEBAQUABIIBADobNVgTs9MItR9d2F7v/WkVxdNBAQCP
# +PP7YjC5xkecdQkQWi0M5QZaUEr8zkKXut4ugw7r2UXferucXM9gIQ8aKNi8JCUh
# Ar3GLYCWATgR9QaHt+8KqddsdJ5Dfa1GvhkX6Sf/PFIT/6qBz7stfQpqc+BMFfVj
# wMcYDy//20VkdCxl7rrN04Vnji2KCTswv5HEtkXT3wUH6Y4ycTC3mlSup/hlFE0F
# PiTa0fji+Kg6TC1aWKhqDXkc8snhVYnulGN7/U18KlXckyXO8sQJT6zx9A4YaXgc
# HQn/Nf3nkWuNEybZwoAZD50DItf7XKEqk44R3H4iy6CnROJJfDm+8g8=
# SIG # End signature block