Private/GitHub/Request-AvmAppInstallation.ps1
|
function Request-AvmAppInstallation { <# .SYNOPSIS Ensure a pull request requests the AVM app installations for a repository. .DESCRIPTION Checks each app configuration file on the operations repository's main branch. When the repository is listed in all of them, returns 'installed'. Otherwise an open pull request that names the module is reused ('pending'). If none exists, the change is committed to a branch in the caller's fork, which is created when missing, and a pull request is opened ('requested'). Re-running reuses an existing fork, branch, or pull request. .PARAMETER Repository Module repository name without the organization. .PARAMETER ModuleName Module name, for example avm-res-network-virtualnetwork. .PARAMETER OperationsRepository Repository holding the app configuration files, as owner/name. .PARAMETER ConfigurationPath App configuration files that must list the repository. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] [string] $Repository, [Parameter(Mandatory)] [string] $ModuleName, [string] $OperationsRepository = 'microsoft/github-operations', [string[]] $ConfigurationPath = @( 'apps/azure/azure-verified-modules.yaml', 'apps/azure/terraform-cloud.yaml' ) ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $missing = @(Get-AvmAppInstallationMissingConfiguration -Repository $Repository ` -OperationsRepository $OperationsRepository -ConfigurationPath $ConfigurationPath) if ($missing.Count -eq 0) { return [pscustomobject]@{ Status = 'installed'; PullRequest = $null; Files = @() } } $boundary = '(?<![A-Za-z0-9-])({0}|{1})(?![A-Za-z0-9-])' -f [regex]::Escape($Repository), [regex]::Escape($ModuleName) $query = "repo:$OperationsRepository is:pr is:open `"$ModuleName`"" $search = Invoke-AvmGitHubApi -Endpoint ('search/issues?per_page=50&q=' + [uri]::EscapeDataString($query)) $open = @( foreach ($item in @($search['items'])) { if ($item -is [System.Collections.IDictionary] -and (([string]$item['title']) -match $boundary -or ([string]$item['body']) -match $boundary)) { $item } } ) if ($open.Count -gt 0) { return [pscustomobject]@{ Status = 'pending'; PullRequest = [string]$open[0]['html_url']; Files = [string[]]$missing } } if (-not $PSCmdlet.ShouldProcess($OperationsRepository, "Open an app installation pull request for $Repository")) { return [pscustomobject]@{ Status = 'planned'; PullRequest = $null; Files = [string[]]$missing } } # Creating a fork returns the caller's existing fork, even a renamed one. try { $fork = Invoke-AvmGitHubApi -Method POST -Endpoint "repos/$OperationsRepository/forks" -Body @{ default_branch_only = $true } } catch [AvmGitHubException] { $login = [string](Invoke-AvmGitHubApi -Endpoint 'user')['login'] $fork = Invoke-AvmGitHubApi -Endpoint "repos/$login/$(($OperationsRepository -split '/')[-1])" -AllowNotFound if ($null -eq $fork -or $fork['fork'] -ne $true -or $fork['parent'] -isnot [System.Collections.IDictionary] -or [string]$fork['parent']['full_name'] -ne $OperationsRepository) { throw } } $forkName = [string]$fork['full_name'] $forkOwner = [string]$fork['owner']['login'] $branch = "chore/app-install-avm/$ModuleName" $sha = [string](Invoke-AvmGitHubApi -Endpoint "repos/$OperationsRepository/git/ref/heads/main")['object']['sha'] for ($attempt = 1; ; $attempt++) { try { if ($null -eq (Invoke-AvmGitHubApi -Endpoint "repos/$forkName/git/ref/heads/$branch" -AllowNotFound)) { $null = Invoke-AvmGitHubApi -Method POST -Endpoint "repos/$forkName/git/refs" ` -Body @{ ref = "refs/heads/$branch"; sha = $sha } } break } catch [AvmGitHubException] { # A new fork is created asynchronously and rejects Git data calls until it is ready. if ($_.Exception.Message -match 'Reference already exists') { break } if ($attempt -ge 10 -or $_.Exception.StatusCode -notin @(404, 409, 422)) { throw } Start-Sleep -Seconds 3 } } $message = "chore: app install avm $ModuleName" $encodedBranch = [uri]::EscapeDataString($branch) foreach ($path in $missing) { $file = Invoke-AvmGitHubApi -Endpoint "repos/$forkName/contents/$($path)?ref=$encodedBranch" $text = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String(([string]$file['content'] -replace '\s', ''))) $update = Get-AvmAppInstallationListContent -Content $text -Repository $Repository if ($update.Changed) { $body = @{ message = $message content = [System.Convert]::ToBase64String([System.Text.UTF8Encoding]::new($false).GetBytes($update.Content)) sha = [string]$file['sha'] branch = $branch } $null = Invoke-AvmGitHubApi -Method PUT -Endpoint "repos/$forkName/contents/$path" -Body $body } } $head = "$($forkOwner):$branch" try { $pullRequest = Invoke-AvmGitHubApi -Method POST -Endpoint "repos/$OperationsRepository/pulls" -Body @{ title = $message head = $head base = 'main' body = "This PR requests the Azure Verified Modules app installations for https://github.com/Azure/$Repository ($ModuleName)." } $url = [string]$pullRequest['html_url'] } catch [AvmGitHubException] { if ($_.Exception.StatusCode -ne 422) { throw } $existing = @(Invoke-AvmGitHubApi -Endpoint ( "repos/$OperationsRepository/pulls?state=open&head=" + [uri]::EscapeDataString($head))) if ($existing.Count -eq 0) { throw } $url = [string]$existing[0]['html_url'] } return [pscustomobject]@{ Status = 'requested'; PullRequest = $url; Files = [string[]]$missing } } # SIG # Begin signature block # MIInNwYJKoZIhvcNAQcCoIInKDCCJyQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC+fzajhTEv0ALw # djUaDg7Nyf6oZZmZdSrrz9w9vT6Og6CCDMkwggYEMIID7KADAgECAhMzAAACHPrN # xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1 # OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP # oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC # /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf # rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j # qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT # xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT # DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw # YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl # MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC # AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN # rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK # 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK # Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY # BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu # uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE # msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz # 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6 # U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO # 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD # 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC # EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX # DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ # Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq # lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo # 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv # QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a # 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1 # FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO # GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7 # ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ # uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS # CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm # VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3 # SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E # BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX # LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB # Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP # oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw # TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC # AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D # 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY # nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI # vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6 # aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w # PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7 # RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK # /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK # YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw # YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT # Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghnEMIIZwAIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwLwYJKoZIhvcNAQkEMSIEINA5GzFnbjX07EBWaUbA9uVXronT # tZQnZAP6467hj1LUMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBv # AGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAE # ggEAuLZayvghI1FDz6n4aJIvaVrVVG/dkiqMTK7ShExjsGBTxBGB1bV27U6rdpCd # NhXc/Sb+xwsf3XqwvPi8nTDDIAZDEFIzklB9iMyGq0AZa30eidQZmOptf/zoclqK # ROpMgVDag8fsJ885tsmARD1qXlNFwAjcqjgiCzKuS1FucRDPHxyq+AK9tTSj4jxL # QmX0wLB3+av70MWdWbwd1VS0VIHlJkhjTeFBFFHfAzcbJD76N8G+r4bOnQLVgqeJ # /obbMFESVZH8Rw7Pr9R5Gkj4gjO9JwMgB89zbyPHleN+sZWpC/wban9NeIfLNm7m # 5nQf3fiFHMIBnHXA+7cN6mtXCaGCF5QwgheQBgorBgEEAYI3AwMBMYIXgDCCF3wG # CSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsqhkiG # 9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQC # AQUABCAIzpR5W5EstvoAHyuLWDZnXDugDsgDnnKUuy5EtSrPUwIGatlMwKQ2GBMy # MDI2MTExMDExNTgyMi4yNDhaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OEQwMC0wNUUw # LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WgghHq # MIIHIDCCBQigAwIBAgITMwAAAiorAhXd3Fx0NAABAAACKjANBgkqhkiG9w0BAQsF # ADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNjAyMTkxOTQwMDla # Fw0yNzA1MTcxOTQwMDlaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OEQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDzPfCBALZdNkf52bWmCNH/CWVuEUUcOXzDCYR98YuodL3s # Wlcm1NQjyKSzgLnAC4iWnX+e7eOtEg/poePlCbaxn7DDHIs+C7ApiXeYJnJpDzgo # xpWN94QAphh4SYs+rWzZcqtA4q0O0Q+4CXVXlTjIbgNdO0UDlqeM2bI++zEd1rmX # 46mCPtFo0N9U0d8/PsPEyn1llTU9q7LGVChCz/Q5od8YPzqOT+f4OjBw7PDsKS2D # 8EzdEpn25ppjrx4lyAGexF87QeWkD1cmTn1SVtjQXJvj8LVYstVeA/k6Y7IE8ZD1 # P62BiuQIBrqF7B3IlddpNelDgnn+5H9XQMoW5rcYh1g1lSh/4/yuulwT2uSufvxI # vLV6NGgnl5LlPU0ZlRMHMDzadU5jPcUIQqXK+FlrR966FcWq+UQFcyZPKIJHrKQi # yryIr6KcDWp27+ADsHJKMASE1QBqZfqgQZhmUXmyXZR+cazdJmIsdAJrwrpRXS9E # 2HIs+FR+xaYB+GVJ9fmtHnTaYT5LNhGURFx02QPpl96817tFGM4vrovYH00qCBus # zUALOFADT9VL+1IJoWM9DsRtxWsqE1apM/ou4PII6gQEjP9YtQQmSpUSolpXNk79 # xE23YpdedBJs8Gx9T9bqmukezNlpHsc4LBufwdAprRJNWMw0Q2pNfERsj1FT+QID # AQABo4IBSTCCAUUwHQYDVR0OBBYEFBiNf4XD+sZ0GWEhgDttwfhUWzosMB8GA1Ud # IwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRp # bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYI # KwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMv # TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1Ud # EwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeA # MA0GCSqGSIb3DQEBCwUAA4ICAQCbKTsUJNVDrY32V6UTxoOzvAMEMmwQB8Os/umq # k5xmjohVswdpPcJyxg4nZOgSFuCVdol5EhF6bQcLuJWZDaNIit7zV29rqDS42sgl # e67tcQWP3IrEIlNh36N9wygQOnue8NW3UFghGTODnKT3J0oGJ/8MYX9D7gMgpANc # 3d48WjVZ0cbscVK7LdeficY+ui9Rsi2JvdG7IfD9GDoSlXgx/qO+8AnuwF4ZBPNQ # QzTe0wVifVIDVw0BBDlb1xyx5OsubuOxJMBUcCpJRTImftz8jp80+cnNCoppLeoh # mJlcAUMDwFqbYvURXCokThtHYFZrUdyj/HiKNHdBVARd44aXAkbiE9KAo13Ldd// # CWgiOzUxs73as2j1J3wr7xnS7HzJak5vlqrYHjQ1nGuicmsNJYaCLkEiHJvSQMfX # y4oYLFLXDrYMTSz/6ogSaUF4EtPJbqDT1DxuOXneSCBprO+EsZ4zeY3JcmHvF+r9 # ZJ5JSNToPNy6Ya/u8U5ftR+/noGjieiXwnvYaBEnT86Iv7hEeIbPlaa5GFDHY9NH # hGTSN1oHfTsvXvSXA6V6jw3yZczCLGLe2LxVsdjh5RYJhkX7GeqiIlZgeoWOo8+0 # Jr+I+hB26BTOc50IjreOrTDBHyyt4wsyrxwBnTfgJV6ACJnw/Dx1Qj6hHNf1kW9R # w+M1UDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcN # AQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAw # BgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEw # MB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk # 4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9c # T8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWG # UNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6Gnsz # rYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2 # LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLV # wIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTd # EonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0 # gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFph # AXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJ # YfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXb # GjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJ # KwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnP # EP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMw # UQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggr # BgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw # DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoY # xDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp # L2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYB # BQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0B # AQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U5 # 18JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgAD # sAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo # 32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZ # iefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZK # PmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RI # LLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgk # ujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9 # af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzba # ukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/ # OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNNMIIC # NQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 # b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh # dGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUG # A1UECxMeblNoaWVsZCBUU1MgRVNOOjhEMDAtMDVFMC1EOTQ3MSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQAz8Eo/ # z8JJc4wkBjU+FEYBPjSq/aCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD # QSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7p1+WDAiGA8yMDI2MTExMDExMjgyNFoY # DzIwMjYxMTExMTEyODI0WjB0MDoGCisGAQQBhFkKBAExLDAqMAoCBQDunX5YAgEA # MAcCAQACAgwtMAcCAQACAhKGMAoCBQDuns/YAgEAMDYGCisGAQQBhFkKBAIxKDAm # MAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcN # AQELBQADggEBAE2ondLsvbaG78mp+c1iuaLKOgoQvnKo1TMfNEtKxHlBFYIwOEnk # bw/c1+zC0AzDDZH53ZsDDXeqfpyqp4AcAPdHm8dEfXS/DlFV0+BRYrSrn471DG1K # aXKQKvL4E29AveN9ABL6uwSiqdnd/HGOdFcMTCGfSEDruw4yJKpoYSDPlH2Bh0pa # UT/ImROm8HtEF4nhXXt/i7WyZgQ6MBLl3eWiNcLKPI/uVQDhIQNfU/M1cql1HOvA # Y5I/wM2VD3rA5sJJY6d4XjRbsqR4U+b8/EFIKHEJqC1W/mj8KsxOusWpEEgp2OBD # bI5DyIt9zRUKEsXImABKx3InEt1RDZOsv04xggQNMIIECQIBATCBkzB8MQswCQYD # VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe # MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3Nv # ZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiorAhXd3Fx0NAABAAACKjANBglg # hkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqG # SIb3DQEJBDEiBCBfwKrMmniOrmu2W2K/vMVCBpk+dd5vL3v0ozdwSx/TBDCB+gYL # KoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIMpLg2mURY/WrRXyRt5mfX47Yh2RYxet # FfiHOLICI/zlMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAC # EzMAAAIqKwIV3dxcdDQAAQAAAiowIgQgYR3GWkThe4Wjv/kPSabH7hmoVdRVd60f # gaXaXjuKaaIwDQYJKoZIhvcNAQELBQAEggIAXayhzmPFpyg6zcOwYbs2UHUa0Kg7 # Rul9/eqNQyzaJd2KWiFoo6kEOaj/63nLI+Tb0FkqboXA4bAo4Mg9rJnk6vV6QLns # w2oI49bzO0kXZmhKTdjkDNmXk+k14zDnD64BKH1er8thbsIcDB50cRipLKDzdgJH # 7w4CK/lNMOLX3zxkZXbhJjg5UgDyBRJVU6ryCqX8jwG9QCAzaCnTgl6I9+33oLkY # CKm9EPGtyaPQahsRQZbFIDagTPgrze1plVdi1QTYWitll6mtI2zsiSYb5ohncM8L # THmtQokSa93Ypd3aOiy5QRkbPpuq67FGLnJd+aGM9Lw+3azDU/MtT+mv7ZGe3i6Q # U5AEKjAvYOlDsBO64zicQTgW4zqvtjXvTQD4Hb0bcPwdzrwHD/+ldyZpA9GXSQW0 # bcNDCEiBEhoVRHy163ZRzbaq1djBimyQCAVe3qx/56wx/v/tE9kWAYSVlKWPT0he # yNjh2oJHMnhrY7gOzWF1mZ8nki9jEW8jm/sj/t30XCbFATrTYzeLhKzjjRCq9lzl # g4ODM8cpXoqjG8svD2IUi8pecMDjqDTc8qZNMDprwL9V6DJQksHNZ5euvkZfOgoI # 7WSMwVmLYKo8T6dCclvhgXJIOZmYuTpsuXjMBtZXVBOT4gNtp6QoOCbNoqdrFDhe # 3dQFF2I78AGFP9s= # SIG # End signature block |