New-AaddsNetworkSecurityGroup.ps1

<#PSScriptInfo
 
.VERSION 1.1
 
.GUID 7732bb75-a0d5-4769-97ee-c5394b554941
 
.AUTHOR aaddsfb@microsoft.com
 
.COMPANYNAME Microsoft Corporation
 
.COPYRIGHT (c) Microsoft Corporation
 
.TAGS Azure-AD-Domain-Services NetworkSecurityGroup
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
    07/19/2020 - Initial release
#>


<#
 
.SYNOPSIS
    Creates a new Azure network security group with the correct networking rules that are compatible with Azure Active
    Directory Domain services. The network security group is created in the same resource group and location as the Azure
    AD Domain Service. The script can optionally link the network security group to the Azure AD Domain Services
    subnet.
 
.DESCRIPTION
    Creates a new Azure network security group with the correct networking rules that are compatible with Azure Active
    Directory Domain services. The network security group is created in the same resource group and location as the Azure
    AD Domain Service. The script can optionally link the network security group to the Azure AD Domain Services
    subnet.
 
.PARAMETER ManagedDomainFqdn
    The fqdn of the managed domain (aadds.cloudcontoso.com).
 
.PARAMETER SubscriptionId
    The Azure subscription that hosts the Azure AD Domain Services resource.
 
.PARAMETER LinkToAaddsSubnet
    Links the newly created network security group to the Azure AD Domain Services subnet.
 
.PARAMETER Credentials
    The credentials used to authenticate to Azure resource manager.
 
#>

[CmdletBinding()]
Param (

    [Parameter(
        Mandatory=$true)]
        [string]
        $ManagedDomainFqdn,

    [Parameter(
        Mandatory=$false)]
        [string]
        $SubscriptionId = $null,

    [Parameter(
        Mandatory=$false)]
        [switch]
        $LinkToAaddsSubnet = $false,

    [Parameter(
        Mandatory=$false)]
        [pscredential]
        $Credentials = $null
)

Process
{
    Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"

    [int] $SUBNET_NAME_INDEX = 9
    [int] $VNET_NAME_INDEX = 7

    $subscription = $null
    $azProfile = $null
    $aadds = $null


    #
    # Check for subscriptionID
    if([String]::Empty -ne $SubscriptionId)
    {
        $subscription = $SubscriptionId
    }

    Write-Host ([string]::Empty)
    Write-Host ([string]::Empty)
    Write-Host "Authenticating to Azure... " -NoNewline

    #
    # Collect credentials from the user for authentication
    if($null -eq $Credentials)
    {
        $azProfile = Connect-AzAccount -ErrorAction SilentlyContinue
    }
    else
    {
        $azProfile = Connect-AzAccount -Credential $Credentials -ErrorAction SilentlyContinue
    }

    if($null -eq $azProfile)
    {
        # Authentication failed
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not authenticate to Azure. Check your credentials and try again."
        Return
    }
    else
    {
        Write-Host -ForegroundColor Green "[Success!]"
    }

    Write-Host ("[INFO] Authenticated to Azure as {0}..." -f $azProfile.Context.Account.Id)

    #
    # Check for user provided subscription Id
    Write-Host "Searching for Azure AD Domain Services instance..." -NoNewline
    if($null -eq $subscription)
    {
        #
        # Locate Azure AD Domain Services in a subscription
        # Use the first instance found as there should only be one instance per tenant
        ($AzSubs = Get-AzSubscription) | Out-Null
        foreach($azsub in $AzSubs)
        {
            (Set-AzContext $azsub.Id) | Out-Null

            $aadds = Get-AzResource -Name $ManagedDomainFqdn -ApiVersion "2020-01-01" -ExpandProperties -ErrorAction SilentlyContinue

            if($null -ne $aadds)
            {
                # Found Azure AD Domain Services in this subscription
                # Add to the subscription List
                $subscription = $azsub.Id
                break
            }
        }
    }
    if($null -eq $subscription)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "A subscription could not be located for this user."
        Write-Host -ForegroundColor Red "Ensure the user has the contributor role to the subscription hosting Azure AD Domain Services."
        Return
    }


    #
    # Set the subscription
    Set-AzContext $subscription -ErrorAction SilentlyContinue | Out-Null

    #
    # Get the Azure AD Domain Services instance in the subscription
    $aadds = Get-AzResource -Name $ManagedDomainFqdn -ApiVersion "2020-01-01" -ExpandProperties -ErrorAction SilentlyContinue


    if($null -eq $aadds)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not find a subscription that hosts Azure AD Domain Services."
        return
    }
    else
    {
        Write-Host -ForegroundColor Green "[Found!]"
    }

    #
    # Validate the resource is Azure AD Domain Services
    Write-Host "Validating resource type..." -NoNewline
    if($aadds.ResourceType -ne "Microsoft.AAD/domainServices")
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: The resource is not an Azure AD Domain Services resource (Status:$($aadds.ResourceType))."
    }
    else {Write-Host -ForegroundColor Green "[Pass!]"}

    #
    # Get information about Azure AD Domain Services needed to create the network security group
    Write-Host ("Getting properties of from the Azure AD Domain Services instance {0}." -f $ManagedDomainFqdn)

    # Get the Azure AD Domain Services subnet ID
    Write-Host "Virtual subnet ID: " -NoNewline
    $replicaSetCount = $aadds.Properties.ReplicaSets.Count
    if($replicaSetCount -eq 1)
    {
        $aaddsSubnetId = $aadds.Properties.ReplicaSets[0].SubnetId
    }
    elseif($replicaSetCount -gt 1)
    {
        $syncOwner = $aadds.Properties.SyncOwner
        $primaryReplicaSet = $aadds.Properties.ReplicaSets | Where-Object { $_.ReplicaSetId -eq $syncOwner }
        $aaddsSubnetId = $primaryReplicaSet.SubnetId
    }
    else { $aaddsSubnetId = $null }

    if($null -eq $aaddsSubnetId)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not get the virtual subnet ID for the Azure AD Domain Services instance."
        Return
    }
    else { Write-Host -ForegroundColor Green ("{0}" -f $aaddsSubnetId) }

    # Get the Azure AD Domain Services resource group
    Write-Host "Resource Group: " -NoNewline
    $aaddsResourceGroupName = $aadds.ResourceGroupName
    if($null -eq $aaddsResourceGroupName)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not get the resource group for the Azure AD Domain Services instance."
        Return
    }
    else { Write-Host -ForegroundColor Green ("{0}" -f $aaddsResourceGroupName) }

    # Get the Azure AD Domain Services location
    Write-Host "Location: " -NoNewline
    $aaddsLocation = $aadds.Location
    if($null -eq $aaddsLocation)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not get the location for the Azure AD Domain Services instance."
        Return
    }
    else { Write-Host -ForegroundColor Green ("{0}" -f $aaddsLocation) }

    #
    # Heuristically build the
    # Azure AD Domain Services subnet name
    # Azure AD Domain Services network name
    # Azure AD Domain Services network ID
    $aaddsSubnetParts = $aaddsSubnetId.Split("/", [System.StringSplitOptions]::RemoveEmptyEntries)
    $aaddsSubnetName = $aaddsSubnetParts[$SUBNET_NAME_INDEX]
    Write-Host "Virtual subnet name: " -NoNewline
    if($null -eq $aaddsSubnetName)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not get the location for the Azure AD Domain Services instance."
        Return
    }
    else { Write-Host -ForegroundColor Green ("{0}" -f $aaddsSubnetName) }
    $aaddsVnetParts = $aaddsSubnetParts[0..7]

    $aaddsVnetName = $aaddsVnetParts[$VNET_NAME_INDEX]
    Write-Host "Virtual network name: " -NoNewline
    if($null -eq $aaddsVnetName)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not get the location for the Azure AD Domain Services instance."
        Return
    }
    else { Write-Host -ForegroundColor Green ("{0}" -f $aaddsVnetName) }

    # Get the Azure AD Domain Services virutal network
    Write-Host ("Getting virtual network information for {0}..." -f $aaddsVnetName) -NoNewline
    $aaddsVirtualNetwork = Get-AzVirtualNetwork -Name $aaddsVnetName `
        -ResourceGroupName $aaddsResourceGroupName -ErrorAction SilentlyContinue
    if($null -eq $aaddsVirtualNetwork)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red ("ERROR: Could not get the virtual network information for {0} from Azure ." -f $aaddsVnetName)
        Return
    }
    else { Write-Host -ForegroundColor Green "[Success!]" }

    # Get the Azure AD Domain Services virtual subnet
    Write-Host ("Getting virtual subnet information for {0}..." -f $aaddsSubnetName) -NoNewline
    $aaddsSubnet = Get-AzVirtualNetworkSubnetConfig -ResourceId $aaddsSubnetId
    if($null -eq $aaddsSubnet)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red ("ERROR: Could not get the virtual subnet information for {0} from Azure ." -f $aaddsSubnetName)
        Return
    }
    else { Write-Host -ForegroundColor Green "[Success!]" }

    # Create the default Azure AD Domain Services NSG with rules and link it to Azure AD Domain Services Subnet
    Write-Host "Creating network security group network security group in the $aaddsResourceGroupName resource group..."

    # Build the rules
    Write-Host "Creating Azure AD Domain Services network security group rules..." -NoNewline
    $nsg101 = New-AzNetworkSecurityRuleConfig -Name AllowSyncWithAzureAD `
        -Access Allow -Protocol Tcp -Direction Inbound -Priority 101 `
        -SourceAddressPrefix AzureActiveDirectoryDomainServices `
        -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443

    $nsg201 = New-AzNetworkSecurityRuleConfig -Name AllowRD `
        -Access Allow -Protocol Tcp -Direction Inbound -Priority 201 `
        -SourceAddressPrefix CorpNetSaw `
        -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 3389

    $nsg301 = New-AzNetworkSecurityRuleConfig -Name AllowPSRemoting `
        -Access Allow -Protocol Tcp -Direction Inbound -Priority 301 `
        -SourceAddressPrefix AzureActiveDirectoryDomainServices `
        -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 5986

    if( ($null -eq $nsg101) -or ($null -eq $nsg201) -or ($null -eq $nsg301))
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not create inbound network Azure AD Domain Services network security group rules."
        Return
    }
    else { Write-Host -ForegroundColor Green "[Success!]" }

    #
    # Find an available name for the NSG
    Write-Host "Automatically generating a name for the network security group..." -NoNewline
    $nsgNamePrefix = "aadds-nsg-"
    $nsgName = [String]::Empty
    $rv = $true
    for($inc = 1; $rv -eq $true; $inc++)
    {
        $nsgName = [String]::Concat($nsgNamePrefix,$inc)
        $tempNsg = Get-AzNetworkSecurityGroup -Name  $nsgName -ResourceGroupName $aaddsResourceGroupName -ErrorAction SilentlyContinue
        if($null -eq $tempNsg)
        {
            break
        }

        if($inc -gt 50)
        {
            Write-Host -ForegroundColor Red "[Failed!]"
            Write-Host -ForegroundColor Red "ERROR: There are more than 50 network security groups that begin with aadds-nsg-."
            Write-Host -ForegroundColor Red "ERROR: Remove the unused network security groups and run the script again."
            Return
        }
        $nsgName = $null
        Write-Host "." -NoNewline
    }

    if($null -eq $nsgName)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: There was a problem automatically generating a network security group name."
        Return
    }
    else {Write-Host -ForegroundColor Green "[Success!]" }

    #
    # Save the network security group
    Write-Host "Saving Azure AD Domain Services network security group..." -NoNewline
    $nsg = New-AzNetworkSecurityGroup -Name $nsgName -ResourceGroupName $aaddsResourceGroupName `
        -Location $aaddsLocation -SecurityRules $nsg101,$nsg201,$nsg301

    if($null -eq $nsg)
    {
        Write-Host -ForegroundColor Red "[Failed!]"
        Write-Host -ForegroundColor Red "ERROR: Could not create a network security group in the $aaddsResourceGroupName resource group."
        Return
    }
    else {Write-Host -ForegroundColor Green "[Success!]"}

    if($true -eq $LinkToAaddsSubnet)
    {
        #
        # Link the NSG to the subnet
        Write-Host ("Linking the network security group {0} to the virtual subnet {1}..." -f $nsgName, $aaddsSubnetName) -NoNewline
        $addressPrefix = $aaddsSubnet.AddressPrefix
        $aaddsSubnet = $null
        $aaddsSubnet = Set-AzVirtualNetworkSubnetConfig -VirtualNetwork $aaddsVirtualNetwork `
            -AddressPrefix $addressPrefix `
            -Name $aaddsSubnetName `
            -NetworkSecurityGroup $nsg -ErrorAction SilentlyContinue
        if($null -eq $aaddsSubnet)
        {
            Write-Host -ForegroundColor Red "[Failed!]"
            Write-Host -ForegroundColor Red "ERROR: Could not update the the subnet with the new network security group."
            Return
        }
        else {Write-Host -ForegroundColor Green "[Success!]"}

        #
        # Commit the changes to the virtual network
        Write-Host "Saving changes to the subnet and virtual network..." -NoNewline
        $rv = Set-AzVirtualNetwork -VirtualNetwork $aaddsVirtualNetwork -ErrorAction SilentlyContinue
        if($null -eq $rv)
        {
            Write-Host -ForegroundColor Red "[Failed!]"
            Write-Host -ForegroundColor Red "ERROR: Could not save the changes to the virtual network."
            Return
        }
        else {Write-Host -ForegroundColor Green "[Success!]"}
    }

    Write-Host ([string]::Empty)
    Write-Host ([string]::Empty)

    Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "false"
}
# SIG # Begin signature block
# MIInMwYJKoZIhvcNAQcCoIInJDCCJyACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBF+AX/uUN/brx0
# yNR43NAInOFGDBnfkDgCECbjgluBeKCCEW8wggiBMIIHaaADAgECAhM2AAABDBla
# ELMo09izAAEAAAEMMA0GCSqGSIb3DQEBCwUAMEExEzARBgoJkiaJk/IsZAEZFgNH
# QkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxFTATBgNVBAMTDEFNRSBDUyBDQSAwMTAe
# Fw0yMDAyMDkxMzI1MDFaFw0yMTAyMDgxMzI1MDFaMC8xLTArBgNVBAMTJE1pY3Jv
# c29mdCBBenVyZSBEZXBlbmRlbmN5IENvZGUgU2lnbjCCASIwDQYJKoZIhvcNAQEB
# BQADggEPADCCAQoCggEBAJL4kx4D2erD4cliqomE3dMX+gvfMz/ovrjRwJqG80Kl
# kGP+kOn35E80o/Ua/SdfQq3gjLNJSJpa6Yn0ph8FOf7U4NT7a8+zrwBTpZ/7llv9
# /jGf037eKxEWsCtMTRfL1dKBOQhn1lHAZvjKdgIgJAFG7ydg1oKsn0wfGBXSgile
# g1IWbTNpR5luLpuHPWRspqDtXCXif/+rjukP5tvDqZmxYP0tQXER4I1eUXiJIXHf
# 7dFZR7VxjZ4BP1rEUU8Gk+BMGpTJTTB21MjwtEjF2U5WAv1KeUpxxlYPKEYGgr2/
# lCXgkoWmPWqSLMbLjcX5uLfMP9j/IW/UnpoaReR1gVsCAwEAAaOCBYIwggV+MCkG
# CSsGAQQBgjcVCgQcMBowDAYKKwYBBAGCN1sDATAKBggrBgEFBQcDAzA8BgkrBgEE
# AYI3FQcELzAtBiUrBgEEAYI3FQiGkOMNhNW0eITxiz6Fm90Wzp0SgWDigi2HkK4D
# AgFkAgEOMIICdgYIKwYBBQUHAQEEggJoMIICZDBiBggrBgEFBQcwAoZWaHR0cDov
# L2NybC5taWNyb3NvZnQuY29tL3BraWluZnJhL0NlcnRzL0JZMlBLSUNTQ0EwMS5B
# TUUuR0JMX0FNRSUyMENTJTIwQ0ElMjAwMSgxKS5jcnQwUgYIKwYBBQUHMAKGRmh0
# dHA6Ly9jcmwxLmFtZS5nYmwvYWlhL0JZMlBLSUNTQ0EwMS5BTUUuR0JMX0FNRSUy
# MENTJTIwQ0ElMjAwMSgxKS5jcnQwUgYIKwYBBQUHMAKGRmh0dHA6Ly9jcmwyLmFt
# ZS5nYmwvYWlhL0JZMlBLSUNTQ0EwMS5BTUUuR0JMX0FNRSUyMENTJTIwQ0ElMjAw
# MSgxKS5jcnQwUgYIKwYBBQUHMAKGRmh0dHA6Ly9jcmwzLmFtZS5nYmwvYWlhL0JZ
# MlBLSUNTQ0EwMS5BTUUuR0JMX0FNRSUyMENTJTIwQ0ElMjAwMSgxKS5jcnQwUgYI
# KwYBBQUHMAKGRmh0dHA6Ly9jcmw0LmFtZS5nYmwvYWlhL0JZMlBLSUNTQ0EwMS5B
# TUUuR0JMX0FNRSUyMENTJTIwQ0ElMjAwMSgxKS5jcnQwga0GCCsGAQUFBzAChoGg
# bGRhcDovLy9DTj1BTUUlMjBDUyUyMENBJTIwMDEsQ049QUlBLENOPVB1YmxpYyUy
# MEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9
# QU1FLERDPUdCTD9jQUNlcnRpZmljYXRlP2Jhc2U/b2JqZWN0Q2xhc3M9Y2VydGlm
# aWNhdGlvbkF1dGhvcml0eTAdBgNVHQ4EFgQUkku2i4tvXu/fb2UHKKZiDd81U7Aw
# DgYDVR0PAQH/BAQDAgeAMFAGA1UdEQRJMEekRTBDMSkwJwYDVQQLEyBNaWNyb3Nv
# ZnQgT3BlcmF0aW9ucyBQdWVydG8gUmljbzEWMBQGA1UEBRMNMjM2MTY5KzQ1Nzc5
# NTCCAdQGA1UdHwSCAcswggHHMIIBw6CCAb+gggG7hjxodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpaW5mcmEvQ1JML0FNRSUyMENTJTIwQ0ElMjAwMS5jcmyGLmh0
# dHA6Ly9jcmwxLmFtZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMS5jcmyGLmh0
# dHA6Ly9jcmwyLmFtZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMS5jcmyGLmh0
# dHA6Ly9jcmwzLmFtZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMS5jcmyGLmh0
# dHA6Ly9jcmw0LmFtZS5nYmwvY3JsL0FNRSUyMENTJTIwQ0ElMjAwMS5jcmyGgbps
# ZGFwOi8vL0NOPUFNRSUyMENTJTIwQ0ElMjAwMSxDTj1CWTJQS0lDU0NBMDEsQ049
# Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENOPUNv
# bmZpZ3VyYXRpb24sREM9QU1FLERDPUdCTD9jZXJ0aWZpY2F0ZVJldm9jYXRpb25M
# aXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnQwHwYDVR0j
# BBgwFoAUG2aiGfyb66XahI8YmOkQpMN7kr0wHwYDVR0lBBgwFgYKKwYBBAGCN1sD
# AQYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAFdWLRaOg25JZG+Hm01zB/zB
# oSC3MUJ7lWHPIE44xH/7Ek9n0KnzXthnL345WNBcnW3pNbqHGVeLx7SlYJFbsiLi
# vKm3+FUc71F5AQvySUTOpRvHRmEBgzuZo9t6n211l2GQLWdGMGvzrIaeV81wsP2r
# W0G++acIHvczziw0mDTM3UYNeyxI6rFwsZsdfbvzbmsqcZuK9B699sEQoWQO19Fu
# 0sIkj3WPKlATUk9dAAhHkwl2dcPckrvhBvwa9rYPLPAjWsFTZLdRTBubE9ukikdd
# PDTqTM+9FhlPwo7PGMKyBngj9jp4WsfIyDfVfE1W/LgtDa+0SN7mPPNNbW5SKcMw
# ggjmMIIGzqADAgECAhMfAAAAFLTFH8bygL5xAAAAAAAUMA0GCSqGSIb3DQEBCwUA
# MDwxEzARBgoJkiaJk/IsZAEZFgNHQkwxEzARBgoJkiaJk/IsZAEZFgNBTUUxEDAO
# BgNVBAMTB2FtZXJvb3QwHhcNMTYwOTE1MjEzMzAzWhcNMjEwOTE1MjE0MzAzWjBB
# MRMwEQYKCZImiZPyLGQBGRYDR0JMMRMwEQYKCZImiZPyLGQBGRYDQU1FMRUwEwYD
# VQQDEwxBTUUgQ1MgQ0EgMDEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDVV4EC1vn60PcbgLndN80k3GZh/OGJcq0pDNIbG5q/rrRtNLVUR4MONKcWGyae
# VvoaQ8J5iYInBaBkaz7ehYnzJp3f/9Wg/31tcbxrPNMmZPY8UzXIrFRdQmCLsj3L
# cLiWX8BN8HBsYZFcP7Y92R2VWnEpbN40Q9XBsK3FaNSEevoRzL1Ho7beP7b9FJlK
# B/Nhy0PMNaE1/Q+8Y9+WbfU9KTj6jNxrffv87O7T6doMqDmL/MUeF9IlmSrl088b
# oLzAOt2LAeHobkgasx3ZBeea8R+O2k+oT4bwx5ZuzNpbGXESNAlALo8HCf7xC3hW
# qVzRqbdnd8HDyTNG6c6zwyf/AgMBAAGjggTaMIIE1jAQBgkrBgEEAYI3FQEEAwIB
# ATAjBgkrBgEEAYI3FQIEFgQUkfwzzkKe9pPm4n1U1wgYu7jXcWUwHQYDVR0OBBYE
# FBtmohn8m+ul2oSPGJjpEKTDe5K9MIIBBAYDVR0lBIH8MIH5BgcrBgEFAgMFBggr
# BgEFBQcDAQYIKwYBBQUHAwIGCisGAQQBgjcUAgEGCSsGAQQBgjcVBgYKKwYBBAGC
# NwoDDAYJKwYBBAGCNxUGBggrBgEFBQcDCQYIKwYBBQUIAgIGCisGAQQBgjdAAQEG
# CysGAQQBgjcKAwQBBgorBgEEAYI3CgMEBgkrBgEEAYI3FQUGCisGAQQBgjcUAgIG
# CisGAQQBgjcUAgMGCCsGAQUFBwMDBgorBgEEAYI3WwEBBgorBgEEAYI3WwIBBgor
# BgEEAYI3WwMBBgorBgEEAYI3WwUBBgorBgEEAYI3WwQBBgorBgEEAYI3WwQCMBkG
# CSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjASBgNVHRMBAf8E
# CDAGAQH/AgEAMB8GA1UdIwQYMBaAFCleUV5krjS566ycDaeMdQHRCQsoMIIBaAYD
# VR0fBIIBXzCCAVswggFXoIIBU6CCAU+GI2h0dHA6Ly9jcmwxLmFtZS5nYmwvY3Js
# L2FtZXJvb3QuY3JshjFodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpaW5mcmEv
# Y3JsL2FtZXJvb3QuY3JshiNodHRwOi8vY3JsMi5hbWUuZ2JsL2NybC9hbWVyb290
# LmNybIYjaHR0cDovL2NybDMuYW1lLmdibC9jcmwvYW1lcm9vdC5jcmyGgapsZGFw
# Oi8vL0NOPWFtZXJvb3QsQ049QU1FUk9PVCxDTj1DRFAsQ049UHVibGljJTIwS2V5
# JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1BTUUs
# REM9R0JMP2NlcnRpZmljYXRlUmV2b2NhdGlvbkxpc3Q/YmFzZT9vYmplY3RDbGFz
# cz1jUkxEaXN0cmlidXRpb25Qb2ludDCCAasGCCsGAQUFBwEBBIIBnTCCAZkwNwYI
# KwYBBQUHMAKGK2h0dHA6Ly9jcmwxLmFtZS5nYmwvYWlhL0FNRVJPT1RfYW1lcm9v
# dC5jcnQwRwYIKwYBBQUHMAKGO2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2lp
# bmZyYS9jZXJ0cy9BTUVST09UX2FtZXJvb3QuY3J0MDcGCCsGAQUFBzAChitodHRw
# Oi8vY3JsMi5hbWUuZ2JsL2FpYS9BTUVST09UX2FtZXJvb3QuY3J0MDcGCCsGAQUF
# BzAChitodHRwOi8vY3JsMy5hbWUuZ2JsL2FpYS9BTUVST09UX2FtZXJvb3QuY3J0
# MIGiBggrBgEFBQcwAoaBlWxkYXA6Ly8vQ049YW1lcm9vdCxDTj1BSUEsQ049UHVi
# bGljJTIwS2V5JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlv
# bixEQz1BTUUsREM9R0JMP2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1j
# ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MA0GCSqGSIb3DQEBCwUAA4ICAQAot0qGmo8f
# pAFozcIA6pCLygDhZB5ktbdA5c2ZabtQDTXwNARrXJOoRBu4Pk6VHVa78Xbz0OZc
# 1N2xkzgZMoRpl6EiJVoygu8Qm27mHoJPJ9ao9603I4mpHWwaqh3RfCfn8b/NxNhL
# Gfkrc3wp2VwOtkAjJ+rfJoQlgcacD14n9/VGt9smB6j9ECEgJy0443B+mwFdyCJO
# 5OaUP+TQOqiC/MmA+r0Y6QjJf93GTsiQ/Nf+fjzizTMdHggpTnxTcbWg9JCZnk4c
# C+AdoQBKR03kTbQfIm/nM3t275BjTx8j5UhyLqlqAt9cdhpNfdkn8xQz1dT6hTnL
# iowvNOPUkgbQtV+4crzKgHuHaKfJN7tufqHYbw3FnTZopnTFr6f8mehco2xpU8bV
# KhO4i0yxdXmlC0hKGwGqdeoWNjdskyUyEih8xyOK47BEJb6mtn4+hi8TY/4wvuCz
# cvrkZn0F0oXd9JbdO+ak66M9DbevNKV71YbEUnTZ81toX0Ltsbji4PMyhlTg/669
# BoHsoTg4yoC9hh8XLW2/V2lUg3+qHHQf/2g2I4mm5lnf1mJsu30NduyrmrDIeZ0l
# dqKzHAHnfAmyFSNzWLvrGoU9Q0ZvwRlDdoUqXbD0Hju98GL6dTew3S2mcs+17Dgs
# dargsEPm6I1lUE5iixnoEqFKWTX5j/TLUjGCFRowghUWAgEBMFgwQTETMBEGCgmS
# JomT8ixkARkWA0dCTDETMBEGCgmSJomT8ixkARkWA0FNRTEVMBMGA1UEAxMMQU1F
# IENTIENBIDAxAhM2AAABDBlaELMo09izAAEAAAEMMA0GCWCGSAFlAwQCAQUAoIGu
# MBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgor
# BgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBkHWwKYiwCSGmFSZwPVdDPn7g8x3Ma
# tNK3k7FQXCNjMDBCBgorBgEEAYI3AgEMMTQwMqAUgBIATQBpAGMAcgBvAHMAbwBm
# AHShGoAYaHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIB
# ACa+5XDAwXvdE1TVy3mdatAYkp0BjgLk+GvU/33ENxOXMrDK1iGL6ML2MuPHCtzG
# SA6X98/JFbq4a4jiMFruOCEUj11dCvcuszvnLGDuOTHrFPyO2XXnx88tsGI3d09K
# hVbUntMYu13+w10ETn5MXyuu2v1aeagahn6C2AmYJrPCKT3dnb+m/WymfAPx/cOP
# LS5vPsGhc1MiAS120D/HaC7QgveubJMoBjvCUVokAzGPrT+thtltdTB5qCiHH1yd
# t5Cks7JmxSg6gZ/AoIibR8fQu8juDyiu+FL0i6Y4gRpv+F5Le9/Wps08mDOjug1W
# oF/+POTv3Gn5nZEIW3pRLdyhghLiMIIS3gYKKwYBBAGCNwMDATGCEs4wghLKBgkq
# hkiG9w0BBwKgghK7MIIStwIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUQYLKoZIhvcN
# AQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkKAwEwMTANBglghkgBZQMEAgEF
# AAQgPX2mvMUrM7NWKKW8vSw/bL1YHLBfIvhO87gL15Z5/3ICBl8V/u5quRgTMjAy
# MDA3MjgwNDE5NDkuMzAxWjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0JCRC1FMzM4LUU5
# QTExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Wggg45MIIE
# 8TCCA9mgAwIBAgITMwAAAR3CwgiuPrquSAAAAAABHTANBgkqhkiG9w0BAQsFADB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0xOTExMTMyMTQwMzlaFw0y
# MTAyMTEyMTQwMzlaMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYD
# VQQLEx1UaGFsZXMgVFNTIEVTTjozQkJELUUzMzgtRTlBMTElMCMGA1UEAxMcTWlj
# cm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
# ADCCAQoCggEBAJyFAfqhfvWYChz5UIApKIOCcRO5wwRonHB1LcdxuA5n+0qtcT8W
# 2IY+O147Mx+JKOh6XfRaIFSS0THVzrP/gLdcwhodY2Pv8hHoP1nvuLaK6bQq8hAo
# ax+YX1rooyCDYdYzhEl/OdpVa0f1Pkq8i0XawcoMNx479zuFQBojMupUI0wTavpW
# vuRh8ie7k0cfSqSA4gZlb0FWCa2l6lZmhK5dwaYX8fUxzfY38q/yxdX+FiUTdNOA
# LMDyzm02i+yimHMP1fQByyw2wnSpGpsDu7+yvclm1BrOM+2N/DXAa+HVQwneVyau
# Bp3CWWVHDbUrOPVc0S741inyLM8juq+g/OUCAwEAAaOCARswggEXMB0GA1UdDgQW
# BBQXXloUYN7I8gi+K0NqCM/ArZvCqTAfBgNVHSMEGDAWgBTVYzpcijGQ80N7fEYb
# xTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5j
# b20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmww
# WgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNV
# HRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IB
# AQBgjJGdkPAMyzjg0BXr4fps+eMb31FfDhF4p2PRJI7kh3AIUSMwuQ8d/P2V7lXe
# EAGFrrzzoQzG/dLb3J+lQdbmpsK0sKfwLJ2Jnx7I0pwUe4qYSbDqzJarhT+1Y4eP
# jm8AWnXrXZUjf/5tnSeDzYB03qOeMqW7D/BN+2q0bohz+001jrG2pVH5llyrygwS
# cp9iPei+zaJbjwvbvIa1YP0XqG4lJEi4hsrbczS+iFE57Pm2edZMtQujVxGPy2Zp
# 1MtNxU5zOB+7dnNrTMW4UFtR+QRnSAwbrWcmuQWdeKXJeH3gWHr/0jAUbIOmhgfw
# ncEBSn9aFt4F+tDJNVYbzEkwMIIGcTCCBFmgAwIBAgIKYQmBKgAAAAAAAjANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAxMjE0NjU1WjB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5m
# K1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiEVEMM1024OAizQt2TrNZzMFcm
# gqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5
# hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/Vm
# wAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXoeByw6ZnNPOcvRLqn9NxkvaQB
# wSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYwggHiMBAGCSsGAQQBgjcVAQQD
# AgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIE
# DB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
# HSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVo
# dHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29D
# ZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAC
# hj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1
# dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMw
# gYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9j
# cy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8A
# UABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQEL
# BQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM9TG+zwXiqf76V20ZMLPCxWbJ
# at/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1
# mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgPF/UveYFl2am1a+THzvbKegBv
# SzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/625Y4zu2JfmttXQOnxzplmkIz/
# amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZqkHimbdLhnPkd/DjYlPTGpQqW
# hqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua
# 2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5vvfHhAN/nMQekkzr3ZUd46Pio
# SKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqH
# czsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduWsqdCosnPGUFN4Ib5KpqjEWYw
# 07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV42neV8HR3jDA/czmTfsNv11P
# 6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto229Nfj950iEkSoYICyzCCAjQC
# AQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNV
# BAsTHVRoYWxlcyBUU1MgRVNOOjNCQkQtRTMzOC1FOUExMSUwIwYDVQQDExxNaWNy
# b3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBEI59Ed+Kz
# R24NQhD8LH1pHjgnQqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy
# MDEwMA0GCSqGSIb3DQEBBQUAAgUA4sm3jTAiGA8yMDIwMDcyODA0MjkwMVoYDzIw
# MjAwNzI5MDQyOTAxWjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDiybeNAgEAMAcC
# AQACAhB0MAcCAQACAhGYMAoCBQDiywkNAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwG
# CisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEF
# BQADgYEATXkBJn36rXQT4SBzYWg2rfJBO/vacD1+A2opl+3ahQlsG03lSZPBPnop
# E45cT36QtYXA8tfgnmSqD7Kz1C2XjKxgwiZiskN9iaAhVqRbT+sMplvaY1WuC+Fe
# mYpMIQ0TFXZm4Xh3O3bxOA1CoJjIH4+9dxrGz/VTyQJis987YK0xggMNMIIDCQIB
# ATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAR3CwgiuPrqu
# SAAAAAABHTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3
# DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCAQSU8NJsUArtcPsP8pvelz5ssCrdR72I2H
# CubMnee1WzCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIAK+DMNCn3d8hwuu
# bOkJ7YgKQkXtcRtSXt1yRull75qTMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh
# bXAgUENBIDIwMTACEzMAAAEdwsIIrj66rkgAAAAAAR0wIgQgt4ZdV/KKGQ5MsS74
# Lh9ylXTWZzytxlvwFMV+umlkTp0wDQYJKoZIhvcNAQELBQAEggEAj5DVEwlmqmim
# +FXxV2RcW+J4Wq97tW0sAdprwYprOOx39IuFXOMq5EIkCuCPfswUj/jsQ3mnmhca
# DNmZEN1ZpxdvLuobWIE2QsTWu3doPaIEsbynKEYDYsWM3u4xp3bHWDnik8v/MZEj
# yCytCbnkWux+Sx239TNwQk4TnuZ5xlum2KYCj6eh3TL4LUCCaTdXbseq4AuK4a2Z
# QWiqkhgNDrtI7WKhIrmFn2Pc/b8NJS3vNM1Jfq5/1//VmBfnbDfa/THpMzpJxD9O
# EgQDgU4lwuJImFZKuhtbswY4WuxEHbrA7Lpzihyp5VqqGPZxVNxkMZN9ob/JmTiQ
# qJovkXHoDA==
# SIG # End signature block