ResourceManager.psm1

#-----------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
#-----------------------------------------------------------------------

function Get-AzsResourceManagerAccessToken {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [object] $context
    )

    $profile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile

    $profileClient = [Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient]::new($profile)

    $token = $profileClient.AcquireAccessToken($context.Subscription.TenantId)

    return $token.AccessToken
}

function Invoke-AzsResourceManager {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet('GET', 'PUT', 'POST', 'DELETE', 'HEAD', 'OPTIONS', 'TRACE')]
        [string] $Method,

        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [Uri] $Uri,

        [Parameter(Mandatory = $false)]
        [object] $Body = $null,

        [Parameter(Mandatory = $false)]
        [string] $AccessToken = "",

        [Parameter(Mandatory = $false)]
        [switch] $ThrowOnError
    )

    function Resolve-RequestUri {
        param (
            [string] $resourceManagerUrl,
            [Uri] $uri
        )

        if ($uri.IsAbsoluteUri) {
            return $uri
        }

        return [uri]::new([uri]::new($resourceManagerUrl), $Uri)
    }

    function Resolve-RequestContent {
        param (
            [object] $body
        )

        if ($null -eq $body) {
            return [NullString]::Value
        }

        if ($body -is [string]) {
            return $Body.ToString()
        }

        return ($body | ConvertTo-Json -Depth 99 -Compress)
    }

    function Resolve-AccessToken {
        param(
            [object] $context,
            [string] $accessToken
        )

        if (-not [string]::IsNullOrEmpty($accessToken)) {
            return $accessToken
        }

        return Get-AzsResourceManagerAccessToken -Context $context
    }

    function Get-HeaderValue {
        param (
            [System.Net.Http.Headers.HttpHeaders] $headers,
            [string] $name
        )

        [System.Collections.Generic.IEnumerable[string]] $values = $null

        if (-not $headers.TryGetValues($name, [ref] $values)) {
            return [NullString]::Value
        }

        return [System.Linq.Enumerable]::FirstOrDefault($values)
    }

    function Trace-HttpRequestMessage {
        param (
            [System.Net.Http.HttpRequestMessage] $request,
            [string] $content
        )

        Write-Verbose "$($request.Method) $($request.RequestUri) with $($content.Length)-char payload" -Verbose

        $sb = [System.Text.StringBuilder]::new()
        $sb.AppendLine("$($request.Method) $($request.RequestUri) HTTP/$($request.Version)") | Out-Null

        DumpHttpMessageHeaders $sb $request.Headers

        if (-not [string]::IsNullOrEmpty($content)) {
            $sb.AppendLine() | Out-Null
            $sb.Append($content) | Out-Null
        }

        Write-Debug $sb.ToString()
    }

    function Trace-HttpResponseMessage {
        param (
            [System.Net.Http.HttpResponseMessage] $response,
            [string] $content
        )

        Write-Verbose "Received $($content.Length)-char response, StatusCode = $($response.StatusCode)" -Verbose

        $sb = [System.Text.StringBuilder]::new()
        $sb.AppendLine("HTTP/$($response.Version) $([int]$response.StatusCode) $($response.ReasonPhrase)") | Out-Null

        DumpHttpMessageHeaders -Sb $sb -Headers $response.Headers

        if (-not [string]::IsNullOrEmpty($content)) {
            $sb.AppendLine() | Out-Null
            $sb.Append($content) | Out-Null
        }

        Write-Debug $sb.ToString()
    }

    function DumpHttpMessageHeaders {
        param (
            [System.Text.StringBuilder] $sb,
            [System.Net.Http.Headers.HttpHeaders] $headers
        )

        if ($null -ne $headers) {
            foreach ($header in $headers) {
                $sb.Append($header.Key) | Out-Null
                $sb.Append(": ") | Out-Null

                if ($header.Key -eq 'Authorization') {
                    $sb.AppendLine('HIDDEN') | Out-Null
                }
                else {
                    $sb.AppendLine($header.Value -join " ") | Out-Null
                }
            }
        }
    }

    #-----------------------------------------------------------------------

    $ctx = Get-AzContext

    if ($null -eq $ctx.Environment) {
        throw 'Az Context is not set.'
    }

    $Uri = Resolve-RequestUri -ResourceManagerUrl $ctx.Environment.ResourceManagerUrl -Uri $Uri

    [string] $requestContent = Resolve-RequestContent -Body $Body

    $AccessToken = Resolve-AccessToken -Context $ctx -AccessToken $AccessToken

    [System.Net.Http.HttpRequestMessage] $request = $null
    [System.Net.Http.HttpResponseMessage] $response = $null

    try {
        $request = [System.Net.Http.HttpRequestMessage]::new()
        $request.Method = [System.Net.Http.HttpMethod]::new($Method)
        $request.RequestUri = $Uri
        $request.Headers.Authorization = [System.Net.Http.Headers.AuthenticationHeaderValue]::new('Bearer', $AccessToken)

        if ($null -ne $requestContent) {
            $request.Content = [System.Net.Http.StringContent]::new($requestContent, [System.Text.Encoding]::UTF8, 'application/json')
        }

        Trace-HttpRequestMessage -Request $request -Content $requestContent

        $task = $HttpClient.SendAsync($request, [System.Net.Http.HttpCompletionOption]::ResponseContentRead)
        $response = $task.Result

        $task = $response.Content.ReadAsStringAsync()
        [string] $responseContent = $task.Result

        if ([string]::IsNullOrEmpty($responseContent)) {
            $responseContent = [NullString]::Value
        }

        Trace-HttpResponseMessage -Response $response -Content $responseContent

        $result = [psobject]::new()
        $result | Add-Member -MemberType NoteProperty -Name 'StatusCode' -Value $response.StatusCode
        $result | Add-Member -MemberType NoteProperty -Name 'AsyncOperationStatusUri' -Value (Get-HeaderValue -Headers $response.Headers -Name 'Azure-AsyncOperation')
        $result | Add-Member -MemberType NoteProperty -Name 'LocationUri' -Value (Get-HeaderValue -Headers $response.Headers -Name 'Location')
        $result | Add-Member -MemberType NoteProperty -Name 'Content' -Value $responseContent

        if ($ThrowOnError) {
            Ensure-SuccessStatusCode -Response $result
        }

        return $result
    }
    catch [System.AggregateException] {
        throw $_.Exception.InnerException.Message
    }
    finally {
        if ($null -ne $request) {
            $request.Dispose()
        }

        if ($null -ne $response) {
            $response.Dispose()
        }
    }
}

function Ensure-SuccessStatusCode {
    param(
        [Parameter(Mandatory = $true)]
        [psobject] $Response
    )

    if (-not (IsSuccessStatusCode -StatusCode $Response.StatusCode)) {
        Write-Verbose "HTTP error: $($Response.StatusCode)" -Verbose
        Write-Verbose $Response.Content -Verbose

        throw "HTTP error: $($Response.StatusCode)"
    }
}

function IsSuccessStatusCode {
    param(
        [Parameter(Mandatory = $true)]
        [System.Net.HttpStatusCode] $StatusCode
    )

    return [int]$StatusCode -ge 200 -and [int]$StatusCode -le 299
}

#-----------------------------------------------------------------------

$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'

[System.Reflection.Assembly]::LoadWithPartialName('System.Net.Http') | Out-Null
[System.Net.Http.HttpClient] $HttpClient = [System.Net.Http.HttpClient]::new()

Export-ModuleMember -Function @('Get-AzsResourceManagerAccessToken', 'Invoke-AzsResourceManager', 'Ensure-SuccessStatusCode')

# SIG # Begin signature block
# MIIjkQYJKoZIhvcNAQcCoIIjgjCCI34CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD1kvATvb+TFzSJ
# WN1JMzQtPTxYt4WPv8aVboS5QuUDIKCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZjCCFWICAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgU5XhnGvl
# 15Xbc2WH4pHZrBMe7J+ezwZK/L9/jlFw9oIwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQAjB4EIGScU7dNXFdJ9/txs+ExH1Y7kCq9Zuo3sBHHj
# ZWNS2xjhCVCNOcUmoKKUjWE63kbcFhsKfLHUSCjjXxOyO8mqbShYwnAW+EpGGD8L
# Ev8tIbtv0GN6TBB3ACWtKqM657DDL9TTnC42gV7z2GwO3gkz4/16n1o30sP9erwu
# iaQmF0wBQiVbSwZCiRv+yCsp5gXMJsfcgsTQGHQFPpgiAiwPseeL5hIleggmvjqG
# k2zL7Ti+HU3LAXdWwTL+9+q3k8l/voybvm+FFCsg2yGZv14DLXLuNtkqVKkgrvxv
# gttN0o3eCTd7FFXQr1paskEgHgaCiLpgg+n0ZyeQ1kSjoYIS8DCCEuwGCisGAQQB
# gjcDAwExghLcMIIS2AYJKoZIhvcNAQcCoIISyTCCEsUCAQMxDzANBglghkgBZQME
# AgEFADCCAVQGCyqGSIb3DQEJEAEEoIIBQwSCAT8wggE7AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIHRmsIYZFmW46XsymTUrWzxHO692dU0GXWaUuX6T
# qF2FAgZe1Ucl7dkYEjIwMjAwNjEzMTgzNDUwLjI3WjAEgAIB9KCB1KSB0TCBzjEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWlj
# cm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBU
# U1MgRVNOOkQ5REUtRTM5QS00M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T
# dGFtcCBTZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAEtLk1BymNlM6AAAAAA
# AS0wDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp
# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw
# b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw
# HhcNMTkxMjE5MDExNTA0WhcNMjEwMzE3MDExNTA0WjCBzjELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJh
# dGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQ5REUt
# RTM5QS00M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqWGN9HVJOphFNLezSLiM
# hxIxX4bg3ShiDCVAr6mXgNUwWavWJNkcUuPdO3tXoX4z8FfHSH2LS67XIGeoKo06
# S4kRFNtWczi7cm9HHOxB8KMF+oP8I3Cgw16SjKUgHPE/nRKSOnWk+ydAEodoI/y2
# C69hXOhNDnirZWlgQ//45hNB4Q+pYWLxaCC+xyS56txQaSFhYzwNX/glTs3+tsOu
# 1qZht7wu2RWJNBhuKBRIICiv0OG0Bm+rwDQDvdcUfZ1/bAOUu0CcoJyxW9dKZnfl
# sCqd43i4RBXLw1B1F4YjW0jpTGgLteeMa8rgxwN0qFq80nsMCdI/n4b8NOR1YP6U
# 3wIDAQABo4IBGzCCARcwHQYDVR0OBBYEFNlsZHxCASH4Tg6K/y9DvjTynYbNMB8G
# A1UdIwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeG
# RWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rp
# bVN0YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUH
# MAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3Rh
# UENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYB
# BQUHAwgwDQYJKoZIhvcNAQELBQADggEBAH1rA2T3Tqt5wG6g7sTOrsHxQa70SgVe
# IvxpzRtuxkMFj3P/enxq1VlELEc3jdrdYZsIxmgpjFIEfpQKRwNzBch80oRtUzOc
# XWaOlAQZuqFH6s82oddmi1JX6+fzkDGZ58Azdtwu35Y0GkS45lniQ9lVwW0yjhjJ
# CPGg1E+LCan4HQeSFCz+X9UiDRcljqTkVEoT8kURxVjjbno12pRn7eOi7dvg4CS0
# Ta6uvhXpVHDV9986XFlvwzK8Tmaq9NUk5K1XEK6G6qDWsh7yVrWBnmKK4jJZ5+04
# 18zDSdJlyEYuKAN4ifDpd/DCAWcLlCXw9t/aM7EJSW4BhvPKQj4ycGIwggZxMIIE
# WaADAgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0y
# NTA3MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RU
# ENWlCgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBE
# D/FgiIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50
# YWeRX4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd
# /XcfPfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaR
# togINeh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQAB
# o4IB5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8
# RhvFM2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIB
# hjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fO
# mhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9w
# a2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggr
# BgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSAB
# Af8EgZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEF
# BQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBt
# AGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Eh
# b7Prpsz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7
# uVOMzPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqR
# UgCvOA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9
# Va8v/rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8
# +n99lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+
# Y1klD3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh
# 2rBQHm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRy
# zR30uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoo
# uLGp25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx
# 16HSxVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341
# Hgi62jbb01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9w
# ZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkQ5
# REUtRTM5QS00M0ZFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2
# aWNloiMKAQEwBwYFKw4DAhoDFQCfzl/Hfod7sXS+CbJSXPbDzaXQsaCBgzCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA
# 4o+XZTAiGA8yMDIwMDYxMzIyMjAyMVoYDzIwMjAwNjE0MjIyMDIxWjB3MD0GCisG
# AQQBhFkKBAExLzAtMAoCBQDij5dlAgEAMAoCAQACAh1sAgH/MAcCAQACAhECMAoC
# BQDikOjlAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA
# AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEACdMbLqz4weDjSNOz
# du9mOu9vXJTARMsrB+mojldp/qQLz/7gddxwuis6cw5YSbv3Y/8GjVwv83AKNTEa
# PwozUAUzlzFgBrY4TtKUHS5rXywemCMmm6hWVB9+59die+LZx7VZnglZEHrRzteS
# FcEy3QfwjCdjDDVdP2Iptvc3rS0xggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBQQ0EgMjAxMAITMwAAAS0uTUHKY2UzoAAAAAABLTANBglghkgBZQME
# AgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJ
# BDEiBCDvRgMOaQ1eMdgn6nPITM6AdZwtxbur5y/tIIEQqzK+/DCB+gYLKoZIhvcN
# AQkQAi8xgeowgecwgeQwgb0EII7xWnJyfSAHj+KVEA88NtL4KZuqP+4LTXWahzmh
# 4YPBMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEt
# Lk1BymNlM6AAAAAAAS0wIgQg3Zmi0Tiu5Ewp+XWfzNSVBXUqnuJWz/VO+GkxT9pE
# FQwwDQYJKoZIhvcNAQELBQAEggEACrRrtpzD/n2ggAh3bE6SE+V6OF5jRbbslcTH
# Thvtai03+G0tuT9x6O+f2BdwBHz5jUtfcpO7TQCMEHgzqVZXpWcMErD+hDSl0F8w
# QYVXzYSYF0JP4u8qzSJ5DQhPglbDOkdt78TqfZijzP7IompPVM7opiT2WULJMhou
# KVXYZR+7F8AvujFXnCP0hFoBJdLD9t9risQJH2VpoUCwGJEeTDaHBIuRDRxfvbC0
# YoRMrpnEstj00P4Dx9i6cfNRO5x6QYhVm02GeZmxBRLDEQex3MW/ttsl/MuTt7B8
# EAWbCho2dzSdAL0Tu8TKA9GlAeQsPQUxslg3SUkTtoRh4/g/3A==
# SIG # End signature block