Public/Invoke-AvmTestE2e.ps1
|
function Invoke-AvmTestE2e { <# .SYNOPSIS Run Bicep or Terraform deployment tests with ordinary cleanup. .DESCRIPTION Bicep discovers tests/e2e/**/main.test.bicep and compiles temporary ARM templates without editing source. Existing Azure PowerShell and Azure CLI sign-ins must identify the same account, tenant, cloud and selected subscription. Required Az modules are checked, not installed. Native ARM validation and deployment support resource-group, subscription, management-group and tenant scope, including nested and cross-scope module deployments. Select authorized test targets: resources created or updated by the test can be removed afterwards. Successful deployments run their case-local Pester assertions, then post.ps1, then native cleanup adapted from the registry workflow. Assertions and hooks run in the same PowerShell process to retain process-only sign-ins. Caller or workflow cancellation applies; no separate-process timeout or credential transfer is used. A failed assertion or hook still runs ordinary cleanup. An authored assertion suite must have passing tests and no failed, skipped, filtered, inconclusive or setup-failed tests. Absent suites are 'not-present'. Cleanup follows recorded Create deployment operations recursively, not Read references or a subscription-wide inventory. Resource-group cases use a unique group and verified ownership tag. Attempt IDs and required post-removal metadata are saved in an atomic local JSON file before submission or removal. The state contains no credentials, parameters or outputs, and survives temporary-template removal. Incomplete cleanup reports pending IDs and stops subsequent cases. The reaper is a fallback, not the normal cleanup mechanism. All runs the complete lifecycle. Deploy retains resources and state for a separate Complete call after caller-owned sign-in renewal. Complete requires matching direct case/module source, assertion files and post hook, plus explicit subscription/tenant. It rereads outputs by exact deployment ID and never submits a deployment. The source check does not fingerprint every imported helper or dependency. Interrupted completion must use avm test cleanup rather than replaying authored scripts. Actions callers may upload state as an artifact; recovery requires that the upload finished before runner loss. KeepResources runs assertions but deliberately skips post.ps1 and cleanup. The saved file can later be passed to avm test cleanup, which does not need the checkout and never runs assertions or hooks. Terraform runs init, apply, idempotency plan and destroy for runnable examples/. MaxRetry controls Terraform transient apply retries. No deployment tests belong in avm pr-check or the local build gate. Listing does not resolve tools or credentials. An absent tier is 'skipped', not a pass; .e2eignore excludes a case. .PARAMETER Path Module directory, or an enclosing context. Defaults to the current location. .PARAMETER Ecosystem Explicit ecosystem, or auto-detection. .PARAMETER AllowPathFallback Accept a PATH tool only when it reports the lock-pinned version. .PARAMETER Example Select case names or root-relative Bicep paths. Explicit ignored, absent or ambiguous selections are errors. .PARAMETER List Return a JSON array of runnable example names without cloud access. .PARAMETER MaxRetry Terraform-only transient apply retry budget, default two retries. .PARAMETER Recurse Bicep-only: include child-module test cases. .PARAMETER SubscriptionId Bicep-only: explicit test subscription GUID. Ambient defaults are not used. .PARAMETER TenantId Bicep-only: explicit tenant GUID required for every deployment scope. .PARAMETER ManagementGroupId Bicep-only: existing target management-group name for that scope. .PARAMETER Location Bicep deployment-metadata location, fixed across regional retries. Also the placement fallback for explicitly global resource types. .PARAMETER ResourceLocation Pin Bicep resource placement. Must agree with parameter/token locations. .PARAMETER ResourceGroupPrefix Required for resource-group Bicep cases; up to 57 safe name characters. .PARAMETER TokenFile JSON string-valued tokens, relative to the module root or absolute. Mutually exclusive with Tokens. Explicit scope parameters own scope tokens. .PARAMETER Tokens Bicep token dictionary instead of TokenFile. Generated avmE2eRunId and avmE2eSuffix tokens provide a unique default namePrefix. .PARAMETER ParameterFile ARM JSON parameter file, preserving values and vault references. Mutually exclusive with Parameters; source is not changed. .PARAMETER Parameters Bicep parameter values. SecureString values remain in memory. Explicit values or files take precedence over CI inputs. .PARAMETER UseCiInputs Opt into AVM_CI_VARIABLES and AVM_CI_SECRETS JSON, localToken_*, TOKEN_NAMEPREFIX and deprecated CI_KEY_VAULT_NAME inputs. CI secrets beat variables; CI_ aliases beat CI__ aliases within a source. Missing explicit IDs may use VALIDATE_SUBSCRIPTION_ID and VALIDATE_TENANT_ID. Pools resolve VALIDATE_SUBSCRIPTION_IDS then TEST_SUBSCRIPTION_IDS from variables, then secrets, then environment. Nothing logs in. .PARAMETER TestSubscriptionIds Optional Bicep JSON array of id/name objects with an optional tag. Missing tags mean General. Cases use only General subscriptions in a stable seeded ordering, independent of JSON ordering. Registry-layout modules require the exact root .reserved-test-subscriptions.json map. Non-General reservations require the protected registry workflow, not native e2e, so its environment lock covers preparation through cleanup. .PARAMETER SubscriptionSelectionSeed Shared nonnegative seed for deterministic subscription ordering. .PARAMETER SubscriptionJobIndex Nonnegative initial case index for round-robin subscription selection. .PARAMETER Phase Bicep All (default), Deploy, or Complete for caller-owned sign-in renewal. .PARAMETER CleanupStatePath Optional new state file for one selected case. Complete requires an existing file and the explicitly matching subscription and tenant. .PARAMETER KeepResources Bicep-only: run assertions but retain resources, skipping post and cleanup. .PARAMETER DeploymentRetryLimit Bicep total submission attempts, one to three. Only confirmed failure or exact preflight rejection can retry; unknown outcomes never resubmit. .PARAMETER ValidationRetryLimit Bicep total regional validation attempts, one to three. Only wholly regional failures relocate; explicit pins, global resources and resource-group cases do not relocate. Deployment stays in the validated region. .PARAMETER SkipModuleVersionCheck Skip the advisory module-version check. .OUTPUTS Engine result with Status, Issues and run counts. Bicep also returns Phase, AssertionResults, PostResults, CleanupPending, CleanupDeferred and CleanupStatePaths. Deployment outputs and parameters are not returned. .EXAMPLE avm test e2e --list .EXAMPLE avm test e2e --subscription-id $testSubscriptionId --tenant-id $testTenantId --location eastus --resource-group-prefix avm-e2e .EXAMPLE avm test e2e --example defaults --subscription-id $testSubscriptionId --tenant-id $testTenantId --location eastus --phase Deploy --cleanup-state-path cleanup.json .EXAMPLE avm test e2e --phase Complete --cleanup-state-path cleanup.json --subscription-id $testSubscriptionId --tenant-id $testTenantId .EXAMPLE avm test e2e --max-retry 0 --ecosystem terraform #> [CmdletBinding(SupportsShouldProcess)] [OutputType([pscustomobject])] param( [Parameter(Position = 0)] [string] $Path = $PWD.Path, [ValidateSet('auto', 'bicep', 'terraform')] [string] $Ecosystem = 'auto', [switch] $AllowPathFallback, [AllowEmptyCollection()] [string[]] $Example = @(), [switch] $List, [ValidateRange(0, 10)] [int] $MaxRetry = 2, [switch] $Recurse, [string] $SubscriptionId, [string] $TenantId, [string] $ManagementGroupId, [string] $Location, [string] $ResourceLocation, [string] $ResourceGroupPrefix, [string] $TokenFile, [Alias('AdditionalTokens')] [System.Collections.IDictionary] $Tokens = @{}, [string] $ParameterFile, [System.Collections.IDictionary] $Parameters = @{}, [switch] $UseCiInputs, [string] $TestSubscriptionIds, [ValidateRange(0, [int]::MaxValue)] [int] $SubscriptionSelectionSeed = 0, [ValidateRange(0, [int]::MaxValue)] [int] $SubscriptionJobIndex = 0, [ValidateSet('All', 'Deploy', 'Complete')] [string] $Phase = 'All', [string] $CleanupStatePath, [switch] $KeepResources, [ValidateRange(1, 3)] [int] $DeploymentRetryLimit = 3, [ValidateRange(1, 3)] [int] $ValidationRetryLimit = 3, [switch] $SkipModuleVersionCheck ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' if (-not $List) { Test-AvmModuleVersion -SkipModuleVersionCheck:$SkipModuleVersionCheck } $context = Get-AvmModuleContextInternal -Path $Path -Ecosystem $Ecosystem $bicepOptions = @( 'Recurse', 'SubscriptionId', 'TenantId', 'ManagementGroupId', 'Location', 'ResourceLocation', 'ResourceGroupPrefix', 'TokenFile', 'Tokens', 'ParameterFile', 'Parameters', 'UseCiInputs', 'TestSubscriptionIds', 'SubscriptionSelectionSeed', 'SubscriptionJobIndex', 'Phase', 'CleanupStatePath', 'KeepResources', 'DeploymentRetryLimit', 'ValidationRetryLimit' ) switch ($context.Ecosystem) { 'bicep' { if ($PSBoundParameters.ContainsKey('MaxRetry')) { throw [AvmConfigurationException]::new('-MaxRetry is only supported for Terraform e2e tests.') } $inputOptions = @{ Context = $context; AllowPathFallback = $AllowPathFallback; Example = $Example; List = $List } foreach ($name in $bicepOptions) { if ($PSBoundParameters.ContainsKey($name)) { $inputOptions[$name] = $PSBoundParameters[$name] } } if ($List) { Invoke-AvmBicepTestE2e @inputOptions } elseif ($PSCmdlet.ShouldProcess($context.Root, "Run Bicep e2e phase $Phase in explicit test targets")) { Invoke-AvmBicepTestE2e @inputOptions -Confirm:$false -WhatIf:$false } else { Invoke-AvmBicepTestE2e @inputOptions -WhatIf } } 'terraform' { foreach ($name in $bicepOptions + @('WhatIf', 'Confirm')) { if ($name -eq 'Recurse' -and -not $Recurse) { continue } if ($PSBoundParameters.ContainsKey($name)) { throw [AvmConfigurationException]::new( 'Bicep scope, input, phase, cleanup and ShouldProcess options are not supported for Terraform e2e tests.') } } Invoke-AvmTerraformTestE2e -Context $context -AllowPathFallback:$AllowPathFallback ` -Example $Example -List:$List -MaxRetry $MaxRetry } default { throw [AvmContextException]::new("Cannot run e2e tests: unknown ecosystem '$($context.Ecosystem)'.") } } } # SIG # Begin signature block # MIInRAYJKoZIhvcNAQcCoIInNTCCJzECAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDTxoSLaWWCdYPA # HszePfrDyFZhiJpy+NP2v5K8CG6PWaCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghngMIIZ3AIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJ # KoZIhvcNAQkEMSIEIPSjkxvi0evSa0yzUQXH7m8pFW+1zCyXLtO4hYRjSb3fMEIG # CisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAE5ahTfwk2de+rxrB # 8FSGX45DqyJXOh/HLSfQXenz5e6p52ERT5Xdydww6H/IPLIQD4yOkAGqnpS7+RPS # rOgb/gtwCN5gPAf83oajUwxN1tMbznb5t7KIBUtoUAoHpy2NXPz71lcwd+8cUg0C # cruczEyHJp03CGqfC8og6Yw0Aqgf5SaXwygFcyYprxtTMK7LnTs7Vf5ge3hph5NW # N2xbqEG/p4ppVEwHaYu7GXCmvGufFzfSdbSmKglzR99tC/2UDvXUTBAQX8poAwNX # hex2DX+lHJ6dsQaBvgGGgBKFrABmbq01G6ovRzh38FhstexVzkS5kgeaWbApaYPQ # /R+3RaGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kw # gheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFF # MIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDqZb0yiwSu75Nm # JqKlDPKONBjYCN5BRUaC6u6FIqmPRgIGaq9xbXIYGBMyMDI2MTAxNTE5MDUzOS45 # NjhaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExp # bWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2RjFBLTA1RTAtRDk0NzEl # MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIF # EKADAgECAhMzAAACHAlVFdfDWQfRAAEAAAIcMA0GCSqGSIb3DQEBCwUAMHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgzMVoXDTI2MTEx # MzE4NDgzMVowgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # LTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEn # MCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjZGMUEtMDVFMC1EOTQ3MSUwIwYDVQQD # ExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEF # AAOCAg8AMIICCgKCAgEAow0xEAUaFIyyLIXeFzeI8IKyBON2u0Dr02ISE5p9G5CU # XfnFu2S0E1gWCMvDWpopX6lRxjmgnqaL3BtnWlBVTo8xUNRZu23ie4YBMAJB7Ut6 # mnqnHVwvDJxGO4TD3SnrCd+yg35B9QFejq3o4+OByvXjynaypZyukcQaLsKQvoxE # 8ElHH7zcOXEJWmU3rnXzaW/S4SH3OPhoUbTTcy6nUgKx5pRWiQ24UEPLYzcxGJjq # jkz+GiCWGPFHDMdW86laWvmCslouQPsN2eBk8dxJcEZmW4l6p4TthoXcfexEA9Yd # YaMz10aMhZNpdsNaDtDQUMDEC3k1D1My69MXSPlUmD9xFyDlkXiVa7BCEp3XcVtq # TgzHGwr28JD6oE7zEPYeuZOiuCBXTZSo/wk3tbDlsESbIPV6inYqrzxiMYqlxfCd # zC3Cimh9/NT/Lk9/aU+Iyyc9b3OaT0dZ8wgLaVDCGELRMrqyImdFHv0MudctzW/k # PsV3Ja9ufpKWujEiN3CW//X8hFa9j5ImNeQzcMit3MoSaoGwnbiZJX1IyibIphlq # ccXFk4oTTSOQBsAUw8U0gwOnM5UJD8mBUBd65Np6NBkx2cviJ4I34GyXFCWyy5Ft # 1QsBYyVfAG3KOhCfPHQf8lQzJvLr57YW0bD/xVs4Ag4gTS6KZNyFEfX9jFdRlr0C # AwEAAaOCAUkwggFFMB0GA1UdDgQWBBRa3mOCzB8u7zpvDh8MGKVYLCk7ZDAfBgNV # HSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5o # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBU # aW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwG # CCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRz # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNV # HRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIH # gDANBgkqhkiG9w0BAQsFAAOCAgEAklb6w/deaid3BujQCtWFBe0n9pkyRy+yyWEg # 70iDwoJ5u0e0O+4GerNzdZb1zTPsHJ8EGMyo1K7ytL21+pmdFMTl19PC8OJ5Y2p+ # XKUQy2dD+hggRMmJgDQsgbOCxHYeO+jg4t+vg61wUrovzzLkH3z0PJXXvoNuBj9L # da9CiNMd60451Kube99ArSf6ZMj3t0p4rFbgSazDs+8TJ+8KA5GVaYjPHj9rlMuI # 3WjohEc9apnQ6hMjMck3jlHZIwluVYeUQE0qjmApfMtTAEzbMUdY8sLTunL1GkbD # SeKn9O7llBGnNtyM1uM9Mdv1VyWh0z/IriQKIjntqqGyoF0HvDHOFZCyUDBPLfly # iu7Y1zQ/sPounsb96aBfQdq3h3LOn6t+m9EnNz/G6MzzWvpJk6YgTHTIqeQN/F/X # piPvbfek3nq/PYbL3au+kBfRUHiCFXSvt6lor0HC626vUmz9ZNPOxwEWLuccomxs # y3JwWH79vsM/7ARqoG5h6d6NahfaOuRP4XI9xtdH3Pa/NCLyQjxKXyLxzwQzjddk # X2EpTJnlypuhPmEdea59Uz2E303LxyXSnKBvGsAnyWYAfnejr3YAiL9YrN2l2dn1 # 98RpA4DCm9QtZYiwC0q2fuUvui34PfPIUZByf7wHuuWu50hY9WLx1kOMI8xyo7AI # 6TaNrnIwggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3 # DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIw # MAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAx # MDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l # LVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA # 5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/ # XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1 # hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7 # M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3K # Ni1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy # 1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF80 # 3RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQc # NIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahha # YQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkL # iWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV # 2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIG # CSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUp # zxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBT # MFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jv # c29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYI # KwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186a # GMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br # aS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsG # AQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcN # AQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1 # OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYA # A7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbz # aN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6L # GYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3m # Sj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0 # SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxko # JLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFm # PWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC482 # 2rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7 # vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCC # AkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExp # bWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo2RjFBLTA1RTAtRDk0NzEl # MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsO # AwIaAxUAWmTiA01u5mxq/nVxiRJLMOskVGeggYMwgYCkfjB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO57iycwIhgPMjAyNjEw # MTUxNzI1NTlaGA8yMDI2MTAxNjE3MjU1OVowdzA9BgorBgEEAYRZCgQBMS8wLTAK # AgUA7nuLJwIBADAKAgEAAgIW3QIB/zAHAgEAAgISRjAKAgUA7nzcpwIBADA2Bgor # BgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAID # AYagMA0GCSqGSIb3DQEBCwUAA4IBAQCbiZGgY68IanGnHYkd848jjmsjHV4js4h0 # aZTh6I1I6wzeXJ0q/EVR1AiaWdGech+PGRu/kse4bWj94Td4G27KDswH03m3FBJ5 # XRijkmD7I97h3I1uUjob8sQrOyuB7Zk/NMjmyMKNxCkg0AhjM0K600lNVDDpCjZE # sAP18A8I7JpN3O2VWpBLe0ooUP3qEgVGxRDHcGhqBq6y49f+GBnUfCsBe8n+FQGe # xHmpO69Nz3OagAQUfCJ6jbl6tmoLwjvgGY4UhEHzk99pdaKSUoBDwX5pdbHnsQVc # CX58RmY+TUH7yi6UZNWj9ShlkEyXV2X1qJW3ZAhWIZjlT7WEsCwLMYIEDTCCBAkC # AQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIcCVUV18NZ # B9EAAQAAAhwwDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG # 9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg3KMU7ivF22nep8QPIBcep9Pb6iwT8R+r # 6OYj1JBvEsgwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCgIGkmNhdo7+KE # 7dWhI+E2Ctx2RLWoYvvJodCIciHHaDCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwAhMzAAACHAlVFdfDWQfRAAEAAAIcMCIEINNH0yi0QdlpbaZr # LykbdgHEZYbdyc7wbY+xKizEINywMA0GCSqGSIb3DQEBCwUABIICABiA4sqZz959 # qgpvLe+H4hUbIOTbl8j17YSV08QS5+9TqSWAxvZkTB42eUhCK+nZmu3czfhnW6KP # z2OhOTyL+IyqiDgyrwe6uOg3ZYxLHjHFZ5DndTp/IrFIg2DWoPsmIn0y1BLV9+aA # Bx5nicVRsdWld6Irh4ox4SYB7X7Hiyw3DhaSSC9iSvjxgogsD9CUOiGRgh1m6d/I # y1DKclljDaJBpcUA9x4mieLHs0iBaNiu7pZxLO7hImv/MFB4gcVv+IvFt0Ln9BI1 # x5rbMkOBp79S96Pc3sC39oeiJoWHQOZIRvKo/RlcG69nqCuQzgLACLO3swkhDxgz # 5z9KWU5aMg6K4MsSb3R2iBXg62kOg+qS65OT9EvWdwPPDc6Tlq3vKB6JXgBRp2gs # J11MzO9hTdPJ/GxWKvtFTP74UD93sIlHKM7xNd/XetsYiTr4ptt32E6QH14hpgR3 # 2eV5yPd9QVwB1+vcq9xjbpuaK0p2IcN03tWw+LmMmjT7LsVVIRs7QdLFkXQnpTec # 1EhHVQ/OsNydJ5KdZicUZp5ZddRiRB2qhZl5by/yBG73ap1Ukc0L/I6vfYdJ1dn/ # ifLbTJx2Ir8t5BnCRlNLZpoPRa5uUl82iVIcGjBjIG/QCQg3rDrSFaXwzvpdfTdK # 3Ep5HpoJtgw8eOtTG2247bSQUKx7d3Pm # SIG # End signature block |