AzureValidation/Microsoft.AzureStack.AzureValidation.psm1

#Requires -RunAsAdministrator

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


<#
 
.SYNOPSIS
This script is intended to be given to the customer along with their deploymentdata.json
in order to validate the Azure Active Directory assets required for Azure Stack deployment.
 
.DESCRIPTION
The script checks the following:
    AADServiceAdministrator is correctly provisioned and matches deploymentdata.json for install
    Registration Account is correct type and owners of the subscriptionID provided.
     
.EXAMPLE
#Install Checker
$serviceAdminCredential = Get-Credential (serviceadmin@contoso.onmicrosoft.com) -Message "Enter Credentials for Service Administrator of Azure Active Directory Tenant"
.\AADHelper.ps1 -AADServiceAdministrator $serviceAdminCredential -deploymentDataJSONPath .\deploymentData.json
 
#Registration Checker
$registrationCredential = Get-Credential (subscriptionowner@contoso.onmicrosoft.com) -Message "Enter Credentials for Subscription Owner"
$subscriptionID = "f7c26209-cd2d-4625-86ba-724ebeece794"
.\AADHelper.ps1 -RegistrationAccount $registrationCredential -RegistrationSubscriptionID $subscriptionID -deploymentDataJSONPath .\deploymentData.json
 
 
.NOTES
 
 
.LINK
 
#>

function Invoke-AzureStackAzureValidation
{
    [OutputType([Array])]
    [CmdletBinding()]
    Param(
            [Parameter(Mandatory=$true, ParameterSetName='Install')]
            [Parameter(Mandatory=$true, ParameterSetName='InstallJSON')]
            [pscredential] 
            $AADServiceAdministrator,

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

            [Parameter(Mandatory=$true, ParameterSetName='Install')]
            [Parameter(Mandatory=$true, ParameterSetName='Registration')]
            [ValidateSet('AzureCloud','AzureChinaCloud','AzureGermanCloud')]
            [string]
            $AzureEnvironment,

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

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

        )
    $erroractionpreference = 'stop'
    Import-LocalizedData LocalizedData -BaseDirectory $PSScriptRoot -Filename Microsoft.AzureStack.AzureValidation.Strings.psd1
    
    $null = Import-Module $PSSCriptRoot\Microsoft.AzureStack.AzureValidation.Internal.psm1 -force
    # Check Azure Module exist and prompt to install if not
    if (-not ((Get-Module AzureRM* -ListAvailable) -AND (Get-Module *AzureStack* -ListAvailable)))
    {
        Write-Log -message ($LocalizedData.MissingAzureRMPSModules) -function $thisFunction -type Error
        throw $LocalizedData.MissingAzureRMPSModules
    }

    $null = Import-Module $PSSCriptRoot\AzureADConfiguration.psm1 -Force -Global

    $thisFunction = $MyInvocation.MyCommand.Name
    $paramToString = ($PSBoundParameters.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join ';'
    Write-Log -message ('AadHelper Started with: {0}' -f $paramToString) -function $thisFunction
    Write-Log -message ('AadHelper Started with parameter set {0}' -f ($PSCmdlet.ParameterSetName -join ',')) -function $thisFunction

    if ($PSCmdlet.ParameterSetName -match 'RegistrationJSON|InstallJSON')
    {
        # Make sure JSON is good
        try
        {
            $deploymentDataJSON = Get-Content $deploymentDataJSONPath | ConvertFrom-Json
            Write-Log -message ("Deployment JSON read from {0}" -f $deploymentDataJSONPath) -function $thisFunction
        }
        catch
        { 
            if ($_.exception -like '*Invalid JSON primitive*') 
            {
                Write-Log -message ($LocalizedData.InvalidJSON -f $deploymentDataJSONPath) -function $thisFunction -type Error
                throw $LocalizedData.InvalidJSON
            }
        }
        $AzureEnvironment = $deploymentDataJSON.DeploymentData.InfraAzureEnvironment
        $AADDirectoryTenantName = $deploymentDataJSON.DeploymentData.InfraAzureDirectoryTenantName
    }

    # Check install criteria
    if ($PSCmdlet.ParameterSetName -like 'Install*')
    {
        $serviceAdministratorOutput = @{}
        $loginResult = Test-AzureRMAccountWrapper -credential $AADServiceAdministrator -AzureEnvironment $AzureEnvironment -AADDirectoryTenantName $AADDirectoryTenantName
        if ($loginResult.result -eq 'OK')
        {
            Write-Host "Checking Installation Requirements: " -nonewline
            $serviceAdministratorOutput = Test-AzsServiceAdministrator -AADServiceAdministrator $AADServiceAdministrator -AzureEnvironment $AzureEnvironment -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}
                }
            }
            else
            {
                Write-Host "OK" -foregroundColor Green
            }
        }
        $serviceAdministratorOutput.Add('AzureAccountLogon',($loginResult | Where-Object {$_.Assets.credential -eq $AADServiceAdministrator.UserName}))
        $serviceAdministratorOutput
    }

    # Check registration criteria
    if ($PSCmdlet.ParameterSetName -like 'Registration*')
    {
        try
        {
            $registrationOutput = @{}
            $loginResult = Test-AzureRMAccountWrapper -credential $RegistrationAccount -AzureEnvironment $AzureEnvironment
            if ($loginResult.result -eq 'OK')
            {
                Write-Host "Checking Registration Requirements: " -nonewline
                $tenantid = $loginResult.Assets.AADDirectoryTenantName
                $registrationSubscription = Get-AzureSubscriptionDetail -tenantid $tenantid -subscriptionid $RegistrationSubscriptionID -credential $RegistrationAccount -AzureEnvironment $AzureEnvironment
                if (-not $registrationSubscription.errordetails)
                {
                    $registrationOutput = Test-AzsRegistrationAccount -subscription $registrationSubscription.subscription -subscriptionId $RegistrationSubscriptionID
                    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}
                    }
                    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}
                }
            }
            $registrationOutput.Add('AzureAccountLogon',($loginResult | Where-Object {$_.Assets.credential -eq $RegistrationAccount.UserName}))
            $registrationOutput.Add('SubscriptionDetail',$registrationSubscription)
            $registrationOutput
        }
        catch
        {
            $errorDetail = ("Checking Registration failed with: {0}" -f $_.exception)
            Write-Host ("Error Details for registration account {0}:" -f $registrationAccount.Username)
            $errorDetail | ForEach-Object {Write-Host "$_" -foregroundcolor Yellow}
        }
    }
}
# SIG # Begin signature block
# MIIdqAYJKoZIhvcNAQcCoIIdmTCCHZUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU+vSyAtSvUpDS8uZISrcU57K1
# q3ygghhUMIIEwjCCA6qgAwIBAgITMwAAAMDeLD0HlORJeQAAAAAAwDANBgkqhkiG
# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw
# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTYwOTA3MTc1ODUw
# WhcNMTgwOTA3MTc1ODUwWjCBsjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIERTRSBFU046
# N0FCNS0yREYyLURBM0YxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDoiKVSfklVAB4E
# Oc9+r95kl32muXNITYcTbaRtuJl+MQzEnD0eU2JUXx2mI06ONnTfFW39ZQPF1pvU
# WkHBrS6m8oKy7Em4Ol91RJ5Knwy1VvY2Tawqh+VxwdARRgOeFtFm0S+Pa+BrXtVU
# hTtGl0BGMsKGEQKdDNGJD259Iq47qPLw3CmllE3/YFw1GGoJ9C3ry+I7ntxIjJYB
# LXA122vw93OOD/zWFh1SVq2AejPxcjKtHH2hjoeTKwkFeMNtIekrUSvhbuCGxW5r
# 54KW0Yus4o8392l9Vz8lSEn2j/TgPTqD6EZlzkpw54VSwede/vyqgZIrRbat0bAh
# b8doY8vDAgMBAAGjggEJMIIBBTAdBgNVHQ4EFgQUFf5K2jOJ0xmF1WRZxNxTQRBP
# tzUwHwYDVR0jBBgwFoAUIzT42VJGcArtQPt2+7MrsMM1sw8wVAYDVR0fBE0wSzBJ
# oEegRYZDaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljcm9zb2Z0VGltZVN0YW1wUENBLmNybDBYBggrBgEFBQcBAQRMMEowSAYIKwYB
# BQUHMAKGPGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z
# b2Z0VGltZVN0YW1wUENBLmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAQEAGeJAuzJMR+kovMi8RK/LtfrKazWlR5Lx02hM9GFmMk1zWCSc
# pfVY6xqfzWFllCFHBtOaJZqLiV97jfNCLpG0PULz24CWSkG7jJ+mZaCSicZ7ZC3b
# WDh1zpc54llYVyyTkRVYx/mtc9GujqbS8CBZgjaT/JsECnvGAPUcLYuSGt53CU1b
# UuiNwuzAhai4glcYyq3/7qMmmAtbnbCZhR5ySoMy7BwdzN70drLtafCJQncfAHXV
# O5r6SX4U/2J2zvWhA8lqhZu9SRulFGRvf81VTf+k5rJ2TjL6dYtSchooJ5YVvUk6
# i7bfV0VBN8xpaUhk8jbBnxhDPKIvDvnZlhPuJjCCBgEwggPpoAMCAQICEzMAAADE
# 6Yn4eoFQ6f8AAAAAAMQwDQYJKoZIhvcNAQELBQAwfjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2ln
# bmluZyBQQ0EgMjAxMTAeFw0xNzA4MTEyMDIwMjRaFw0xODA4MTEyMDIwMjRaMHQx
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xHjAcBgNVBAMTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAIiKuCTDB4+agHkV/CZg/HKILPr0o5eIlka3o8tfiS86My4ekXj6fKkfggG1
# essavAPKRuvFmff7BB3yhQr/Im6h8mc9xScY5Sgf9QSUQWPs47oVjO0TmjXeOHBU
# bzvsrUUJMEnBvo8wmQzLdsn3c5UWd9GLu5THCIUg7R6oNfFxwuB0AEuK0tyR69Z4
# /o36rWCIPb25H65il7/FhLGQrtavK9NU+zXazXGS5h7/7HFry38IdnTgEFFI1PEA
# yEhMowc15VkN/XycyOZa44X11poPH46m5IQXwdbKnx0Bx/1IpxOSM5chSDL4wiSi
# ALK+U8qDbilbge84boDzu+wTC+sCAwEAAaOCAYAwggF8MB8GA1UdJQQYMBYGCisG
# AQQBgjdMCAEGCCsGAQUFBwMDMB0GA1UdDgQWBBTL1mKEz2A56v9nwlzSyLurt8MT
# mDBSBgNVHREESzBJpEcwRTENMAsGA1UECxMETU9QUjE0MDIGA1UEBRMrMjMwMDEy
# K2M4MDRiNWVhLTQ5YjQtNDIzOC04MzYyLWQ4NTFmYTIyNTRmYzAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AAYWH9tXwlDII0+iUXjX7fj9zb3VwPH5G1btU8hpRwXVxMvs4vyZW5VfETgowAVF
# E+CaeYi8Zqvbu+sCVSO3PSN4QW2u+PEAWpSZihzMCZXQmhxEMKmlFse6R1v1KzSL
# n49YN8NOHK8iyhDN2IIQqTXwriLIjySmgYvfJxzkZh2JPi7/VwNNwW6DoDLrtLMv
# UFZdBrEVjMgdY7dzDOPWeiYPKpZFpzKDPpY+V0l3I4n+sRDHiuUIFVHFK1oxWzlq
# lqikiGuWKG/xxK7qvUUXzGJOgbVUGkeOmKVtwG4nxvgnH8jtIKkLsfHOC5qU4mqd
# aYOhNtdtIP6F1f/DuJc2Cf49FMGYFKnAhszvgsGrVSRDGLVIhXiG0PnSnT8Z2RSJ
# 542faCSIaDupx4BOJucIIUxj/ZyTFU0ztVZgT9dKuTiO/y7dsV+kQ2vJeM+xu2uP
# g2yHcqrqpfuf3RrWOfxkyW0+COV8g7GtvKO6e8+WVqR6WMsSR2LSIe/8PMQxC/cv
# PmSlN29gUD+3RJBPoAuLvn5Y9sdnh2HbnpjEyIzLb0fhwC6U7bH2sDBt7GpJqOmW
# dsi9CMT+O/WuczcGslbPGdS79ZTKhxzygGoBT7YbgXOz01siPzpYGN+I7mfESacv
# 3CWLPV7Q7DREkR28kQx2gj7vxNgtoQQCjkj5790CzwOiMIIGBzCCA++gAwIBAgIK
# YRZoNAAAAAAAHDANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZImiZPyLGQBGRYDY29t
# MRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcwNDAzMTI1MzA5WhcNMjEw
# NDAzMTMwMzA5WjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSEwHwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQCfoWyx39tIkip8ay4Z4b3i48WZUSNQrc7dGE4k
# D+7Rp9FMrXQwIBHrB9VUlRVJlBtCkq6YXDAm2gBr6Hu97IkHD/cOBJjwicwfyzMk
# h53y9GccLPx754gd6udOo6HBI1PKjfpFzwnQXq/QsEIEovmmbJNn1yjcRlOwhtDl
# KEYuJ6yGT1VSDOQDLPtqkJAwbofzWTCd+n7Wl7PoIZd++NIT8wi3U21StEWQn0gA
# SkdmEScpZqiX5NMGgUqi+YSnEUcUCYKfhO1VeP4Bmh1QCIUAEDBG7bfeI0a7xC1U
# n68eeEExd8yb3zuDk6FhArUdDbH895uyAc4iS1T/+QXDwiALAgMBAAGjggGrMIIB
# pzAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQjNPjZUkZwCu1A+3b7syuwwzWz
# DzALBgNVHQ8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwgZgGA1UdIwSBkDCBjYAU
# DqyCYEBWJ5flJRP8KuEKU5VZ5KShY6RhMF8xEzARBgoJkiaJk/IsZAEZFgNjb20x
# GTAXBgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBS
# b290IENlcnRpZmljYXRlIEF1dGhvcml0eYIQea0WoUqgpa1Mc1j0BxMuZTBQBgNV
# HR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9w
# cm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEESDBGMEQG
# CCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01p
# Y3Jvc29mdFJvb3RDZXJ0LmNydDATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG
# 9w0BAQUFAAOCAgEAEJeKw1wDRDbd6bStd9vOeVFNAbEudHFbbQwTq86+e4+4LtQS
# ooxtYrhXAstOIBNQmd16QOJXu69YmhzhHQGGrLt48ovQ7DsB7uK+jwoFyI1I4vBT
# Fd1Pq5Lk541q1YDB5pTyBi+FA+mRKiQicPv2/OR4mS4N9wficLwYTp2Oawpylbih
# OZxnLcVRDupiXD8WmIsgP+IHGjL5zDFKdjE9K3ILyOpwPf+FChPfwgphjvDXuBfr
# Tot/xTUrXqO/67x9C0J71FNyIe4wyrt4ZVxbARcKFA7S2hSY9Ty5ZlizLS/n+YWG
# zFFW6J1wlGysOUzU9nm/qhh6YinvopspNAZ3GmLJPR5tH4LwC8csu89Ds+X57H21
# 46SodDW4TsVxIxImdgs8UoxxWkZDFLyzs7BNZ8ifQv+AeSGAnhUwZuhCEl4ayJ4i
# IdBD6Svpu/RIzCzU2DKATCYqSCRfWupW76bemZ3KOm+9gSd0BhHudiG/m4LBJ1S2
# sWo9iaF2YbRuoROmv6pH8BJv/YoybLL+31HIjCPJZr2dHYcSZAI9La9Zj7jkIeW1
# sMpjtHhUBdRBLlCslLCleKuzoJZ1GtmShxN1Ii8yqAhuoFuMJb+g74TKIdbrHk/J
# mu5J4PcBZW+JC33Iacjmbuqnl84xKf8OxVtc2E0bodj6L54/LlUWa8kTo/0wggd6
# MIIFYqADAgECAgphDpDSAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQg
# Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDla
# Fw0yNjA3MDgyMTA5MDlaMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n
# dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y
# YXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS6
# 8rZYIZ9CGypr6VpQqrgGOBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15
# ZId+lGAkbK+eSZzpaF7S35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+er
# CFDPs0S3XdjELgN1q2jzy23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVc
# eaVJKecNvqATd76UPe/74ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGM
# XeiJT4Qa8qEvWeSQOy2uM1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/
# U7qcD60ZI4TL9LoDho33X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwj
# p6lm7GEfauEoSZ1fiOIlXdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwC
# gl/bwBWzvRvUVUvnOaEP6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1J
# MKerjt/sW5+v/N2wZuLBl4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3co
# KPHtbcMojyyPQDdPweGFRInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfe
# nk70lrC8RqBsmNLg1oiMCwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAw
# HQYDVR0OBBYEFEhuZOVQBdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoA
# UwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQY
# MBaAFHItOgIxkEO5FAVO4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6
# Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1
# dDIwMTFfMjAxMV8wM18yMi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAC
# hkJodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1
# dDIwMTFfMjAxMV8wM18yMi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4D
# MIGDMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2RvY3MvcHJpbWFyeWNwcy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBs
# AF8AcABvAGwAaQBjAHkAXwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcN
# AQELBQADggIBAGfyhqWY4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjD
# ctFtg/6+P+gKyju/R6mj82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw
# /WvjPgcuKZvmPRul1LUdd5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkF
# DJvtaPpoLpWgKj8qa1hJYx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3z
# Dq+ZKJeYTQ49C/IIidYfwzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEn
# Gn+x9Cf43iw6IGmYslmJaG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1F
# p3blQCplo8NdUmKGwx1jNpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0Qax
# dR8UvmFhtfDcxhsEvt9Bxw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AAp
# xbGbpT9Fdx41xtKiop96eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//W
# syNodeav+vyL6wuA6mk7r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqx
# P/uozKRdwaGIm1dxVk5IRcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIEvjCC
# BLoCAQEwgZUwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
# BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo
# MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAMTp
# ifh6gVDp/wAAAAAAxDAJBgUrDgMCGgUAoIHSMBkGCSqGSIb3DQEJAzEMBgorBgEE
# AYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCMGCSqGSIb3DQEJ
# BDEWBBT4vmRAIS/8IDranoAH3YuKiLOmITByBgorBgEEAYI3AgEMMWQwYqBIgEYA
# TQBpAGMAcgBvAHMAbwBmAHQAIABBAHoAdQByAGUAUwB0AGEAYwBrACAAUABhAHIA
# dABuAGUAcgBUAG8AbwBsAGsAaQB0oRaAFGh0dHA6Ly9Db2RlU2lnbkluZm8gMA0G
# CSqGSIb3DQEBAQUABIIBADE4AW6qIVSEkK9OtkJ8y6zUAbnMBqglWCptGdLAbhfB
# ZcAY//X0EOoxeSP7UZS+hvD/EjQEt8655CSP4/7f0AGAxKFKVFHTwWtyfYWFrW/n
# m/pBdsArXEXWlZe4VwynXwV+9NLilx5fyLleZ7SPb1kH3NEa45DEnTYXDU6F+Yv/
# qLhGrymQYQYcxGsc40iAGQbYQ81NV9ZsWjhK07uWzRJXDltuu76G5dk4KxTjlBlA
# 1NrcBlydfiEnmNs/SasyPauQkaiGKcZNckQewHr/ckmPwq9hFkFOAZSb7Y2eMFGc
# yS7Akr8r8jL2UMAGkmdYyY3rF14JyiBx2pmVgPj5sA+hggIoMIICJAYJKoZIhvcN
# AQkGMYICFTCCAhECAQEwgY4wdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEhMB8GA1UEAxMYTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBAhMzAAAA
# wN4sPQeU5El5AAAAAADAMAkGBSsOAwIaBQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZI
# hvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xODA0MjcwOTI5MzBaMCMGCSqGSIb3DQEJ
# BDEWBBS0OtVD2uSyXUwX8eJO+3+rE7oC3jANBgkqhkiG9w0BAQUFAASCAQBsvz12
# L9yQG7UkF4fGsbxl1HMS+DP0gFFI9sZ4SdQlTS/E0CNXTHuKd1qht+uR6UPHUmPm
# zDNvOtN6pSLOQ4Ew/fApNKHNOwZdhWaiNKG4R16VVNwYKo5cMMre/kBrjQ5qyqWx
# gI+ZyOZeXVmC9TUmNIwvk7GWQ7ZCTDayTJAQDK31gtbbsCNN3VoTbu7WTymlyqfp
# /NooSXqUNaYZW1FSEQV3ZH2RZcEi2t2klHwAC3ZBkEyjhsSo8u9SXx6g7y02dLOg
# EtndbAG4o0IpCj7YmJPK6hbBbC8LWZ85wPNkdNUZEnOu9KVv9N1mW0JYEXMmpJYV
# mayyeohs75serHFM
# SIG # End signature block