AzureValidation/Microsoft.AzureStack.AzureValidation.psm1

$erroractionpreference = 'stop'
Import-LocalizedData LocalizedData -BaseDirectory $PSScriptRoot -Filename Microsoft.AzureStack.AzureValidation.Strings.psd1

Import-Module $PSSCriptRoot\Microsoft.AzureStack.AzureValidation.Internal.psm1 -force
Import-Module $PSScriptRoot\..\Microsoft.AzureStack.ReadinessChecker.Utilities.psm1 -Force
function Invoke-AzsRegistrationValidation {
    <#
    .SYNOPSIS
        Validates Credential and Subscription for Azure Stack Registration.
    .DESCRIPTION
        Validate registration before you begin an Azure Stack deployment:
        Validates the Azure subscription provided is a supported type. Subscriptions must be a Cloud Service Provider (CSP) or Enterprise Agreement (EA).
        The account provided can sign in to Azure and is a subscription owner.
    .EXAMPLE
        PS C:\> Connect-AzAccount -subscription 3f764e1c-d0ab-49e3-99aa-44124d56dd2d
        PS C:\> Invoke-AzsRegistrationValidation -RegistrationSubscriptionID 3f764e1c-d0ab-49e3-99aa-44124d56dd2d
        Validates Credential and Subscription for Azure Stack Registration.
    .PARAMETER Context
        Specifies Azure PowerShell context representing the context of intended registration account
    .PARAMETER DeploymentDataJSONPath
        Specifies the Azure Stack deployment deployment data JSON configuration file. Generally only available to the deployment team.
    .PARAMETER ForceTLS12
        Force the use of TLS 1.2, true by default
    .PARAMETER OutputPath
        Specifies custom path to save Readiness JSON report and Verbose log file.
    .PARAMETER CleanReport
        Specifies whether to purge the existing report and start again with a clean report. Execution history and validation data is lost for all Readiness Checker validations.
    .LINK
        Azure Stack Registration Validation - https://aka.ms/AzsValidateAzureRegistration
        Azure Stack Readiness Checker - https://aka.ms/AzsReadinessChecker
    #>


    [OutputType([Array])]
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ParameterSetName = 'RegistrationJSON')]
        [ValidateScript( {Test-Path $_ -Include *.json})]
        [string]
        $deploymentDataJSONPath,

         [Parameter(Helpmessage = 'Azure PowerShell context representing the context of intended registration account')]
         [ValidateNotNull()]
         [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context = (Get-AzContext -ErrorAction Stop),

        [Parameter(Mandatory = $true, ParameterSetName = 'Registration')]
        [Parameter(Mandatory = $true, ParameterSetName = 'RegistrationJSON')]
        [string]
        $RegistrationSubscriptionID,

        [Parameter(Mandatory = $false, HelpMessage = "Force the use of TLS 1.2, true by default")]
        [bool]$ForceTLS12 = $true,

        [Parameter(HelpMessage = "Directory path for log and report output")]
        [string]$OutputPath = "$ENV:TEMP\AzsReadinessChecker",

        [Parameter(HelpMessage = "Remove all previous progress and create a clean report")]
        [switch]$CleanReport = $false
    )
    try {
        $thisFunction = $MyInvocation.MyCommand.Name
        $GLOBAL:OutputPath = $OutputPath
        Import-Module $PSScriptRoot\..\Microsoft.AzureStack.ReadinessChecker.Reporting.psm1 -Force
        Import-Module $PSSCriptRoot\AzureADConfiguration.psm1 -Force -Global

        Write-Header -invocation $MyInvocation -params $PSBoundParameters

        # Ensure the local machine has Az.Accounts module
        if (-not (Get-Module -Name 'Az.Accounts' -ListAvailable | Where-Object Version -ge '2.2.8')) {
            Write-AzsReadinessLog -Message $LocalizedData.NoAzAccounts -Type Error -Function $thisFunction
            throw $LocalizedData.NoAzAccounts
        }

        # Parse DeploymentData JSON
        if ($PSCmdlet.ParameterSetName -eq 'RegistrationJSON') {
            $deploymentDataJSON = ConvertTo-DeploymentData -path $deploymentDataJSONPath
            $AzureEnvironment = $deploymentDataJSON.DeploymentData.InfraAzureEnvironment
        }

        # If user specifies custom azure environment, overwrite the AzureEnvironment as the json path

        # Get/Clean Existing Report
        $readinessReport = Get-AzsReadinessProgress -clean:$CleanReport
        $readinessReport = Add-AzsReadinessCheckerJob -report $readinessReport
        $registrationOutput = @{}

        # exit immediately if the user hasn't authenticated to Azure
        if (-not $context){
            Write-AzsReadinessLog -Message $LocalizedData.NoAzureContext -Type Error -Function $thisFunction
            throw $LocalizedData.NoAzureContext
        }

        # Force Security Profile to TLS1.2
        if ($ForceTLS12) {
            # Change Security Protocol to TLS1.2 for the session and track for clean up later.
            $restoreSecurityProtocol = [Net.ServicePointManager]::SecurityProtocol
            $tempSecurityProtocol = [Net.SecurityProtocolType]::Tls12
            Set-SecurityProtocol -securityProtocol $tempSecurityProtocol
        }

        Write-Host "Checking Registration Requirements: " -nonewline
        $tenantId = (Get-AzContext).Tenant.Id
        if ($null -eq $tenantid) {
            throw "No TenantId."
        }
        else {
            $registrationSubscription = Get-AzureSubscriptionDetail -tenantid $tenantid -subscriptionid $RegistrationSubscriptionID
        }

        ## expand test-azsregistrationaccount to test graph endpoint
        if (-not $registrationSubscription.errordetails) {
            $registrationOutput = Test-AzsRegistrationAccount -subscription $registrationSubscription.subscription -subscriptionId $RegistrationSubscriptionID -tenantId $tenantid
            if ($registrationOutput.result -ne 'OK') {
                Write-Host "Fail " -foregroundColor Red
                Write-Host ("Error Details for registration account {0}:" -f (Get-AzContext).Account.Id)
                $registrationOutput.errorDetails | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
                Write-Host ("Additional help URL {0}" -f "https://aka.ms/AzsRemediateRegistration")
            }
            else {
                Write-Host "OK" -foregroundColor Green
            }
        }
        else {
            Write-Host "Fail " -foregroundColor Red
            Write-Host ("Error Details for registration account {0}:" -f (Get-AzContext).Account.Id)
            $registrationSubscription.errorDetails | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
            Write-Host ("Additional help URL {0}" -f "https://aka.ms/AzsRemediateRegistration")
        }
        $registrationOutput.Add('SubscriptionDetail', $registrationSubscription)
    }
    catch {
        Write-Host "Fail " -foregroundColor Red
        $errorDetail = ("Checking Registration failed with: {0}" -f $_.exception.Message)
        Write-Host ("Error Details for registration account {0}:" -f (Get-AzContext).Account.Id)
        $errorDetail | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
    }
    finally {
        # Write results to readiness report
        $readinessReport.AzureValidation.AzureRegistration = $registrationOutput
        $readinessReport = Close-AzsReadinessCheckerJob -report $readinessReport
        Write-AzsReadinessProgress -report $readinessReport
        Write-AzsReadinessReport -report $readinessReport

        # Remove global variables
        Remove-Variable -Name OutputPath
        # Restore TLS back to default
        if ($ForceTLS12 -and $restoreSecurityProtocol) {
            Set-SecurityProtocol -securityProtocol $restoreSecurityProtocol
        }
        Write-Footer -invocation $MyInvocation
    }
}
function Invoke-AzsAzureIdentityValidation {
    <#
    .SYNOPSIS
        Validates Service Administrator and AAD Tenant for Azure Stack Identity
    .DESCRIPTION
        Validate identity before you begin an Azure Stack Deployment
        Validates Service Administrator provided is a Global Administrator of the AAD Tenant directory provided.
    .EXAMPLE
        PS C:\> Connect-AzAccount -tenant contoso.onmicrosoft.com
        PS C:\> Invoke-AzsAzureIdentityValidation -AADDirectoryTenantName contoso.onmicrosoft.com
        Validates Service Administrator and AAD Tenant for Azure Stack Identity
    .PARAMETER Context
        Azure PowerShell context representing the context of intended Service Administrator Account
    .PARAMETER AADDirectoryTenantName
        Specifies the Azure Active Directory name intended to be used for Azure Stack deployment.
    .PARAMETER ForceTLS12
        Force the use of TLS 1.2, true by default
    .PARAMETER OutputPath
        Specifies custom path to save Readiness JSON report and Verbose log file.
    .PARAMETER CleanReport
        Specifies whether to purge the existing report and start again with a clean report. Execution history and validation data is lost for all Readiness Checker validations.
   .LINK
        Azure Stack Identity Validation - https://aka.ms/AzsValidateAzureIdentity
        Azure Stack Readiness Checker - https://aka.ms/AzsReadinessChecker
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ParameterSetName = 'InstallJSON')]
        [ValidateScript( {Test-Path $_ -Include *.json})]
        [string]
        $deploymentDataJSONPath,

        [Parameter(HelpMessage = "Azure PowerShell context representing the context of intended Service Administrator Account")]
        [ValidateNotNull()]
        [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context = (Get-AzContext -ErrorAction Stop),

        [Parameter(Mandatory = $true, ParameterSetName = 'Install', HelpMessage = "Enter primary domain name of Azure Tenant Directory")]
        [string]$AADDirectoryTenantName,

        [Parameter(Mandatory = $false, HelpMessage = "Force the use of TLS 1.2, true by default")]
        [bool]$ForceTLS12 = $true,

        [Parameter(HelpMessage = "Directory path for log and report output")]
        [string]$OutputPath = "$ENV:TEMP\AzsReadinessChecker",

        [Parameter(HelpMessage = "Remove all previous progress and create a clean report")]
        [switch]$CleanReport = $false
    )
    try {
        $thisFunction = $MyInvocation.MyCommand.Name
        $GLOBAL:OutputPath = $OutputPath
        Import-Module $PSScriptRoot\..\Microsoft.AzureStack.ReadinessChecker.Reporting.psm1 -Force
        Import-Module $PSSCriptRoot\AzureADConfiguration.psm1 -Force -Global

        Write-Header -invocation $MyInvocation -params $PSBoundParameters

        # Ensure the local machine has Az.Accounts module
        if (-not (Get-Module -Name 'Az.Accounts' -ListAvailable | Where-Object Version -ge '2.2.8')) {
            Write-AzsReadinessLog -Message $LocalizedData.NoAzAccounts -Type Error -Function $thisFunction
            throw $LocalizedData.NoAzAccounts
        }

        if ($PSCmdlet.ParameterSetName -eq 'InstallJSON') {
            $deploymentDataJSON = ConvertTo-DeploymentData -path $deploymentDataJSONPath
            $AzureEnvironment = $deploymentDataJSON.DeploymentData.InfraAzureEnvironment
            $AADDirectoryTenantName = $deploymentDataJSON.DeploymentData.InfraAzureDirectoryTenantName
        }

        # Get/Clean Existing Report
        $readinessReport = Get-AzsReadinessProgress -clean:$CleanReport
        $readinessReport = Add-AzsReadinessCheckerJob -report $readinessReport
        $serviceAdministratorOutput = @{}

        # exit immediately if the user hasn't authenticated to Azure
        if (-not $context){
            Write-AzsReadinessLog -Message $LocalizedData.NoAzureContext -Type Error -Function $thisFunction
            throw $LocalizedData.NoAzureContext
        }

        # Force Security Profile to TLS1.2
        if ($ForceTLS12) {
            # Change Security Protocol to TLS1.2 for the session and track for clean up later.
            $restoreSecurityProtocol = [Net.ServicePointManager]::SecurityProtocol
            $tempSecurityProtocol = [Net.SecurityProtocolType]::Tls12
            Set-SecurityProtocol -securityProtocol $tempSecurityProtocol
        }

        Write-AzsReadinessLog -Message ("Starting Azure Identity Validation`n") -Type Info -Function $thisFunction -toScreen
        Write-Host "Checking Installation Requirements: " -nonewline

        $serviceAdministratorOutput = Test-AzsServiceAdministrator -AADDirectoryTenantName $AADDirectoryTenantName
        if ($serviceAdministratorOutput.result -ne 'OK') {
            Write-Host "Fail " -foregroundColor Red
            if ($serviceAdministratorOutput.errorDetails -ne @()) {
                Write-Host ("Error Details for Service Administrator Account {0}" -f $AADServiceAdministrator.Username)
                $serviceAdministratorOutput.errorDetails | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
                Write-Host ("Additional help URL {0}" -f "https://aka.ms/AzsRemediateAzureIdentity")
            }
        }
        else {
            Write-Host "OK" -foregroundColor Green
        }
        Write-AzsReadinessLog -Message ("`nFinished Azure Identity Validation") -Type Info -Function $thisFunction -toScreen
    }
    catch {
        Write-Host "Fail " -foregroundColor Red
        $errorDetail = ("Checking Azure Identity failed with: {0}" -f $_.exception.Message)
        Write-Host "Error Details for Identity:"
        $errorDetail | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
    }
    finally {
        # Write results to readiness report
        $readinessReport.AzureValidation.AzureInstallResult = $serviceAdministratorOutput
        $readinessReport = Close-AzsReadinessCheckerJob -report $readinessReport
        Write-AzsReadinessProgress -report $readinessReport
        Write-AzsReadinessReport -report $readinessReport

        # Remove global variables
        Remove-Variable -Name OutputPath
        # Restore TLS back to default
        if ($ForceTLS12 -and $restoreSecurityProtocol) {
            Set-SecurityProtocol -securityProtocol $restoreSecurityProtocol
        }
        Write-Footer -invocation $MyInvocation
    }
}
# SIG # Begin signature block
# MIIjkQYJKoZIhvcNAQcCoIIjgjCCI34CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCybqL6SvodstPw
# Rlh9jo5ssLyekxUgEUr84Q6JNxljv6CCDYEwggX/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/BvW1taslScxMNelDNMYIVZjCCFWICAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAlKLM6r4lfM52wAAAAACUjAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgPtM8026C
# E8fjtH9M1CZn21nP2zZdy80CKVgdS6iXcAEwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQCEiIJ8tGBi1kb3xMOS7anVZhHCi8pE0SICF0Ye2Z3T
# BBlRQRHP1teZ/uqX3USYhJ3AVbd4wpmHzR6EGbICCNi/LeuZ/yudT4syTWaEYxTh
# HmpLVVVtwJkeAj5Y+m8+OBE6/YNjBwh63dYGmffDkOcrNhNJYFRnJJcmlnOqIsB/
# K/Rt6n7PwTTC+Xd8ojiPAKqyLOSUJg159AUCP4YTNqg9c4FhKgMoHN9rX9NOqEvP
# eUSa102VFwvxzDx5ZI75OY9OQE1HVPhx0QQMH8Bknj/33L4iDzwb7B5SG0hNNQYG
# 6MgTRdOM1lg9yv+7C4qkoDIn+4x2lxQntxMf0ezwNhnboYIS8DCCEuwGCisGAQQB
# gjcDAwExghLcMIIS2AYJKoZIhvcNAQcCoIISyTCCEsUCAQMxDzANBglghkgBZQME
# AgEFADCCAVQGCyqGSIb3DQEJEAEEoIIBQwSCAT8wggE7AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIM9zUUaFN12z0Wne29NVA1mWhsWIxu5qywhowvGy
# x1HFAgZhwMdj9AcYEjIwMjIwMTA1MTIxMTM1LjQ3WjAEgAIB9KCB1KSB0TCBzjEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWlj
# cm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBU
# U1MgRVNOOjYwQkMtRTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T
# dGFtcCBTZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAFaLLluRDTLbygAAAAA
# AVowDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw
# HhcNMjEwMTE0MTkwMjE2WhcNMjIwNDExMTkwMjE2WjCBzjELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJh
# dGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjYwQkMt
# RTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsL1cHFcNrScIrvQd/4aK
# Ho3FGXWYCHMUl2iTxuzfGknztMzbysR4eRkBoT4pv0aL1S9OlDfOsRbJZKkhCTLG
# /9Z/RwiEDWYk6rK7bRM3eX3pm+DNivM7+tCU+9spbv2gA7j5gWx6RAK2vMz2FChL
# kFgbA+H1DProG5LEf1DB7LA0FCyORWiKSkHGRL4RdIjOltrZp++dExfsst7Z6vJz
# 4+U9eZNI58fVY3KRzbm73OjplfSAB3iNSkHN0wuccK0TrZsvY87TRyYAmyK2qBqi
# /7eUWt93Sw8ALBMY72LKaUmVvaxq/COpKePlHMbhHEbqtTaLt61udBOjNHvc4cwY
# 5QIDAQABo4IBGzCCARcwHQYDVR0OBBYEFGRzJT/1HI+SftAGhdk5NDzA3jFnMB8G
# A1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeG
# RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rp
# bVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH
# MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3Rh
# UENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB
# BQUHAwgwDQYJKoZIhvcNAQELBQADggEBAAAAbex8WBtSLDiBYxXxU7GVsgb8IgxK
# JyIO0hmc8vzg4w3iUl5Xkt4mv4dgFyjHmu5Zmbj0rb2IGYm/pWJcy0/zWlhnUQUz
# vfTpj7MsiH+1Lnvg95awe88PRA7FDgc4zYY0+8UB1S+jzPmmBX/kT6U+7rW5QIgF
# MMRKIc743utqCpvcwRM+pEo8s0Alwo8NxqUrOeYY+WfNjo/XOin/tr3RVwEdEopD
# +FO+f/wLxjpv4y+TmRgmHrso1tVVy64FbIVIxlMcZ6cee4dWD2y8fv6Wb9X/Ahtl
# Qookk7QdCbKh3JJ4P8ksLs02wNhGkU37b10tG3HR5bJmiwmZPyopsEgwggZxMIIE
# WaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0y
# NTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RU
# ENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBE
# D/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50
# YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd
# /XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaR
# togINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB
# o4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8
# RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB
# hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO
# mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
# a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSAB
# Af8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEF
# BQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBt
# AGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Eh
# b7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7
# uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqR
# UgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9
# Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8
# +n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+
# Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh
# 2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRy
# zR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoo
# uLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx
# 16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341
# Hgi62jbb01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9w
# ZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjYw
# QkMtRTM4My0yNjM1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
# aWNloiMKAQEwBwYFKw4DAhoDFQDMgAWYvcYcdZwAliLeFobCWmUaLqCBgzCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA
# 5X+04jAiGA8yMDIyMDEwNTEwMTA0MloYDzIwMjIwMTA2MTAxMDQyWjB3MD0GCisG
# AQQBhFkKBAExLzAtMAoCBQDlf7TiAgEAMAoCAQACAiZ/AgH/MAcCAQACAhDXMAoC
# BQDlgQZiAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA
# AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEApJ2OIQP6+dEgGmll
# 9C13o/Pf4qaRdWxReHuywwSuNfELs/ruj5ryNfLpIIvIsNqpAnjysA/pF1SxFmX9
# lQS91UAHQbfNjO8IKFBfKfk9U7Fj/KvJFCLjrQERzMJcIG77OvsgCBDshWSdaaFU
# 6K6MtK5Brmwx46SD/LEpu4h6BQwxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMAITMwAAAVosuW5ENMtvKAAAAAABWjANBglghkgBZQME
# AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ
# BDEiBCBATS0pJgK1kXKYo7Gdpq+blLZFFwfI2JXeOEF6Psu8TzCB+gYLKoZIhvcN
# AQkQAi8xgeowgecwgeQwgb0EIJP8qCZ0xLLkXTDDghqv1yZ/kizekzSFS4gicvlt
# sX+wMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAFa
# LLluRDTLbygAAAAAAVowIgQgUgKHCHviuq8O0FbElbX1EMYcmf7GuYY36/EfMfsH
# s2cwDQYJKoZIhvcNAQELBQAEggEABA4lKk7mwplS5/5VoR0rOUeFpcXDZLue+7TY
# 5SgFPJBvRFW1yPT6icnkaypUXQ7WcyakYNsrpJrRIt1p0SWK89+PV50rjzqABdVx
# 5aRh6bbpFDkwCZ13KhEUwDdaRg0tJUKhB/DLvXwXV1lLQRuXXByvG+1379QTQ2aJ
# RneyltaANSnshs3KjxloDggK+YdBl2ueSbFx/eVVM6zmdZX+sdvC5rNPcdu60NWI
# QC6tiyPEA8BW/HO62rnVPNwZaoTwKwvPIuSvutMjftyNIqBFwElkUYyN7l4CCMjI
# LIYA23NDfuTgNgfzD7QC7wdX5qGZpvpcrfMFc3VvmMfoP8K2vw==
# SIG # End signature block