Private/az-commands.ps1

$MAX_RETRY_COUNT = 4  # for some operations, retry a couple of times
$SNAILMODE_MAX_RETRY_COUNT = 10 # For very slow tenants, retry more often

$script:Snail_Mode = $false
$Sleep_Factor = 1
$Snail_Maximum_Sleep_Factor = 90 # times ten is 15 minutes

function Convert-LinesToObject {
    param (
        [Parameter(ValueFromPipeline = $true)]
        [string[]]
        $Lines
    )

    BEGIN {
        $linesJsonBuilder = new-object System.Text.StringBuilder
    }

    PROCESS {
        if ($null -eq $Lines) {
            return
        }
        $null = $linesJsonBuilder.Append([string]::Concat($Lines))
    }

    END {
        if ($null -eq $Lines) {
            return
        }

        return ConvertFrom-Json $linesJsonBuilder.ToString()
    }
}

$PERMISSION_ALREADY_ASSIGNED = "Permission already assigned"
$PERMISSION_DOES_NOT_EXIST = "Permission does not exist"

function CheckAzOutput($azOutput, $fThrowOnError, $noSecretLeakageWarning = $false) {
    [String[]]$errorMessages = @()
    foreach ($outputElement in $azOutput) {
        if ($null -ne $outputElement) {
            if ($outputElement.GetType() -eq [System.Management.Automation.ErrorRecord]) {
                if ($outputElement.ToString().Contains("Permission being assigned already exists on the object") -or $outputElement.ToString().Contains("RoleAssignmentExists") -or $outputElement.ToString().Contains("Permission already assigned") -or $outputElement.ToString() -match 'A role assignment with ID.*already exists') {
                    Write-Information "Permission is already assigned when executing $azCommand"
                    Write-Output $PERMISSION_ALREADY_ASSIGNED
                }
                elseif ($outputElement.ToString().Contains("Permission being assigned was not found on")) {
                    Write-Information "Could not assign permission, as it does not exist for this application, when executing $azCommand"
                    Write-Output $PERMISSION_DOES_NOT_EXIST
                }
                elseif ($outputElement.ToString().Contains("does not exist or one of its queried reference-property objects are not present.")) {
                    # This indicates we are in a tenant with especially long delays between creation of an object and when it becomes available via Graph (this happens and it seems to be tenant-specific).
                    # Let's go into snail mode and thereby grant Graph more time
                    Write-Warning "Created object is not yet available via MS Graph. Reducing executing speed to give Graph more time."
                    $script:Snail_Mode = $true
                    $Sleep_Factor = 0.8 * $Sleep_Factor + 0.2 * $Snail_Maximum_Sleep_Factor # approximate longer sleep times
                    Write-Verbose "Retrying operations now $SNAILMODE_MAX_RETRY_COUNT times, and waiting for (n * $Sleep_Factor) seconds on n-th retry"
                }
                elseif ($outputElement.ToString().Contains("Blowfish") -or $outputElement.ToString().Contains('cryptography on a 32-bit Python')) {
                    # Ignore, this is an issue of az 2.45.0 and az 2.45.0-preview
                    Write-Debug "Ignoring expected warning about Blowfish: $outputElement"
                }
                elseif ($outputElement.ToString().EndsWith('MGMT_DEPLOYMENTMANAGER') -and $outputElement.ToString().StartsWith('ERROR')) {
                    Write-Warning "Ignoring error message from az account show: $outputElement"
                }
                elseif ($outputElement.ToString().Contains("CryptographyDeprecationWarning")) {
                    # Ignore, this is an issue of az 2.64.0-preview
                    Write-Debug "Ignoring expected warning about algorithm deprecation: $outputElement"
                    $expectAlgorithmToBeIgnored = $true
                }
                elseif ($expectAlgorithmToBeIgnored -and ($outputElement.ToString().Trim(' ').StartsWith('"class": algorithms') -or $outputElement.ToString().Trim(' ').StartsWith('"cipher": algorithms'))) {
                    # Ignore, this is the next line of the previous issue
                    Write-Debug "Ignoring algorithm line for crypto warning: $outputElement"
                    $expectAlgorithmToBeIgnored = $false
                }
                elseif ($expectPackageWarning -and $outputElement.ToString().Contains('pkg_resources')) {
                    # Ignore, this is the next line of the previous issue
                    Write-Debug "Ignoring package warning line: $outputElement"
                    $expectPackageWarning = $false
                }
                elseif($outputElement.ToString().Contains("The specified table: 'SCEPman_CL' does not exist.")) {
                    # Ignore, the table does not exist yet
                    Write-Debug "Ignoring expected warning about missing table"
                    $expectResourceNotFound = $true
                }
                elseif($outputElement.ToString().Contains("The Resource 'Microsoft.Insights/dataCollectionRules/dcr-scepmanlogs'") -and $outputElement.ToString().Contains("was not found.")) {
                    # Ignore, the table does not exist yet
                    Write-Debug "Ignoring expected warning about missing DCR"
                    $expectResourceNotFound = $true
                }
                elseif ($expectResourceNotFound -and ($outputElement.ToString().Contains('Code: ResourceNotFound'))) {
                    Write-Debug "Ignoring expected error: ResourceNotFound"
                }
                elseif ($outputElement.ToString().Trim(' ') -in @("ERROR: (ResourceNotFound) None", "Code: ResourceNotFound", "Message: None")) {
                    Write-Debug "Ignoring expected generic error: $outputElement"
                }
                elseif ($outputElement.ToString().Contains("SyntaxWarning: invalid escape sequence '\ '")) {
                    # Ignore, this is a harmless issue of az graph extension 2.10 with more recent python versions (?)
                    # See https://github.com/Azure/azure-cli-extensions/issues/8369
                    Write-Debug "Ignoring expected warning about wrong escape seqences: $outputElement"
                    $expectIntervalWarning = $true
                }
                elseif ($expectIntervalWarning -and ($outputElement.ToString().Trim(' ').StartsWith('"""'))) {
                    # Ignore, this is the next line of the previous issue
                    Write-Debug "Ignoring line for syntax warning: $outputElement"
                    $expectIntervalWarning = $false
                }
                elseif ($outputElement.ToString().Contains("SyntaxWarning: invalid escape sequence '\s'")) {
                    # See https://github.com/HandBrake/HandBrake/issues/5454
                    Write-Debug "Ignoring expected warning about wrong escape seqences: $outputElement"
                    $expectAzSuggestionWarning = $true
                }
                elseif ($expectAzSuggestionWarning -and ($outputElement.ToString().Trim(' ').StartsWith('az monitor'))) {
                    Write-Debug "Ignoring expected AI suggestion about some other az module: $outputElement"
                }
                elseif ($outputElement.ToString().StartsWith("WARNING") -or $outputElement.ToString().Contains("UserWarning: ")) {
                    if ($outputElement.ToString().StartsWith("WARNING: The underlying Active Directory Graph API will be replaced by Microsoft Graph API") `
                            -or $outputElement.ToString().StartsWith("WARNING: This command or command group has been migrated to Microsoft Graph API.")) {
                        # Ignore, we know that
                        Write-Debug "Ignoring expected warning about Graph API migration: $outputElement"
                    }
                    elseif ($outputElement.ToString().StartsWith("WARNING: App settings have been redacted.")) {
                        # Ignore, this is a new behavior of az 2.53.1 and affects the output of az webapp settings set, which we do not use anyway.
                        Write-Debug "Ignoring expected warning about redacted app settings: $outputElement"
                    }
                    elseif ($noSecretLeakageWarning -and $outputElement.ToString().StartsWith("WARNING: [Warning] This output may compromise security by showing")) {
                        Write-Debug "Ignoring expected warning about secret leakage: $outputElement"
                    }
                    elseif ($outputElement.ToString().Contains("pkg_resources is deprecated as an API.")) {
                        # Ignore, see https://developercommunity.visualstudio.com/t/Azure-DevOps-Extension-reports-pkg_resou/10919558
                        Write-Debug "Ignoring expected warning about a deprecated package because of some extension: $outputElement"
                        $expectPackageWarning = $true
                    }
                    else {
                        Write-Debug "Warning about unexpected az output"
                        Write-Warning $outputElement.ToString()
                    }
                }
                else {
                    if ($outputElement.ToString().contains("does not have authorization to perform action 'Microsoft.Authorization/roleAssignments/write'")) {
                        $errorMessages += "You have insufficient privileges to assign roles to Managed Identities. Make sure you have the Global Admin or Privileged Role Administrator role."
                    }
                    elseif ($outputElement.ToString().Contains("Forbidden")) {
                        $errorMessages += "You have insufficient privileges to complete the operation. Please ensure that you run this CMDlet with required privileges e.g. Global Administrator"
                    }

                    Write-Debug "Error about unexpected az output: $outputElement"
                    $errorMessages += $outputElement
                }
            }
            else {
                Write-Output $outputElement # add to return value of this function
            }
        }
    }
    if ($errorMessages.Count -gt 0) {
        $ErrorMessageOneLiner = [String]::Join("`r`n", $errorMessages)
        if ($fThrowOnError) {
            throw $ErrorMessageOneLiner
        }
        else {
            Write-Error $ErrorMessageOneLiner
        }
    }
}

function AzLogin {
    # Check whether az is available
    $azCommand = Get-Command az 2>&1
    if ($azCommand.GetType() -eq [System.Management.Automation.ErrorRecord]) {
        if ($azCommand.CategoryInfo.Reason -eq "CommandNotFoundException") {
            $errorMessage = "Azure CLI (az) is not installed, but required. Please use the Azure Cloud Shell or install Azure CLI as described here: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli"
            Write-Error $errorMessage
            throw $errorMessage
        }
        else {
            Write-Error "Unknown error checking for az"
            throw $azCommand
        }
    }

    # check whether already logged in
    $env:AZURE_HTTP_USER_AGENT = "pid-a262352f-52a9-4ed9-a9ba-6a2b2478d19b"
    try {
        $account = Invoke-Az -azCommand @("account", "show") -MaxRetries 0
    }
    catch {
        $errorMessage = $_.Exception.Message
        if (($errorMessage.Contains("az login")) -or ($errorMessage.Contains("az account set"))) {
            Write-Warning "Not logged in with az yet. Trying to log in ... if this doesn't work, please log in manually."
            $null = az login # TODO: Check whether the login worked
            return AzLogin
        }
        else {
            Write-Error "Error $errorMessage while trying to use az" # possibly az not installed?
            throw new-object System.Exception("Error when checking whether az is logged in", $_.Exception)
        }
    }
    try {
        $accountInfo = Convert-LinesToObject($account)
    }
    catch {
        $errormessage = "Error parsing output from az account show. Error message: $_"
        $errorMessage += "`r`Output from az account show: $account"
        Write-Error $errormessage
        throw $errorMessage
    }
    Write-Information "Logged in to az as $($accountInfo.user.name)"

    return $accountInfo
}

$azVersionInfo = $null

function GetAzVersion {
    if ($null -eq $azVersionInfo) {
        $azVersionInfo = Convert-LinesToObject -lines $(az version)
    }
    return $azVersionInfo
}

function AzUsesAADGraph {
    $cliVersion = [Version]::Parse((GetAzVersion).'azure-cli')
    return $cliVersion -lt '2.37'
}

# Check heuristically whether we are in Azure Cloud Shell
function IsAzureCloudShell {
    $cloudShellProves = 0   # The more proves, the more likely we are in Azure Cloud Shell. We use a 2 out of 3 vote.
    $azuredrive = get-psdrive -Name Azure -ErrorAction Ignore
    if ($null -ne $azuredrive) {
        ++$cloudShellProves
    }

    if (Test-Path -Path ~/clouddrive) {
        ++$cloudShellProves
    }

    if ($PSVersionTable.Platform -eq "Unix") {
        ++$cloudShellProves
    }

    return $cloudShellProves -ge 2
}

function Invoke-Az ($azCommand, $maxRetries = $MAX_RETRY_COUNT) {
    return ExecuteAzCommandRobustly -azCommand $azCommand -callAzNatively -maxRetries $maxRetries
}

function Format-AzCommandArgumentForDisplay($azCommandArgument) {
    if ($null -eq $azCommandArgument) {
        return '""'
    }

    $argument = $azCommandArgument.ToString()
    if ($argument -cmatch '^[A-Za-z0-9-]+$') {
        return $argument
    }

    # Escaping must happen outside the double-quoted string, otherwise its parser eats the backticks
    $escapedArgument = $argument.Replace('`', '``').Replace('"', '`"')
    return "`"$escapedArgument`""
}

function Format-AzCommandForDisplay($azCommand) {
    return (@($azCommand) | ForEach-Object { Format-AzCommandArgumentForDisplay -azCommandArgument $_ }) -join ' '
}

function WriteToAzStdin($azCommand, [string]$stdinInput) {
    if ($PSVersionTable.PSVersion.Major -ge 7) {
        # PowerShell 7+ handles stdin piping correctly without BOM
        $previousOutputEncoding = $OutputEncoding
        $OutputEncoding = [System.Text.UTF8Encoding]::new($false)
        try {
            return $stdinInput | az $azCommand 2>&1
        } finally {
            $OutputEncoding = $previousOutputEncoding
        }
    }

    # Windows PowerShell 5.1: Piping stdin through batch files (az.cmd) is unreliable.
    # Instead, write to a temp file and use az CLI's @filepath mechanism.
    $tempFile = [System.IO.Path]::GetTempFileName()
    try {
        [System.IO.File]::WriteAllText($tempFile, $stdinInput, [System.Text.UTF8Encoding]::new($false))
        $modifiedCommand = $azCommand | ForEach-Object { if ($_ -eq '@-') { "@$tempFile" } else { $_ } }
        return az $modifiedCommand 2>&1
    } finally {
        Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
    }
}

# It is intended to use for az cli add permissions and az cli add permissions admin
# $azCommand - The command to execute.
# $noSecretLeakageWarning - Pass true if you are sure that the output contains no secrets. This will supress az warnings about leaking secrets in the output.
function ExecuteAzCommandRobustly($azCommand, $principalId = $null, $appRoleId = $null, $GraphBaseUri = $null, $maxRetries = $MAX_RETRY_COUNT, [switch]$callAzNatively, [switch]$noSecretLeakageWarning, $stdinInput = $null) {
    $azErrorCode = 1234 # A number not null
    $retryCount = 0
    $script:Snail_Mode = $false

    try {
        $definedPreference = $PSNativeCommandUseErrorActionPreference
        $PSNativeCommandUseErrorActionPreference = $false   # See https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables?view=powershell-7.3#psnativecommanduseerroractionpreference

        while ($azErrorCode -gt 0 -and ($retryCount -le $maxRetries -or $script:Snail_Mode -and $retryCount -le $SNAILMODE_MAX_RETRY_COUNT)) {
            $PreviousErrorActionPreference = $ErrorActionPreference
            $ErrorActionPreference = "Continue"     # In Windows PowerShell, if this is set to "Stop", az will not return the error code, but instead throw an exception
            $LASTEXITCODE = 0   # Required for unit tests when mocking az
            if ($null -ne $stdinInput) {
                Write-Debug "Calling az natively with stdin: az $azCommand"
                $lastAzOutput = WriteToAzStdin -azCommand $azCommand -stdinInput $stdinInput
            }
            elseif ($callAzNatively) {
                Write-Debug "Calling az natively: az $azCommand"
                $lastAzOutput = az $azCommand 2>&1
            }
            else {
                $lastAzOutput = Invoke-Expression "$azCommand 2>&1" # the output is often empty in case of error :-(. az just writes to the console then
            }
            $azErrorCode = $LASTEXITCODE
            $ErrorActionPreference = $PreviousErrorActionPreference
            Write-Debug "az command $azCommand returned with error code $azErrorCode"
            try {
                $lastAzOutput = CheckAzOutput -azOutput $lastAzOutput -fThrowOnError $true -noSecretLeakageWarning $noSecretLeakageWarning
                # If we were requested to check that the permission is there and there was no error, do the check now.
                # However, if the permission has been there previously already, we can skip the check
                if ($null -ne $appRoleId -and $azErrorCode -eq 0 -and $PERMISSION_ALREADY_ASSIGNED -ne $lastAzOutput) {
                    $appRoleAssignments = Convert-LinesToObject -lines $(az rest --method get --url "$GraphBaseUri/v1.0/servicePrincipals/$principalId/appRoleAssignments")
                    $grantedPermission = $appRoleAssignments.value | Where-Object { $_.appRoleId -eq $appRoleId }
                    if ($null -eq $grantedPermission) {
                        $azErrorCode = 999 # A number not 0
                    }
                }
                elseif ($null -ne $appRoleId -and $PERMISSION_ALREADY_ASSIGNED -eq $lastAzOutput) {
                    $azErrorCode = 0  # The permission was already there, so we are done. Ignore the error message that the permission was already there.
                }
                elseif ($null -ne $appRoleId -and $PERMISSION_DOES_NOT_EXIST -eq $lastAzOutput) {
                    $azErrorCode = -24  # This kind of permission doesn't even exist. We are probably not in the global cloud or something else is unusual. No need to retry.
                }
            }
            catch {
                Write-Warning $_
                $azErrorCode = 654  # a number not 0

                $message = $_.ToString()
                if ($message.Contains("Failed to connect to MSI. Please make sure MSI is configured correctly") -and $message.Contains("400")) {
                    if (IsAzureCloudShell) {
                        Write-Warning "Trying to log in again to Azure CLI, as this usually fixes the token issue in Azure Cloud Shell"
                        az login
                    }
                }
            }
            if ($azErrorCode -gt 0) {
                ++$retryCount
                Write-Verbose "Retry $retryCount for $azCommand after $($retryCount * $SLEEP_FACTOR) seconds of sleep because Error Code is $azErrorCode"
                Start-Sleep ($retryCount * $SLEEP_FACTOR) # Sleep for some seconds, as the grant sometimes only works after some time
            }
        }
    }
    finally {
        $PSNativeCommandUseErrorActionPreference = $definedPreference
    }

    if ($azErrorCode -ne 0 ) {
        if ($null -eq $lastAzOutput) {
            $errorMessage = "no error message"
            $readableAzOutput = "no az output"
        }
        else {
            # might be an object[]
            $readableAzOutput = CheckAzOutput -azOutput $lastAzOutput -fThrowOnError $false
            try {
                $null = CheckAzOutput -azOutput $lastAzOutput -fThrowOnError $true
                throw "During second evaluation of az output, the output was not an error, but it should have been. This is unexpected."
            }
            catch {
                $errorMessage = $_.ToString()
            }
        }
        throw "Error $azErrorCode when executing $azCommand : $readableAzOutput; Error message: $errorMessage"
    }
    else {
        return $lastAzOutput
    }
}

function HashTable2AzJson($psHashTable) {
    $output = ConvertTo-Json -Compress -InputObject $psHashTable -Depth 10
    if ($PSVersionTable.PSVersion.Major -lt 7 -or ($PSVersionTable.PSVersion.Major -eq 7 -and $PSVersionTable.PSVersion.Minor -lt 3) `
            -or $PSVersionTable.OS.StartsWith("Microsoft Windows")) {
        # The double quoting is now also required on PS 7.3.0 on Windows ... does it depend on the az version?
        $output = $output -replace '"', '\"' # The double quoting is required by PowerShell <7.2 (see https://github.com/PowerShell/PowerShell/issues/1995 and https://docs.microsoft.com/en-us/cli/azure/use-cli-effectively?tabs=bash%2Cbash2#use-quotation-marks-in-parameters)
        return $output.Insert(1, ' ') # Seemingly, there needs to be a space in the JSON somewhere in the beginning for PS 5 to pass consecutive spaces to az instead of having space-separated parameters
    }
    return $output
}

function AppSettingsHashTable2AzJson($psHashTable, $convertForLinux) {
    if ($convertForLinux) {
        $escapedpsHashTable = @{}
        foreach ($key in $psHashTable.Keys) {
            $escapedpsHashTable.Add($key.Replace(":", "__"), $psHashTable[$key])
        }
    }
    else {
        $escapedpsHashTable = $psHashTable
    }

    return HashTable2AzJson -psHashTable $escapedpsHashTable
}
# SIG # Begin signature block
# MIIv6wYJKoZIhvcNAQcCoIIv3DCCL9gCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBM86OkcwhjEXw+
# LcYgg8alONGvI2vl4dC2rps9eiOc+aCCFA4wggVyMIIDWqADAgECAhB2U/6sdUZI
# k/Xl10pIOk74MA0GCSqGSIb3DQEBDAUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK
# ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln
# bmluZyBSb290IFI0NTAeFw0yMDAzMTgwMDAwMDBaFw00NTAzMTgwMDAwMDBaMFMx
# CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQD
# EyBHbG9iYWxTaWduIENvZGUgU2lnbmluZyBSb290IFI0NTCCAiIwDQYJKoZIhvcN
# AQEBBQADggIPADCCAgoCggIBALYtxTDdeuirkD0DcrA6S5kWYbLl/6VnHTcc5X7s
# k4OqhPWjQ5uYRYq4Y1ddmwCIBCXp+GiSS4LYS8lKA/Oof2qPimEnvaFE0P31PyLC
# o0+RjbMFsiiCkV37WYgFC5cGwpj4LKczJO5QOkHM8KCwex1N0qhYOJbp3/kbkbuL
# ECzSx0Mdogl0oYCve+YzCgxZa4689Ktal3t/rlX7hPCA/oRM1+K6vcR1oW+9YRB0
# RLKYB+J0q/9o3GwmPukf5eAEh60w0wyNA3xVuBZwXCR4ICXrZ2eIq7pONJhrcBHe
# OMrUvqHAnOHfHgIB2DvhZ0OEts/8dLcvhKO/ugk3PWdssUVcGWGrQYP1rB3rdw1G
# R3POv72Vle2dK4gQ/vpY6KdX4bPPqFrpByWbEsSegHI9k9yMlN87ROYmgPzSwwPw
# jAzSRdYu54+YnuYE7kJuZ35CFnFi5wT5YMZkobacgSFOK8ZtaJSGxpl0c2cxepHy
# 1Ix5bnymu35Gb03FhRIrz5oiRAiohTfOB2FXBhcSJMDEMXOhmDVXR34QOkXZLaRR
# kJipoAc3xGUaqhxrFnf3p5fsPxkwmW8x++pAsufSxPrJ0PBQdnRZ+o1tFzK++Ol+
# A/Tnh3Wa1EqRLIUDEwIrQoDyiWo2z8hMoM6e+MuNrRan097VmxinxpI68YJj8S4O
# JGTfAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0G
# A1UdDgQWBBQfAL9GgAr8eDm3pbRD2VZQu86WOzANBgkqhkiG9w0BAQwFAAOCAgEA
# Xiu6dJc0RF92SChAhJPuAW7pobPWgCXme+S8CZE9D/x2rdfUMCC7j2DQkdYc8pzv
# eBorlDICwSSWUlIC0PPR/PKbOW6Z4R+OQ0F9mh5byV2ahPwm5ofzdHImraQb2T07
# alKgPAkeLx57szO0Rcf3rLGvk2Ctdq64shV464Nq6//bRqsk5e4C+pAfWcAvXda3
# XaRcELdyU/hBTsz6eBolSsr+hWJDYcO0N6qB0vTWOg+9jVl+MEfeK2vnIVAzX9Rn
# m9S4Z588J5kD/4VDjnMSyiDN6GHVsWbcF9Y5bQ/bzyM3oYKJThxrP9agzaoHnT5C
# JqrXDO76R78aUn7RdYHTyYpiF21PiKAhoCY+r23ZYjAf6Zgorm6N1Y5McmaTgI0q
# 41XHYGeQQlZcIlEPs9xOOe5N3dkdeBBUO27Ql28DtR6yI3PGErKaZND8lYUkqP/f
# obDckUCu3wkzq7ndkrfxzJF0O2nrZ5cbkL/nx6BvcbtXv7ePWu16QGoWzYCELS/h
# AtQklEOzFfwMKxv9cW/8y7x1Fzpeg9LJsy8b1ZyNf1T+fn7kVqOHp53hWVKUQY9t
# W76GlZr/GnbdQNJRSnC0HzNjI3c/7CceWeQIh+00gkoPP/6gHcH1Z3NFhnj0qinp
# J4fGGdvGExTDOUmHTaCX4GUT9Z13Vunas1jHOvLAzYIwggboMIIE0KADAgECAhB3
# vQ4Ft1kLth1HYVMeP3XtMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkw
# FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENv
# ZGUgU2lnbmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAw
# MDBaMFwxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTIw
# MAYDVQQDEylHbG9iYWxTaWduIEdDQyBSNDUgRVYgQ29kZVNpZ25pbmcgQ0EgMjAy
# MDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMsg75ceuQEyQ6BbqYoj
# /SBerjgSi8os1P9B2BpV1BlTt/2jF+d6OVzA984Ro/ml7QH6tbqT76+T3PjisxlM
# g7BKRFAEeIQQaqTWlpCOgfh8qy+1o1cz0lh7lA5tD6WRJiqzg09ysYp7ZJLQ8LRV
# X5YLEeWatSyyEc8lG31RK5gfSaNf+BOeNbgDAtqkEy+FSu/EL3AOwdTMMxLsvUCV
# 0xHK5s2zBZzIU+tS13hMUQGSgt4T8weOdLqEgJ/SpBUO6K/r94n233Hw0b6nskEz
# IHXMsdXtHQcZxOsmd/KrbReTSam35sOQnMa47MzJe5pexcUkk2NvfhCLYc+YVaMk
# oog28vmfvpMusgafJsAMAVYS4bKKnw4e3JiLLs/a4ok0ph8moKiueG3soYgVPMLq
# 7rfYrWGlr3A2onmO3A1zwPHkLKuU7FgGOTZI1jta6CLOdA6vLPEV2tG0leis1Ult
# 5a/dm2tjIF2OfjuyQ9hiOpTlzbSYszcZJBJyc6sEsAnchebUIgTvQCodLm3HadNu
# twFsDeCXpxbmJouI9wNEhl9iZ0y1pzeoVdwDNoxuz202JvEOj7A9ccDhMqeC5LYy
# AjIwfLWTyCH9PIjmaWP47nXJi8Kr77o6/elev7YR8b7wPcoyPm593g9+m5XEEofn
# GrhO7izB36Fl6CSDySrC/blTAgMBAAGjggGtMIIBqTAOBgNVHQ8BAf8EBAMCAYYw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
# FgQUJZ3Q/FkJhmPF7POxEztXHAOSNhEwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0
# Q9lWULvOljswgZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8v
# b2NzcC5nbG9iYWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUH
# MAKGOmh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWdu
# aW5ncm9vdHI0NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9i
# YWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFUGA1UdIAROMEwwQQYJ
# KwYBBAGgMgECMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24u
# Y29tL3JlcG9zaXRvcnkvMAcGBWeBDAEDMA0GCSqGSIb3DQEBCwUAA4ICAQAldaAJ
# yTm6t6E5iS8Yn6vW6x1L6JR8DQdomxyd73G2F2prAk+zP4ZFh8xlm0zjWAYCImbV
# YQLFY4/UovG2XiULd5bpzXFAM4gp7O7zom28TbU+BkvJczPKCBQtPUzosLp1pnQt
# pFg6bBNJ+KUVChSWhbFqaDQlQq+WVvQQ+iR98StywRbha+vmqZjHPlr00Bid/XSX
# hndGKj0jfShziq7vKxuav2xTpxSePIdxwF6OyPvTKpIz6ldNXgdeysEYrIEtGiH6
# bs+XYXvfcXo6ymP31TBENzL+u0OF3Lr8psozGSt3bdvLBfB+X3Uuora/Nao2Y8nO
# ZNm9/Lws80lWAMgSK8YnuzevV+/Ezx4pxPTiLc4qYc9X7fUKQOL1GNYe6ZAvytOH
# X5OKSBoRHeU3hZ8uZmKaXoFOlaxVV0PcU4slfjxhD4oLuvU/pteO9wRWXiG7n9dq
# cYC/lt5yA9jYIivzJxZPOOhRQAyuku++PX33gMZMNleElaeEFUgwDlInCI2Oor0i
# xxnJpsoOqHo222q6YV8RJJWk4o5o7hmpSZle0LQ0vdb5QMcQlzFSOTUpEYck08T7
# qWPLd0jV+mL8JOAEek7Q5G7ezp44UCb0IXFl1wkl1MkHAHq4x/N36MXU4lXQ0x72
# f1LiSY25EXIMiEQmM2YBRN/kMw4h3mKJSAfa9TCCB6gwggWQoAMCAQICDF3VjaKN
# us83AvC1UTANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJCRTEZMBcGA1UEChMQ
# R2xvYmFsU2lnbiBudi1zYTEyMDAGA1UEAxMpR2xvYmFsU2lnbiBHQ0MgUjQ1IEVW
# IENvZGVTaWduaW5nIENBIDIwMjAwHhcNMjUxMTI3MTcwNDI2WhcNMjcxMTI4MTcw
# NDI2WjCCAQwxHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRIwEAYDVQQF
# EwlIUkIgMTIzODExEzARBgsrBgEEAYI3PAIBAxMCREUxFzAVBgsrBgEEAYI3PAIB
# AhMGSGVzc2VuMSIwIAYLKwYBBAGCNzwCAQETEU9mZmVuYmFjaCBhbSBNYWluMQsw
# CQYDVQQGEwJERTEPMA0GA1UECBMGSGVzc2VuMRowGAYDVQQHExFPZmZlbmJhY2gg
# YW0gTWFpbjEZMBcGA1UECQwQS2Fpc2Vyc3RyYcOfZSAzOTEXMBUGA1UEChMOZ2x1
# ZWNra2FuamEgQUcxFzAVBgNVBAMTDmdsdWVja2thbmphIEFHMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAkQoXi0dUFVttodx+Ydj1O6EZZqgDdlSGDA/6
# x1UCkMrWNVEW+LdbUGU8KW7fUcKPCAcDJNrXfXxZeBht2G4pPvhaMz/kBdSK6bI1
# sqo1WSN//beapdUefQpq/wgnUneq13tEJQAke6EWdLyidObcogBSp9wCXBbMWsTO
# utgCONjyu8AilmzRY+94lO7VwUA2LGGPX8FRAEt5AMzifsEo2lIEKiDou2H8HUUC
# PibiChiuT3oGIDYYnCA/RzS44E0cAuAzlD3NQNCeIDzfoFiUD8mAC1gYU6i8yIej
# jUGl8+kpbpBYjgzwbsiCBn0rDhrlpJ3MHkZCrp82kzWK0l7c3ukNvdlGcU4tKdXk
# AHgpJecdYUDvz9iaYFvYEivF+Jg+Tc8ZnzsP5/q3KKw4g0QiJ+MXgvwJx8OSvAKW
# tkwkLxgE9oxufs3Y8xsmwyWqxWDBcyzzvs6yISnUaeTtGmyB8BsEbahDFrxHhV6U
# nwxNpJ+iM+j08J1tNIW0AXjY6ojGOIC8IIL+EiK34MXJ6Jxy22mntMnc6ztK6c7H
# IKiRHIPX4jXtg7IYRS/k5muuIt/xKzN7qtF9xJbaZi8jRE6fgWDwszLJUMHSLthh
# yKTsUEvuqZ79WnSHErg26EPQYirAY/IFt7Z7+3SDW2WI8uG2qY6hkpE0hm+/F3uS
# M+s98jUCAwEAAaOCAbYwggGyMA4GA1UdDwEB/wQEAwIHgDCBnwYIKwYBBQUHAQEE
# gZIwgY8wTAYIKwYBBQUHMAKGQGh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20v
# Y2FjZXJ0L2dzZ2NjcjQ1ZXZjb2Rlc2lnbmNhMjAyMC5jcnQwPwYIKwYBBQUHMAGG
# M2h0dHA6Ly9vY3NwLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1ZXZjb2Rlc2lnbmNh
# MjAyMDBVBgNVHSAETjBMMEEGCSsGAQQBoDIBAjA0MDIGCCsGAQUFBwIBFiZodHRw
# czovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAHBgVngQwBAzAJBgNV
# HRMEAjAAMEcGA1UdHwRAMD4wPKA6oDiGNmh0dHA6Ly9jcmwuZ2xvYmFsc2lnbi5j
# b20vZ3NnY2NyNDVldmNvZGVzaWduY2EyMDIwLmNybDATBgNVHSUEDDAKBggrBgEF
# BQcDAzAfBgNVHSMEGDAWgBQlndD8WQmGY8Xs87ETO1ccA5I2ETAdBgNVHQ4EFgQU
# q/cn5ijjtp0mG1yoiF02hg4dx4IwDQYJKoZIhvcNAQELBQADggIBAJ1TZv/rvy2w
# jANcL/kb6rTk+/6L7l49UghLghUKFVfrdEEc+21iexA7zlkvhM0TrhdiFU7TjDky
# InPctzsDlqwUhawEx4PT8ZkZkZzm25YWaqtZH44st/Fz59KiG+85NUdRd+0cL3Y8
# NR66z3xfI6K3W/nrIcE6RHm/opOM+L02Hd2MBligLnoFYcTvR3NPCA21A6+IOaYM
# n5YZzNKFXWry8ZHpWjnE4u9mxHYpS1zu2aIkwL8mfYM5moYoh0PAcp9XA5Sm4KrV
# LeIzZ3HIy4EzLCbFBP+OGFpkqq8pTtmYItG+g1rYEg5a8egrY83zJMHazaTFBgRI
# MNXCgeMZhC8O6NsAtbj3FSbiYKg1hNwZzHYL+uL3jcPZjuUoOpmvXu67xWs4ZfdT
# Mluy5E7FyWwtnOjr/04EXWyKATYMDIkd47Wqam/ZB7umF5T5YPnmTlv18ArEXuVQ
# EEpS/cN90DtRz2OGruu+V9bg3fk6NKDJLve8detDOTTBN0C/bFGxI5YLHmwVAdaq
# pz3t14ShRjVcxP7aN0bEL3YOuQvjnjQGe29H6n/MPf8UG4WYMd+a8qIP4HROLJq0
# YJylzYBglqoQeQC/OG+PtWTvL9oByPVYNc+llAuap/xmWSLZgAqPbi+PAfow69Lg
# bppHUCnJhNkXD/mJ4qB0KvPG+bzL19dEMYIbMzCCGy8CAQEwbDBcMQswCQYDVQQG
# EwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTEyMDAGA1UEAxMpR2xvYmFs
# U2lnbiBHQ0MgUjQ1IEVWIENvZGVTaWduaW5nIENBIDIwMjACDF3VjaKNus83AvC1
# UTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkG
# CSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEE
# AYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDId9Y0pTABM4/9oWdDvIsb9J6Kg/qmVLOL
# zRzH66P9DDANBgkqhkiG9w0BAQEFAASCAgB3vbwL+fhbhvD3fSG+3zmqhJNenMEz
# Xv47pn5SlP3W66z3yxOTqlvEsK/4AWCsErRwECFfT5FYB3smfo09Pl1jP7iY5kI3
# K96dLiNfzZdoQYE6+TnmfmmzgnsyhnNDo1X1t6ulvLFvwZ4oeRtrWJ5CS/zeESXt
# CEmSL5uTWZ4CwxicJ7jWnPOMB7EiBHGA/Ybrd5yyvADSCX6T5IV7SlCJV2tAhs+t
# Hid/TmEkiC/ASbZ2dX6vtoQDCfNzx20AMESpuqT4SsBqfUd0DMxVZU6F8mJHxWAG
# AZ8F02C3loILuwUsnr2hipTIc3wkTIHyhswi3X12kPAqa0zWaUOJJg971I9pE8Qh
# JoN8mI7iGcavLUNSj+1FMeeT62XiW+EQ3Kn8Y7hSuatikeXMqn/JV8yIzgys6VEP
# GWAoF5DGd2Yg73qjEBCHz0brKLubHtb5rAopwFaewdNVGaYAR4Y4hbOpNMo7m0o6
# yZy6TG+kFvTv7F1PPBnSIHNTVmu2ZnwdC1QLSFbEfqjimJHxh11bi1fiZhsogJDC
# X35sxBLIW9jPvnDUKdoCmIvkI2m/hVYNxRVCLqaDTUK7UJD2D3uucNdu/1DaZBUe
# 3+lvncZqt60zZe6dtE6/nnmMvDbZKY68dzFqGToK8RVCzAG+OSQZpW7fmF/vHdJv
# qbLJCRVIMQWMFaGCGBEwghgNBgorBgEEAYI3AwMBMYIX/TCCF/kGCSqGSIb3DQEH
# AqCCF+owghfmAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFiBgsqhkiG9w0BCRABBKCC
# AVEEggFNMIIBSQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDRgTSz
# jSWf7B0b0plamxEfjpPfimHlbOFkQvbVXNw0xQIGaqlLHZfzGBMyMDI2MDkyMzEz
# NTcyOS41NTZaMASAAgH0oIHhpIHeMIHbMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRp
# b25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0EwMC0wNUUwLUQ5NDcxNTAz
# BgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3RhbXBpbmcgQXV0aG9y
# aXR5oIIPITCCB4IwggVqoAMCAQICEzMAAAAF5c8P/2YuyYcAAAAAAAUwDQYJKoZI
# hvcNAQEMBQAwdzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjFIMEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlv
# biBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIwMTExOTIwMzIz
# MVoXDTM1MTExOTIwNDIzMVowYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0Eg
# VGltZXN0YW1waW5nIENBIDIwMjAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
# AoICAQCefOdSY/3gxZ8FfWO1BiKjHB7X55cz0RMFvWVGR3eRwV1wb3+yq0OXDEqh
# UhxqoNv6iYWKjkMcLhEFxvJAeNcLAyT+XdM5i2CgGPGcb95WJLiw7HzLiBKrxmDj
# 1EQB/mG5eEiRBEp7dDGzxKCnTYocDOcRr9KxqHydajmEkzXHOeRGwU+7qt8Md5l4
# bVZrXAhK+WSk5CihNQsWbzT1nRliVDwunuLkX1hyIWXIArCfrKM3+RHh+Sq5RZ8a
# Yyik2r8HxT+l2hmRllBvE2Wok6IEaAJanHr24qoqFM9WLeBUSudz+qL51HwDYyID
# PSQ3SeHtKog0ZubDk4hELQSxnfVYXdTGncaBnB60QrEuazvcob9n4yR65pUNBCF5
# qeA4QwYnilBkfnmeAjRN3LVuLr0g0FXkqfYdUmj1fFFhH8k8YBozrEaXnsSL3kdT
# D01X+4LfIWOuFzTzuoslBrBILfHNj8RfOxPgjuwNvE6YzauXi4orp4Sm6tF245Da
# FOSYbWFK5ZgG6cUY2/bUq3g3bQAqZt65KcaewEJ3ZyNEobv35Nf6xN6FrA6jF944
# 7+NHvCjeWLCQZ3M8lgeCcnnhTFtyQX3XgCoc6IRXvFOcPVrr3D9RPHCMS6Ckg8wg
# gTrtIVnY8yjbvGOUsAdZbeXUIQAWMs0d3cRDv09SvwVRd61evQIDAQABo4ICGzCC
# AhcwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRr
# aSg6NS9IY0DPe9ivSek+2T3bITBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9z
# aXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQMHgoA
# UwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUyH7SaoUqG8oZ
# mAQHJ89QEE9oqKIwgYQGA1UdHwR9MHsweaB3oHWGc2h0dHA6Ly93d3cubWljcm9z
# b2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZp
# Y2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5j
# cmwwgZQGCCsGAQUFBwEBBIGHMIGEMIGBBggrBgEFBQcwAoZ1aHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJZGVudGl0eSUy
# MFZlcmlmaWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUy
# MDIwMjAuY3J0MA0GCSqGSIb3DQEBDAUAA4ICAQBfiHbHfm21WhV150x4aPpO4dhE
# mSUVpbixNDmv6TvuIHv1xIs174bNGO/ilWMm+Jx5boAXrJxagRhHQtiFprSjMktT
# liL4sKZyt2i+SXncM23gRezzsoOiBhv14YSd1Klnlkzvgs29XNjT+c8hIfPRe9rv
# VCMPiH7zPZcw5nNjthDQ+zD563I1nUJ6y59TbXWsuyUsqw7wXZoGzZwijWT5oc6G
# vD3HDokJY401uhnj3ubBhbkR83RbfMvmzdp3he2bvIUztSOuFzRqrLfEvsPkVHYn
# vH1wtYyrt5vShiKheGpXa2AWpsod4OJyT4/y0dggWi8g/tgbhmQlZqDUf3UqUQsZ
# aLdIu/XSjgoZqDjamzCPJtOLi2hBwL+KsCh0Nbwc21f5xvPSwym0Ukr4o5sCcMUc
# Sy6TEP7uMV8RX0eH/4JLEpGyae6Ki8JYg5v4fsNGif1OXHJ2IWG+7zyjTDfkmQ1s
# nFOTgyEX8qBpefQbF0fx6URrYiarjmBprwP6ZObwtZXJ23jK3Fg/9uqM3j0P01nz
# VygTppBabzxPAh/hHhhls6kwo3QLJ6No803jUsZcd4JQxiYHHc+Q/wAMcPUnYKv/
# q2O444LO1+n6j01z5mggCSlRwD9faBIySAcA9S8h22hIAcRQqIGEjolCK9F6nK9Z
# yX4lhthsGHumaABdWzCCB5cwggV/oAMCAQICEzMAAABYZc3rP6HX/NIAAAAAAFgw
# DQYJKoZIhvcNAQEMBQAwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGlt
# ZXN0YW1waW5nIENBIDIwMjAwHhcNMjUxMDIzMjA0NjU1WhcNMjYxMDIyMjA0NjU1
# WjCB2zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UE
# CxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVs
# ZCBUU1MgRVNOOjdBMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVi
# bGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEB
# BQADggIPADCCAgoCggIBAJ14NKR2kOz1QJZZ/h2WayRXC6Cm3pIVtci/kDQVIBRV
# LI2SCmqqjeKCCbCTN6bNKyTUju53QjUbmQ19loLq9wJO+3l65aAV6mZjumVV3gzU
# Qi5XVuANPzX48ye115HRIY6/NYIVxKUe8wvsG2jTfrD0scywGQXomwxT+18LOcF9
# GbSLDdWtvl6uW71qd5ETMrcLJE47p0YHi2S/7Qadt+yVXUTxQEPY2ESqQqlAaP79
# cYwS0Qldz2pSo5xn29zDeFO41cqonxR0oXJ1uQhEUaAZlGurMoA8xUq/Ht4Pd0zz
# SxVIr1BNBpi321+XHg9RrdRXGesfyS1QE85OfgVh2zMcCSFZFT06kkjI0OikynXJ
# 6t6R9HOABSaf4Jb3KatFPX82SIyRhr3aYrIZUZe+0ULVIUo8R7wp9ywv8akEMDh0
# kA7BoejV9g9EX7EshLUwAcgGnvtsNRwckw03JpUzSxLkC3go9YDQXSFo9FjmMg7Y
# 40P/Ik1mOnstb2/ah5YyI+R4CJ3U+T/TGJNlYNnu4SlGTQTWwP4bIAbLqGfiF/PV
# r4xDfWmxfdUuDiVbop5irCzvzC255e4pLg9HTeqANGHsMg0J3LNdrDPRzrbu9kAP
# 02ZXn71vK/cbTFwGKEJXBo7JPFuFHO3H6lamyD43aHPGcQcy3F3Fcj+KeXMi8L5D
# AgMBAAGjggHLMIIBxzAdBgNVHQ4EFgQUco1NxXq+FcVyfoYz3ESZ8Z0SCbAwHwYD
# VR0jBBgwFoAUa2koOjUvSGNAz3vYr0npPtk92yEwbAYDVR0fBGUwYzBhoF+gXYZb
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw
# UHVibGljJTIwUlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAyMDIwLmNybDB5Bggr
# BgEFBQcBAQRtMGswaQYIKwYBBQUHMAKGXWh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0
# YW1waW5nJTIwQ0ElMjAyMDIwLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQM
# MAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDBmBgNVHSAEXzBdMFEGDCsGAQQB
# gjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v
# cGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wCAYGZ4EMAQQCMA0GCSqGSIb3DQEB
# DAUAA4ICAQB768SIlRdYh3F78ayUwg/HjrWz1Gyvcfi8+9ImHoYfhtFKDjHDH9OE
# A+rUD03meR4RhE5HJ9K2gyeTaX20QbihB99a8W2Sve6NdikQzSdzbuHj2lCKR/03
# Fs/Dy1TghAssEsWxIBBttJMsnRnOOnOfdbV86X5CQ3JI6GK8qWnq3XkC0UB/S2qE
# 2GX3QMzKPg8tDtQ8xSZBeWJ7JgDPJbfa9TbV3RY3WxTAm1eXWruuJldtrNfOgPFJ
# ovpdt/flHtHGyUCMpFoSJYYVocUIKJLKh8+5KdhhWQAHskk5iDkDlslzgPCQPEyE
# icp9yQzoErb+fNz91dEEgC0+iVwwltuhVdtPFRiTM5L9Ettojk2Sf6Gpk6ZPEcw/
# KFk/KX23tav5P28T4l2EW86bPemuWDpKO59FXaOtQziYS/5oZNNWN+PIKur4Rv1U
# 3HRh4dawpMDjNOQd+QGWavQLT6X0cyYp6x88xkcfq+nw93HGDDzDF/zcHdoq866Y
# de9n0HEn9XzML8CIZnx+BliSfpn9RwOrl6ADlQcnJILl75Y/ID4u09hEukVPPkLL
# H6kzUySo7AYXlilr9luBElXp9i+5Qs+4u/RYQPtJKdMlyZ2MHc1ktKL0yDVCyDZs
# 1cjHElJuVrlX/GGd93ALndjqa+jdfkbi2Q2rw744noZSOXR/hhMuejGCB0Mwggc/
# AgEBMHgwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5n
# IENBIDIwMjACEzMAAABYZc3rP6HX/NIAAAAAAFgwDQYJYIZIAWUDBAIBBQCgggSc
# MBEGCyqGSIb3DQEJEAIPMQIFADAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQw
# HAYJKoZIhvcNAQkFMQ8XDTI2MDkyMzEzNTcyOVowLwYJKoZIhvcNAQkEMSIEIP4A
# LhZ0Qe82cyyqH1Z+W56BCoyHmsmj3a/csA3jzOj4MIG5BgsqhkiG9w0BCRACLzGB
# qTCBpjCBozCBoAQgxSJUu7IH0PsAKNXFm1s/ljevVQXbOfIW62p/dfWi6HEwfDBl
# pGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENB
# IDIwMjACEzMAAABYZc3rP6HX/NIAAAAAAFgwggNeBgsqhkiG9w0BCRACEjGCA00w
# ggNJoYIDRTCCA0EwggIpAgEBMIIBCaGB4aSB3jCB2zELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdBMDAtMDVFMC1E
# OTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5n
# IEF1dGhvcml0eaIjCgEBMAcGBSsOAwIaAxUAnWR5G9unq3BdjutrwnWVH1ErxFyg
# ZzBlpGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5n
# IENBIDIwMjAwDQYJKoZIhvcNAQELBQACBQDuXlWQMCIYDzIwMjYwOTIzMTM0MTM2
# WhgPMjAyNjA5MjQxMzQxMzZaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAO5eVZAC
# AQAwBwIBAAICE38wBwIBAAICErQwCgIFAO5fpxACAQAwNgYKKwYBBAGEWQoEAjEo
# MCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG
# 9w0BAQsFAAOCAQEAsndaVCoNFPLU2VhntMux3iJjLO1vQtTwjEIOFirYc0N77jAY
# HNhEjvAimkVv/MftgZkDTMN0xxfcnGBWQI9f4MV7Bosvdyj1L29Z0Cd4X2HhWpbv
# 0/no624Ng4QwkeKHv1+ESH7cM8nJvqXwN93sdNNGYIkHh17axJTFtTRMNC16/O1v
# EXcXPTzv1rZ03V6b5SpfLENgn/lrrNQoqv2WtQqsG9oIqsUzFmFIuzZo7YOlsOXF
# EXdqsZgft99k2RRLR9YhZCKPC5FJpp8IXNGv8yIpFFplXO66yx9uLoO8DqoX5t7f
# +Rmd3cNpE1YqM2HMKDjWdFQY5OC4ZVjikBkxgTANBgkqhkiG9w0BAQEFAASCAgBD
# 9qZCMXO5w62/Qa1JlIHLYf19zMroE5WYT0eFdonsakGyhqkfIyWTMvwojCq1uGw4
# xl+9h/EjpyhD+cJGPqYiOF39a57HKktyq+Zo7KpkuysSV3RRy7MP/5hItl8Fi8uO
# QWqvnN5Tw00vuhBd3cJhBzc4TrMG/E71EkvB+xnyeQgv/gp5KrVrjnEq2ZFV5Dat
# VQ07TKTIsHTD3ObBzVshvYjaTXH3chmnWX40YYaXyzXoWTowxtujnBdkp+ejS+Fk
# Wtm7qwXm+dBFhmeewofvCoRVjirXFsokqwauVbcOX8mcnPbJIvCcq8HhUXjiN7+H
# wkkbfS1JkmqwmxZYd2tGZUP2vji2d+dK8rMVVdOCszzhSarORGIcZDnN2mLFM71L
# fSiUJstOyfpE1K9OUROGsMLrBRBF7pqD7bwzRgoIohfatzXI0RmTXGTyrcf2vOZe
# 5Nhvx+IgghAr4vUyqc7qbYpYjjiRu00ExdxoDaCAWihEqDcv8aZ2t21n6D7H0Y6v
# qxKEgXU4PxAPOFfLyi60RkA7eFIth26BDDc33GSPcx+89zTp2ZI/OECx9tnkeCXW
# Yu9kNWZbHAmBc3IlkWKHxdnCigigFrzN8z5ekfFrbOEE21B43f2bb0pDQ40IGVFa
# ys19ATex3W/tvNC+R/C9Sh/4+qQQ435hUroq2lt6RQ==
# SIG # End signature block