AzStackHciConnectivity/AzStackHci.Connectivity.psm1

<#############################################################
 # #
 # Copyright (C) Microsoft Corporation. All rights reserved. #
 # #
 #############################################################>


Import-Module $PSScriptRoot\AzStackHci.Connectivity.Helpers.psm1 -Force -DisableNameChecking -Global
Import-Module $PSScriptRoot\..\AzStackHci.EnvironmentChecker.Utilities.psm1 -Force -DisableNameChecking -Global
Import-Module $PSScriptRoot\..\AzStackHci.EnvironmentChecker.Reporting.psm1 -Force -DisableNameChecking -Global
Import-LocalizedData -BindingVariable lcTxt -FileName AzStackHci.Connectivity.Strings.psd1
function Invoke-AzStackHciConnectivityValidation
{
    <#
    .SYNOPSIS
        Perform AzStackHci Network Validation
    .DESCRIPTION
        Perform AzStackHci Network Validation
    .EXAMPLE
        PS C:\> Invoke-AzStackHciConnectivityValidation
        Perform network validation against all built in service targets from localhost.
    .EXAMPLE
        PS C:\> $Credential = Get-Credential -Message "Credential for local cluster"
        PS C:\> Invoke-AzStackHciConnectivityValidation -Credential $Credential
        Perform network validation against all built in service targets from all cluster nodes of local system.
    .EXAMPLE
        PS C:\> Invoke-AzStackHciConnectivityValidation -Service ARC
        Perform network validation against ARC targets from localhost.
    .EXAMPLE
        PS C:\> $Credential = Get-Credential -Message "Credential for $RemoteSystem"
        PS C:\> $RemoteSystemSession = New-PSSession -Computer 10.0.0.4 -Credential $Credential
        PS C:\> Invoke-AzStackHciConnectivityValidation -PsSession $RemoteSystemSession
        Perform network validation against all built in service target from pre-existing remote PS session.
    .EXAMPLE
        PS C:\> $RemoteSystem = 'node01.contoso.com'
        PS C:\> $Credential = Get-Credential -Message "Credential for $RemoteSystem"
        PS C:\> Invoke-AzStackHciConnectivityValidation -ComputerName $RemoteSystem -Credential $Credential
        Perform network validation against all built in service target from a remote system.
    .INPUTS
        Inputs (if any)
    .OUTPUTS
        Output (if any)
    .NOTES
        View Results: Use the commands above but add the results to a variable for simplicity then use the following to view e.g.
        $result = Invoke-AzStackHciConnectivityValidation -Service ARC
 
        # Review first result in detail
        $result[0]
 
        $formattingHelpScriptBlock = {$_.AdditionalData | Select-Object ComputerName, StatusCode, EndPoint | ConvertTo-Json};
        $Result | Format-Table Status, Title, Service, OperationType, @{Name ='Detail'; Expression = $formattingHelpScriptBlock } -AutoSize -Wrap
 
        View Log file: Get-Content "$Home\.AzStackHci\AzStackHciEnvironmentChecker.log"
    #>

    [CmdletBinding()]
    param (

        [Parameter(Mandatory = $false, HelpMessage = "Specify the Computer Name(s) to run validation from. If null the local machine will be used.")]
        [string[]]
        $ComputerName,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the Cluster Name to run validation from. If null the local machine will be used.")]
        [string]
        $Cluster,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the credential used to connect to remote computer.")]
        [pscredential]
        $Credential,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the authentication used to connect to remote computer.")]
        [System.Management.Automation.Runspaces.AuthenticationMechanism]
        $Authentication,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the PsSession(s) used to validation from. If null the local machine will be used.")]
        [System.Management.Automation.Runspaces.PSSession[]]
        $PsSession,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the services to target for connectivity validation.")]
        [ArgumentCompleter({ Get-AzStackHciConnectivityServiceName })]
        [ValidateScript({ $_ -in (Get-AzStackHciConnectivityServiceName) })]
        [string[]]
        $Service,

        [Parameter(Mandatory = $false, HelpMessage = "Specify the Operation Type to target for connectivity validation. e.g. Deployment, Update, etc...")]
        [ArgumentCompleter({ Get-AzStackHciConnectivityOperationName })]
        [ValidateScript({ $_ -in (Get-AzStackHciConnectivityOperationName) })]
        [string[]]
        $OperationType,

        [Parameter(Mandatory = $false, HelpMessage = "Specify proxy server.")]
        [string]
        $Proxy,

        [Parameter(Mandatory = $false, HelpMessage = "Specify proxy server credential.")]
        [pscredential]
        $ProxyCredential,

        [Parameter(Mandatory = $false, HelpMessage = "Return PSObject result.")]
        [switch]
        $PassThru,

        [Parameter(Mandatory = $false, HelpMessage = "Directory path for log and report output")]
        [string]$OutputPath,

        [Parameter(Mandatory = $false, HelpMessage = "Remove all previous progress and create a clean report")]
        [switch]$CleanReport = $false

    )

    try
    {
        $script:ErrorActionPreference = 'Stop'
        Set-AzStackHciOutputPath -Path $OutputPath
        $script:Standalone = [bool](Get-Module -Name AzStackHci.EnvironmentChecker -ErrorAction SilentlyContinue)
        if ($script:Standalone -and -not $PassThru)
        {
            Write-AzStackHciHeader -invocation $MyInvocation -params $PSBoundParameters
            Log-Info "Running in standalone mode"
            Test-ModuleUpdate
        }

        # Call/Initialise reporting
        $envcheckerReport = Get-AzStackHciEnvProgress -clean:$CleanReport
        $envcheckerReport = Add-AzStackHciEnvJob -report $envcheckerReport

        # Create neccessary PsSessions
        if (-not $PsSession)
        {
            $PsSession = Open-PsSession -ComputerName $ComputerName -Cluster $Cluster -Authentication $Authentication -Credential $Credential
        }

        # TO DO: Omit system?
        $ConnectivityTargets = Get-AzStackHciConnectivityTarget -Service $Service -OperationType $OperationType
        Write-Progress -Id 1 -Activity "Checking AzStackHci Dependancies" -Status "Environment configuration" -PercentComplete 0 -ErrorAction SilentlyContinue

        # Run validation
        $i = 0
        $webResult = @()
        $diagnosticResults = @()
        if ($PsSession)
        {
            foreach ($Session in $PsSession)
            {
                if ($Session.State -ne 'Opened')
                {
                    try
                    {
                        Connect-PSSession -Session $Session
                    }
                    catch
                    {
                        $PsSessionFail = $lcTxt.PsSessionFail -f $Session.ComputerName, $_.Exception.Message
                        Log-Info ($PsSessionFail) -type Error
                        throw $PsSessionFail
                    }
                }
                $diagnosticResults += Test-Dns -PsSession $Session
                # Fail fast if dns fails
                if ($diagnosticResults[-1].Status -ne 'Succeeded')
                {
                    Log-Info ($lcTxt.DnsFail -f $Session.ComputerName, ($dnsResult.AdditionalData.Detail | ConvertTo-Json)) -Type Warning -ConsoleOut
                }
                $diagnosticResults += Get-ProxyDiagnostics -PsSession $Session -Proxy $Proxy
                $diagnosticResults += Test-RootCA -PsSession $Session -Proxy $Proxy -ProxyCredential $ProxyCredential
                if ($diagnosticResults.Status -contains 'Failed')
                {
                    throw $lcTxt.DiagnosticSystemFailure -f ($diagnosticResults | Where-Object Status -EQ 'Failed' | ConvertTo-Json)
                }
                foreach ($Target in $ConnectivityTargets)
                {
                    $i++
                    $progressPercentage = ($i / (($ConnectivityTargets.Count) * $PsSession.Count )) * 100
                    Write-Progress -Id 1 -Activity "Checking AzStackHci dependencies to $($Target.Service)" -Status "Connectivity from $($Session.ComputerName) to $($Target.Title) Endpoints" -PercentComplete $progressPercentage -ErrorAction SilentlyContinue
                    Log-Info -Message ("Checking {0} on {1}" -f $target.Title, $session.ComputerName)
                    if ($Proxy)
                    {
                        $webResult += Invoke-WebRequestEx -Target $Target -PsSession $Session -Proxy $Proxy -ProxyCredential $ProxyCredential
                    }
                    else
                    {
                        $webResult += Invoke-TestNetConnection -Target $Target -PsSession $Session
                    }
                }
            }
        }
        else
        {
            $diagnosticResults += Test-Dns
            # Fail fast if dns fails
            if ($diagnosticResults[-1].Status -ne 'Succeeded')
            {
                Log-Info ($lcTxt.DnsFail -f $ENV:COMPUTERNAME, ($dnsResult.AdditionalData.Detail | ConvertTo-Json)) -Type Warning -ConsoleOut
            }
            $diagnosticResults += Get-ProxyDiagnostics -Proxy $Proxy
            $diagnosticResults += Test-RootCA -Proxy $Proxy -ProxyCredential $ProxyCredential
            if ($diagnosticResults.Status -contains 'Failed')
            {
                $failedDiagnosticResult = $diagnosticResults | Where-Object Status -EQ 'Failed' | ConvertTo-Json
                $failedDiagnosticDetailMsg = ($diagnosticResults | Where-Object Status -EQ 'Failed' | Select-Object -ExpandProperty AdditionalData | Select-Object -ExpandProperty Detail) -join '. '
                throw $lcTxt.DiagnosticSystemFailure -f $failedDiagnosticDetailMsg, $failedDiagnosticResult
            }
            foreach ($Target in $ConnectivityTargets)
            {
                $i++
                $progressPercentage = ($i / ($ConnectivityTargets.Count)) * 100
                $progressParams = @{
                    Id              = 1
                    Activity        = "Checking AzStackHci dependencies to $($Target.Service)"
                    Status          = if ($Proxy) { "Connectivity from $ENV:ComputerName (via $Proxy) to $($Target.Title) Endpoints" } else { "Connectivity from $ENV:ComputerName to $($Target.Title) Endpoints" }
                    PercentComplete = $progressPercentage
                    ErrorAction     = 'SilentlyContinue'
                }
                Write-Progress @progressParams
                Log-Info -Message ("Checking {0} (proxy:'{1}')" -f $target.Title, $proxy)
                if ($target.Protocol -notmatch 'http' -or -not $Proxy)
                {
                    $webResult += Invoke-TestNetConnection -Target $Target
                }
                else
                {
                    $webResult += Invoke-WebRequestEx -Target $Target -Proxy $Proxy -ProxyCredential $ProxyCredential
                }
            }
        }

        # Feedback results - user scenario
        if ($script:Standalone -and -not $PassThru)
        {
            Write-Host 'Connectivity Results'
            $webresult.service | Sort-Object | Get-Unique | Where-Object { $PSITEM -ne 'System' } | ForEach-Object {
                Write-AzStackHciResult -Title $PSITEM -Result ($webResult | Where-Object Service -EQ $PSITEM)
            }
            Write-AzStackHciResult -Title 'Diagnostics' -Result $diagnosticResults -Expand
            Write-Summary -Result $webResult -Property1 Source -Property2 Resource
            Write-FailedUrls -Result $webResult
        }
        else
        {
            return ($webResult + $diagnosticResults)
        }


    }
    catch
    {
        Log-Info -Message "" -ConsoleOut
        Log-Info -Message "$($_.Exception.Message)" -ConsoleOut -Type Error
        Log-Info -Message "$($_.ScriptStackTrace)" -ConsoleOut -Type Error
        $cmdletFailed = $true
        throw $_.exception
    }
    finally
    {
        # Clean up
        if ($PsSession -and -not $PSBoundParameters['PsSession'])
        {
            $PsSession | Remove-PSSession
        }
        # Write result to telemetry channel
        foreach ($r in ($webResult + $diagnosticResults))
        {
            Write-ETWResult -Result $r
        }
        # Write validation result to report object and close out report
        $envcheckerReport | Add-Member -MemberType NoteProperty -Name 'Connectivity' -Value $webResult -Force
        $envcheckerReport | Add-Member -MemberType NoteProperty -Name 'Diagnostics' -Value $diagnosticResults -Force
        $envcheckerReport = Close-AzStackHciEnvJob -report $envcheckerReport
        Write-AzStackHciEnvReport -report $envcheckerReport
        Write-AzStackHciFooter -invocation $MyInvocation -Failed:$cmdletFailed
        Remove-Variable -Name AzStackHciConnectivityTargets -Scope GLOBAL -ErrorAction SilentlyContinue
    }
}
# SIG # Begin signature block
# MIInsQYJKoZIhvcNAQcCoIInojCCJ54CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA5arjBAloiO8Yy
# yw9g9yR+3aASlbhBoTeCZOE4nb72taCCDYUwggYDMIID66ADAgECAhMzAAACzfNk
# v/jUTF1RAAAAAALNMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAyWhcNMjMwNTExMjA0NjAyWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDrIzsY62MmKrzergm7Ucnu+DuSHdgzRZVCIGi9CalFrhwtiK+3FIDzlOYbs/zz
# HwuLC3hir55wVgHoaC4liQwQ60wVyR17EZPa4BQ28C5ARlxqftdp3H8RrXWbVyvQ
# aUnBQVZM73XDyGV1oUPZGHGWtgdqtBUd60VjnFPICSf8pnFiit6hvSxH5IVWI0iO
# nfqdXYoPWUtVUMmVqW1yBX0NtbQlSHIU6hlPvo9/uqKvkjFUFA2LbC9AWQbJmH+1
# uM0l4nDSKfCqccvdI5l3zjEk9yUSUmh1IQhDFn+5SL2JmnCF0jZEZ4f5HE7ykDP+
# oiA3Q+fhKCseg+0aEHi+DRPZAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU0WymH4CP7s1+yQktEwbcLQuR9Zww
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ3MDUzMDAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AE7LSuuNObCBWYuttxJAgilXJ92GpyV/fTiyXHZ/9LbzXs/MfKnPwRydlmA2ak0r
# GWLDFh89zAWHFI8t9JLwpd/VRoVE3+WyzTIskdbBnHbf1yjo/+0tpHlnroFJdcDS
# MIsH+T7z3ClY+6WnjSTetpg1Y/pLOLXZpZjYeXQiFwo9G5lzUcSd8YVQNPQAGICl
# 2JRSaCNlzAdIFCF5PNKoXbJtEqDcPZ8oDrM9KdO7TqUE5VqeBe6DggY1sZYnQD+/
# LWlz5D0wCriNgGQ/TWWexMwwnEqlIwfkIcNFxo0QND/6Ya9DTAUykk2SKGSPt0kL
# tHxNEn2GJvcNtfohVY/b0tuyF05eXE3cdtYZbeGoU1xQixPZAlTdtLmeFNly82uB
# VbybAZ4Ut18F//UrugVQ9UUdK1uYmc+2SdRQQCccKwXGOuYgZ1ULW2u5PyfWxzo4
# BR++53OB/tZXQpz4OkgBZeqs9YaYLFfKRlQHVtmQghFHzB5v/WFonxDVlvPxy2go
# a0u9Z+ZlIpvooZRvm6OtXxdAjMBcWBAsnBRr/Oj5s356EDdf2l/sLwLFYE61t+ME
# iNYdy0pXL6gN3DxTVf2qjJxXFkFfjjTisndudHsguEMk8mEtnvwo9fOSKT6oRHhM
# 9sZ4HTg/TTMjUljmN3mBYWAWI5ExdC1inuog0xrKmOWVMIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGYIwghl+AgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAALN82S/+NRMXVEAAAAA
# As0wDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEILBK
# OcebFZuGhOgpQXdOLebAf17EzNEq8mfJsm7K7sHWMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEABLKrzFotxdUKMWYyLtOltZVZKFJ8nHMSUjPi
# EyZKbPjqtIfTB5PajM9kTRXHXZbnfFG9QCJKHxrr5194IhC/bPn9T1Fw6jkS/1Pk
# +lZBP0AbyK+J/E2N4MJW6i3qfw+s29qka7PT23u9sAt33Uu1H+H0p3RhajXo4CZT
# bHGDY8MvrPVkoS/e6fieP5kijj4n7MYqFSAfEpfZaeQ2U4GuWlnOxAWz47VqCI/E
# ovnN0+A/qC4BIHIFnphD3qjuynD59feYH9mWlWt++IBV7jygCSBuCcdXJAXd7p8/
# IdslkZTs31Fv6MwSQhI1WI4G0wYMZ1nzFjM9Mmt2MNuSwNwWTKGCFwwwghcIBgor
# BgEEAYI3AwMBMYIW+DCCFvQGCSqGSIb3DQEHAqCCFuUwghbhAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFVBgsqhkiG9w0BCRABBKCCAUQEggFAMIIBPAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCA0PHH8tSjHxqq75C3SCUBUheKIMZH0CP/K
# UFPr1nkq6gIGYvuhQcvjGBMyMDIyMDgzMTExMDAzNS40MTNaMASAAgH0oIHUpIHR
# MIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQL
# EyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhh
# bGVzIFRTUyBFU046NjBCQy1FMzgzLTI2MzUxJTAjBgNVBAMTHE1pY3Jvc29mdCBU
# aW1lLVN0YW1wIFNlcnZpY2WgghFfMIIHEDCCBPigAwIBAgITMwAAAaZZRYM5TZ7r
# SwABAAABpjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0Eg
# MjAxMDAeFw0yMjAzMDIxODUxMjFaFw0yMzA1MTExODUxMjFaMIHOMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQg
# T3BlcmF0aW9ucyBQdWVydG8gUmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046
# NjBCQy1FMzgzLTI2MzUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDZmL97UiPnyfzU
# CZ48+ybwp3Pl5tKyqHvWCg+DLzGArpe3oHa0/87+bxW0IIzUO+Ou9nzcHms7ZEeu
# VfMtvbuTy9rH9NafrnIXtGbuLUooPhrEOmUJfbYz0QGP9yEwRw3iGMr6vFp3vfuz
# aDy4cQ0junbV+2ArkOM3Ez90hOjLweG+TYoIXbb6GVWmJNZV6Y1E33ZiqF9QAatb
# CW1C0p0otEHeL75d5mfY8cL/XUf55WT+tpa2WGauyz7Rw+gZZnJQeT0/PQ50ptbI
# 2mZxR6yszrJquRpZi+UhboAgmTqCs9d9xSXkGhTHFwWUgkIzQAVgWxyEQhNcrBxx
# vNw3aJ0ZpwvBDpWHkcE1s/0As+qtK4jiG2MgvwNgYFBKbvf/RMpq07MjK9v80vBn
# RMm0OVu39Fq3K5igf2OtvoOk5nzkvDbVPi9YxqCjRukOUZXycGbvCf0PXZeDschy
# rsu/PsJuh7Be7gIs6bFoet1FGqCvzhkIgRtzSfpHn+XlqZ72uGSX4QJ6mEwGQ9bh
# 4H/FX0I55dAQdmF8yvVmk6nXvHfvKgsVSq+YSWL2zvl9/tpOTwoq1Cv0m6K3l/sV
# IVWkBIVQ2KpWrcj7bSO2diK5ITM8Bb3PqdEHsjIjZqNnAWXo8fInAznFIncMpg1G
# KhjxOzAPL7Slt33nkkmCbAhJLlDv7wIDAQABo4IBNjCCATIwHQYDVR0OBBYEFDpU
# ITv8xpaivfVJDS/xrvwK8jfYMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1
# GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEp
# LmNybDBsBggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUy
# MFBDQSUyMDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB
# BQUHAwgwDQYJKoZIhvcNAQELBQADggIBAIDA8Vg06Rqi5xaD4Zv4g38BxhfMa9jW
# 6yZfHoBINk4UybE39MARPmULJ2H60ZlwW3urAly1Te9Kj7iPjhGzeTDmouwbntf+
# I+VU5Fqrh+RmXlWrdjfnQ+5UlFqdHVPI/rgYQS+RhUpqA1VZvs1thkdo7jyNb9ue
# ACU29peOfGp5ZCYxr5mJ9gbUUtd4f8A0e4a0GiOwYHch1gFefhxI+VIayK677cCY
# or0mlBAN6iumSv62SEL/7jkQ5DjcPtqRxyBNUl5v1iJYa1UthyKIH69yY6r2YqJ+
# iyUg++NY/MVQy4gpcAG7KR6FRY8bcQXDI6j8emlgiUvL40qE54ZFeDzueZqrDO0P
# F0ERkIQO8OMzUDibvZA+MRXWKT1Jizf3WiHBBJaHwYxs/rBHdQeMqqiJN7thuFco
# E1xZrYS/HIUqO6/hiL06lioUgP7Gp0uDd4woAgntxU0ibKeIOZ8Gry71gLc3DiL0
# kaKxpgHjdJtsIMwSveU/6oKxhg10qLNSTQ1kVQZz9KrMNUKKuRtA/Icb0D7N1+Ny
# gb9RiZdMKOa3AvvTjFsSZQet4LU6ELANQhK2KGCzGbVMyS++I8GZP4K6RxEISIQd
# 7J3gvMMxiibn7e2Dvx1gqbsHQoSI8p05wYfshRjHYN8EayGznMP4ipl2aKTE0DDn
# JiHiMCQHswOwMIIHcTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+
# F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU
# 88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqY
# O7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzp
# cGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0Xn
# Rm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1
# zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZN
# N3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLR
# vWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTY
# uVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUX
# k8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB
# 2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKR
# PEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0g
# BFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQM
# MAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQE
# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQ
# W9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNv
# bS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBa
# BggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqG
# SIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOX
# PTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6c
# qYJWAAOwBb6J6Gngugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/z
# jj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz
# /AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyR
# gNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdU
# bZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo
# 3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4K
# u+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10Cga
# iQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9
# vMvpe784cETRkPHIqzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGC
# AtIwggI7AgEBMIH8oYHUpIHRMIHOMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSkwJwYDVQQLEyBNaWNyb3NvZnQgT3BlcmF0aW9ucyBQdWVydG8g
# UmljbzEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046NjBCQy1FMzgzLTI2MzUxJTAj
# BgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMC
# GgMVAGp0M62VvUwfd1Xuz2uFD2qNn3ytoIGDMIGApH4wfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDmuTz8MCIYDzIwMjIwODMx
# MDU1MDUyWhgPMjAyMjA5MDEwNTUwNTJaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIF
# AOa5PPwCAQAwCgIBAAICJd8CAf8wBwIBAAICESMwCgIFAOa6jnwCAQAwNgYKKwYB
# BAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGG
# oDANBgkqhkiG9w0BAQUFAAOBgQAynUGdhUv2+xZqhLB2/rL/Fq7y80num2UZfE6f
# f2loFEhumdarTjsj/FUews6IwkeSTwnr0mlegjE9zPi7nPN3ypYemHPAd4PwUzvs
# IpOEJ0O5S6G/UpLuu2+4neis/088cUVtnP/niIyv2LuD+8hg4fxzJit8h7hwyeDr
# 5eR0VjGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# AhMzAAABpllFgzlNnutLAAEAAAGmMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG
# 9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEINHBE28+eLD94LqD
# 4t/g4uLZ0hlFO2YMchZcUzfSEZIEMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCB
# vQQggwsZi8M/dH1r4TCmyUwEGirdw6F3ogIX6fEw/bYEqw0wgZgwgYCkfjB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAaZZRYM5TZ7rSwABAAABpjAi
# BCBHDZboXFSwAFH0+LRbLDCHXGHjNu8UTi6Xq+wtpT4wQTANBgkqhkiG9w0BAQsF
# AASCAgBlhdTAxqTqC8rGcbIejVJKGJYRCbSn+CorQ/ivHnzbVj8yxnWEf369TY/M
# dQiqM7xg+dI4ruDYyIA+1u7l0R0NINflOb3FRT4JwHbX6QMHuZL1bvJWyUSbvNJI
# WkO9oHjZgPb33YECUEz+Bnv6za/EHDGzNcLF9PywrDZ9Fi4/8d7idtdWlnzQNnjs
# 9M42MgWNlVDoNZocC5uDt4nS0ApQl4CqP8ysWII+h9gS8CB+3Wj7po2hRIIYWQug
# A1YWCVtWyRGLvZGB7gXIQ2AomsUg6AxotzCqLYI/yRUJmuX7ZfcInP2J1nmalmgc
# mCwntF4A7pzf8KiEBS/+cUrkcU0bBdJqZyLYTnrluGlyr9zFDd1Xjrqh3K1Qpkmi
# NpoVc324JI4E8og0xtQ8OWY4QoORLtHJzJKlh+IT9WddTm1KbglfmtUEd+SlvZsa
# titxRdJfmRJYic3Sp3rLcCNbDqPKdSq9j4rCGDLyhTb0OLfOKjyYQ9W+0l5M3VAR
# TrCLG6E7L+XQqKgn9Bkd4ZQL3cGjqRM41FlAn5GxcxbuLhg5pzr+yd8RHKx6TjMp
# 4pIVNEAEm08HYc/f8MN+6JRaYYKD6uq9txjNcS8ejBreEEblHVGprQ0DTCs6oJdE
# tZlVdYHe1s/Mr1iHobeafr0nD6n3BgceoUZPCXrdT9oahsBp3Q==
# SIG # End signature block