AzureValidation/Microsoft.AzureStack.AzureValidation.psm1

#Requires -RunAsAdministrator

$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:\> Invoke-AzsRegistrationValidation -RegistrationAccount $credential -RegistrationSubscriptionID 3f764e1c-d0ab-49e3-99aa-44124d56dd2d -AzureEnvironment AzureCloud
        Validates Credential and Subscription for Azure Stack Registration.
    .EXAMPLE
        PS C:\> Invoke-AzsRegistrationValidation -RegistrationAccount $credential -RegistrationSubscriptionID 3f764e1c-d0ab-49e3-99aa-44124d56dd2d -AzureEnvironment CustomCloud -CustomCloudARMEndpoint 'https://management.azurecloud.contoso.com'
        Validates Credential and Subscription for Azure Stack Registration against custom azure cloud.
    .PARAMETER RegistrationAccount
        Specifies the Registration Account intended to be used for Azure Stack registration.
    .PARAMETER RegistrationSubscriptionID
        Specifies the Registration Subscription ID intended to be used for Azure Stack registration.
    .PARAMETER AzureEnvironment
        Specifies the instance of Azure Services containing the accounts, directories and subscriptions intended to be used for Azure Stack deployment and registration.
    .PARAMETER CustomCloudARMEndpoint
        Specifies the custom ARM Endpoint for a custom Azure Environment.
    .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(Mandatory = $false, ParameterSetName = 'Registration')]
        [ValidateSet('AzureCloud', 'AzureChinaCloud', 'AzureGermanCloud', 'AzureUSGovernment','CustomCloud')]
        [string]
        $AzureEnvironment = 'AzureCloud',

        [Parameter(Mandatory = $false, ParameterSetName = 'Registration')]
        [Parameter(Mandatory = $false, ParameterSetName = 'RegistrationJSON')]
        [string]
        $CustomCloudARMEndpoint,

        [Parameter(Mandatory = $true, ParameterSetName = 'RegistrationJSON')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Registration')]
        [pscredential] 
        $RegistrationAccount,

        [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
        
        # Check Azure Module exist and prompt to install if not
        if (-not (Get-Module AzureRM.Profile -ListAvailable)) {
            Write-AzsReadinessLog -message ($LocalizedData.MissingAzureRMPSModules) -function $thisFunction -type Error
            throw $LocalizedData.MissingAzureRMPSModules
        }
        Import-Module $PSSCriptRoot\AzureADConfiguration.psm1 -Force -Global

        Write-Header -invocation $MyInvocation -params $PSBoundParameters

        # 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
        if ($CustomCloudARMEndpoint) {
            Write-AzsReadinessLog -message ($LocalizedData.UserSpecifiedCustomAzureEnvironment -f $CustomCloudARMEndpoint) -function $thisFunction -type Info
            $AzureEnvironment = 'CustomCloud'
        }

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

        # 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-RegistrationTenantId -Credential $RegistrationAccount -Subscriptionid $RegistrationSubscriptionID -AzureEnvironment $AzureEnvironment -CustomCloudARMEndpoint $CustomCloudARMEndpoint
        if ($null -eq $tenantid.tenantid) {
            throw "{0}" -f $tenantid.errorDetails
        }
        else {
            $registrationSubscription = Get-AzureSubscriptionDetail -tenantid $tenantid.tenantid -subscriptionid $RegistrationSubscriptionID -credential $RegistrationAccount -AzureEnvironment $AzureEnvironment -CustomCloudARMEndpoint $CustomCloudARMEndpoint
        }

        ## expand test-azsregostrationaccount to test graph endpoint
        if (-not $registrationSubscription.errordetails) {
            $registrationOutput = Test-AzsRegistrationAccount -subscription $registrationSubscription.subscription -subscriptionId $RegistrationSubscriptionID -tenantId $tenantid.tenantid -Credential $RegistrationAccount -AzureEnvironment $AzureEnvironment -CustomCloudARMEndpoint $CustomCloudARMEndpoint
            if ($registrationOutput.result -ne 'OK') {
                Write-Host "Fail " -foregroundColor Red
                Write-Host ("Error Details for registration account {0}:" -f $registrationAccount.Username)
                $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 $registrationAccount.Username)
            $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 $registrationAccount.Username)
        $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) {
            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:\> Invoke-AzsAzureIdentityValidation -AADServiceAdministrator $credential -AzureEnvironment AzureCloud -AADDirectoryTenantName contoso.onmicrosoft.com
        Validates Service Administrator and AAD Tenant for Azure Stack Identity
    .EXAMPLE
        PS C:\> Invoke-AzsAzureIdentityValidation -AADServiceAdministrator $credential -AzureEnvironment CustomCloud -CustomCloudARMEndpoint 'https://management.azurecloud.contoso.com' -AADDirectoryTenantName contoso.onmicrosoft.com
        Validates Service Administrator and AAD Tenant for Azure Stack Identity
    .PARAMETER AADServiceAdministrator
        Specifies Azure Active Directory Service Administrator intended to be used for Azure Stack deployment.
    .PARAMETER AADDirectoryTenantName
        Specifies the Azure Active Directory name intended to be used for Azure Stack deployment.
    .PARAMETER AzureEnvironment
        Specifies the instance of Azure Services containing the accounts, directories and subscriptions.
    .PARAMETER CustomCloudARMEndpoint
        Specifies the custom ARM Endpoint for a custom Azure Environment.
    .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 = 'Install')]
        [Parameter(Mandatory = $true, ParameterSetName = 'InstallJSON')]
        [pscredential] 
        $AADServiceAdministrator,

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

        [Parameter(Mandatory = $false, ParameterSetName = 'Install')]
        [ValidateSet('AzureCloud', 'AzureChinaCloud', 'AzureGermanCloud', 'AzureUSGovernment', 'CustomCloud')]
        [string]
        $AzureEnvironment = 'AzureCloud',

        [Parameter(Mandatory = $false, ParameterSetName = 'Install')]
        [Parameter(Mandatory = $false, ParameterSetName = 'InstallJSON')]
        [string]
        $CustomCloudARMEndpoint,

        [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

        # Check Azure Module exist and prompt to install if not
        if (-not (Get-Module AzureRM.Profile -ListAvailable)) {
            Write-AzsReadinessLog -message ($LocalizedData.MissingAzureRMPSModules) -function $thisFunction -type Error
            throw $LocalizedData.MissingAzureRMPSModules
        }
        Import-Module $PSSCriptRoot\AzureADConfiguration.psm1 -Force -Global

        Write-Header -invocation $MyInvocation -params $PSBoundParameters

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

        # If user specifies custom azure environment, overwrite the AzureEnvironment as the json path
        if ($CustomCloudARMEndpoint) {
            Write-AzsReadinessLog -message ($LocalizedData.UserSpecifiedCustomAzureEnvironment -f $CustomCloudARMEndpoint) -function $thisFunction -type Info
            $AzureEnvironment = 'CustomCloud'
        }
        
        # Get/Clean Existing Report
        $readinessReport = Get-AzsReadinessProgress -clean:$CleanReport
        $readinessReport = Add-AzsReadinessCheckerJob -report $readinessReport
        $serviceAdministratorOutput = @{}

        # 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 -AADServiceAdministrator $AADServiceAdministrator -AzureEnvironment $AzureEnvironment -CustomCloudARMEndpoint $CustomCloudARMEndpoint -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) {
            Set-SecurityProtocol -securityProtocol $restoreSecurityProtocol
        }
        Write-Footer -invocation $MyInvocation
    }
}
# SIG # Begin signature block
# MIIjhgYJKoZIhvcNAQcCoIIjdzCCI3MCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAlsy3dCQDUqt2y
# BDcgRe9ugTFKkFv70rBupeVkyKOLqKCCDYUwggYDMIID66ADAgECAhMzAAABiK9S
# 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0
# t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs
# 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd
# vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv
# V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W
# MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q
# qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X
# 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P
# mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM
# i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT
# GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz
# LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM
# SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa
# 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV
# Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+
# r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK
# 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/Xmfwb1tbWrJUnMTDXpQzTGCFVcwghVTAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA
# AYgwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIH+K
# E5PVrUuzEAX2vy/uG35KcIBKuH5ix7QIloMsEflnMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEADhjDPpr6A55ZumAuLJr6wPm+FsuCBd6zcgSa
# ZZfJrEsNFBlHp3Z6psefBTW+ePyvG3j4ywPK2Bw+VCDASMvXuaIVBNn1whRZUsK8
# AXjaddDF3HnDu0xTf98EFE8Jl4+1p3dQdBK19AeDb1sLClQXYWmNkzA1qjDDy9XL
# uqLVQovqn6fctolHxpJ4rKFTsefT7B/yQx2MqXChFYY4bI3D0eWRlB9dlEeM+IWf
# 39Xvi605fXhcKCQfn9Gjt7A4wftEKVnkZQ5SB+2Hd1TPwI6kA98GVoHK2a52a7Nk
# 8xR8zS3SZWvKWdzll8ahPPQzfijyKbv4lV4VZ+dZTxIynTFl/6GCEuEwghLdBgor
# BgEEAYI3AwMBMYISzTCCEskGCSqGSIb3DQEHAqCCErowghK2AgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCBqHK4r8iSbSXOTKRrEuguz0jZFmSJuJeCM
# etsjWIh9XQIGXz0t4NECGBMyMDIwMDgyNzEwMDgwMC40MDJaMASAAgH0oIHQpIHN
# MIHKMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9z
# b2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjoxNzlFLTRCQjAtODI0NjElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDjgwggTxMIID2aADAgECAhMzAAABDKp4btzMQkzBAAAA
# AAEMMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTAyMzIzMTkxNloXDTIxMDEyMTIzMTkxNlowgcoxCzAJBgNVBAYTAlVT
# MQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVy
# YXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjE3OUUtNEJC
# MC04MjQ2MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq5011+XqVJmQKtiw39igeEMv
# CLcZ1forbmxsDkpnCN1SrThKI+n2Pr3zqTzJVgdJFCoKm1ks1gtRJ7HaL6tDkrOw
# 8XJmfJaxyQAluCQ+e40NI+A4w+u59Gy89AVY5lJNrmCva6gozfg1kxw6abV5WWr+
# PjEpNCshO4hxv3UqgMcCKnT2YVSZzF1Gy7APub1fY0P1vNEuOFKrNCEEvWIKRrqs
# eyBB73G8KD2yw6jfz0VKxNSRAdhJV/ghOyrDt5a+L6C3m1rpr8sqiof3iohv3ANI
# gNqw6ex+4+G+B7JMbIHbGpPdebedL6ePbuBCnbgJoDn340k0aw6ij21GvvUnkQID
# AQABo4IBGzCCARcwHQYDVR0OBBYEFAlCOq9DDIa0A0oqgKtM5vjuZeK+MB8GA1Ud
# IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB
# XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH
# AwgwDQYJKoZIhvcNAQELBQADggEBAET3xBg/IZ9zdOfwbDGK7cK3qKYt/qUOlbRB
# zgeNjb32K86nGeRGkBee10dVOEGWUw6KtBeWh1LQ70b64/tLtiLcsf9JzaAyDYb1
# sRmMi5fjRZ753TquaT8V7NJ7RfEuYfvZlubfQD0MVbU4tzsdZdYuxE37V2J9pN89
# j7GoFNtAnSnCn1MRxENAILgt9XzeQzTEDhFYW0N2DNphTkRPXGjpDmwi6WtkJ5fv
# 0iTyB4dwEC+/ed0lGbFLcytJoMwfTNMdH6gcnHlMzsniornGFZa5PPiV78XoZ9Fe
# upKo8ZKNGhLLLB5GTtqfHex5no3ioVSq+NthvhX0I/V+iXJsopowggZxMIIEWaAD
# AgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBD
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3
# MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWl
# CgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/Fg
# iIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeR
# X4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/Xcf
# PfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogI
# Neh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB
# 5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvF
# M2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP
# BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjE
# MFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv
# Y3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEF
# BQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8E
# gZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcC
# AjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUA
# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Pr
# psz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOM
# zPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCv
# OA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v
# /rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99
# lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1kl
# D3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQ
# Hm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30
# uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp
# 25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HS
# xVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi6
# 2jbb01+P3nSISRKhggLKMIICMwIBATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMx
# CzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTc5RS00QkIw
# LTgyNDYxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
# ATAHBgUrDgMCGgMVAMsg9FQ9pgPLXI2Ld5z7xDS0QAZ9oIGDMIGApH4wfDELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDi8Y9WMCIY
# DzIwMjAwODI3MDk0ODA2WhgPMjAyMDA4MjgwOTQ4MDZaMHMwOQYKKwYBBAGEWQoE
# ATErMCkwCgIFAOLxj1YCAQAwBgIBAAIBGzAHAgEAAgIR7zAKAgUA4vLg1gIBADA2
# BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB
# AAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAB2YEG7OPGx1NmclLKu49RXnAdqDxnBG
# MHfIhAs2gwKJlZNIIN/081c4gK3Mc+04/CM547lkdqquyQDNvsKIHTrJBySEKP2e
# LGDHG5o4GICjjzp8vTkw/0ZdRUMm7kymwr+FJECm/Q9RhAYPLmi3Sbjmo/fvYNsH
# m1plw7sOcYMjMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAEMqnhu3MxCTMEAAAAAAQwwDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg6rJw2BWB
# IbD4h+IzXP5E1bi6KLg+QR98SjzOm85aemQwgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCCDkBYpfszX6bb//5XuqZG+3Ur/DDky67xfMYkGrKBUKTCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABDKp4btzMQkzBAAAA
# AAEMMCIEIMxpWbnF5LSD5/OAyvYzlBrv6DCj7+K4yH7mbtXF3vWOMA0GCSqGSIb3
# DQEBCwUABIIBAA9fPtRmpCJMifyvn/Rx0vYCbdGVZ10TwKrZFjqZO6NiiAUwqFWV
# Z/y8fRhXz/MyylJdZ3k1wtXDDXY9zfLpXmAq2pDHEZGBgpcBgYemm61GJOh55j3X
# JBeRa3wP6IlAlHEehVf8iQzn1TwSoGN6iM4Dvsre2J3V5wTCgnYcBgqezNdwPSJF
# AuFNVDBVkpcG+BcQdX14YmYAS/7x+gUhAvDrLwGhBL9FEgKc7elj6AgmABxw+4uu
# OzXZpwgkRTJkGufwNoBx/eGcBqqva+9spuAhNeFX+7sqNHQRkynRWaskqFj2OglI
# y5xZAWHpBFc8BluZ+MKoenxaZoOo84u1/Rk=
# SIG # End signature block