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.

        Bicep registry modules read repository-root .required-features.json and
        .required-resource-providers.json maps using the exact module Path.
        Validate each whole map before dependency preparation or Azure calls.
        After compilation and identity verification, register features and
        propagate their providers, then register required provider namespaces,
        before token replacement, validation or deployment. Missing, empty or
        unrelated maps skip registration. WhatIf validates configuration without
        Azure access. Complete and cleanup never register prerequisites, and
        registrations are never undone. No additional caller input is needed.

        Packaged Resources/bicep/retry-policy.json selects bounded retries
        from complete structured failure evidence. InPlace reuses the exact
        deployment, template and parameters. Fresh generates a new external
        naming context and deployment identity, using another eligible region
        by default. Fixed caller inputs and names authored inside Bicep are
        not rewritten, so Fresh does not guarantee all resource names differ.
        No resources are deleted between attempts. Resource evidence is saved
        before another attempt can overwrite deployment-operation history.
        Status recovery shares one configured four-hour deadline across all
        attempts in a case; it never starts a read or accepts a terminal result
        at or after that deadline.

        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 unique groups and verified ownership tags. All 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.
        Final cleanup covers every attempt, including failed attempts.
        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 all attempts 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 for every attempt. This is an explicit debugging exception.
        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.
        After pre.ps1 and .env loading, the shared Terraform authentication
        preflight checks ARM_* inputs or an enabled Azure CLI subscription.
        It passes CLI subscription/tenant IDs only to child processes and never
        signs in or changes the selected account. An authentication failure
        skips Terraform operations, reports an error and still runs post.ps1.
        Providers still validate credentials and resource permissions.
        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.
        Required provider declarations use only the exact Bicep registry module
        key in the root .required-resource-providers.json, without inheritance.
    .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.
        With UseCiInputs, omitting this parameter uses the configured CI target.
        An explicitly supplied value always takes precedence and is validated;
        an invalid or empty explicit value is never replaced by a CI fallback.
    .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 per fresh context.
        In-place retries preserve these values; explicit caller values stay fixed.
    .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.
        When ManagementGroupId is omitted, the same source order resolves
        VALIDATE_MANAGEMENT_GROUP_ID before ARM_MGMTGROUP_ID within each source.
        Management-group cases still require a valid resolved target.
        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 every attempt, skipping post and cleanup.
    .PARAMETER DeploymentRetryLimit
        Bicep total submission attempts, one to three, capped by packaged policy.
        Only exact Failed deployments with complete eligible evidence can retry;
        unknown outcomes and preflight rejection alone never authorize resubmission.
    .PARAMETER ValidationRetryLimit
        Bicep regional validation budget, one to three, capped by packaged policy.
        Only wholly regional validation failures move an unpinned, non-global,
        non-resource-group case. Fresh deployment retries share the region budget
        and validate their new context; in-place retries reuse prior validation.
    .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
# MIInOgYJKoZIhvcNAQcCoIInKzCCJycCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCYI9mN2E4AyV63
# ufcW0LCB1Pc/YVw61llBgQSLUYtjoKCCDMkwggYEMIID7KADAgECAhMzAAACHPrN
# 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/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghnHMIIZwwIBATBu
# MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc
# +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwG
# CisGAQQBgjcCAQQwLwYJKoZIhvcNAQkEMSIEIPEN9VuO/X9YQ64Qm6f9A1N99FWa
# ET/3YMOB1n5nu+fhMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBv
# AGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAE
# ggEABtCQ2MxW3Ph/+x9VZGm2hqPKfnZ2sO7HxeymnMtLC/SunaZxFEF0paqiR13c
# HWMd9iDPTqvbPnwaiDeroDoiXeFA5fVnDe4Q+ZE7aEpxClR39pTf+S5gWZfuS09h
# 0zAeVoVCWgkb4AXAoQTZ2NUD0g7DnbtfEJq/PKkiPRka+pXg4pQmlBfvOkrOtulc
# xf5PyeGbGhEOAMi7vAYSXWhO6o1g1U/u6b/hg/zFg1y7YUs8pui8DyMhZWtIH6gN
# yaj82GxPU2wBaHFxT1R45pJCliWedMvMB2MRoWxTLfk+6v6oj4F+W+X9TMgxGR6j
# MpVFJnBYaeBTOHFjIwqvlTzchqGCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCCF38G
# CSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsqhkiG
# 9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQC
# AQUABCDcknHtSq+qk6LHcj0wIx5ECTF2WuT0EDkmg3AMgh5BmgIGaqpr3XMpGBMy
# MDI2MTAyMDIyMjAyMi4xNjFaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj
# YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTYwMC0wNUUw
# LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WgghHt
# MIIHIDCCBQigAwIBAgITMwAAAiY1tD5nQ5P2HwABAAACJjANBgkqhkiG9w0BAQsF
# ADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNjAyMTkxOTQwMDJa
# Fw0yNzA1MTcxOTQwMDJaMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw
# JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTYwMC0wNUUwLUQ5NDcxJTAjBgNVBAMT
# HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQC//w+ZZIL5RFFpVI8D3ZyuNu8IzcAEOD30OLYjh337rXjc
# rIlOSzpJc4ZeUxEyli6x6F6zm4NR8dbPb9diDp/hOUzHWGxiA1Z3RXKBb/4F/ojy
# vN43SEGWqSfVc3I3BlsYT35ecVAJ9kVf90YOv29tFjJBBZkYvrT/DwwyRLscOyP4
# p+9/lyJjD+ULs3YXBhVrfZ+MbQB+BYKLqRvBKbj/wR9akNrMxQINoGaD5jZO/N/n
# SsmG2P1zv/cv4gSoMBnWeQIBkjd2I5w1DeXupp2vSiNmR5sA2ZkBK3yiQWaJvRxO
# DlkfiyHk9Mkk/TrYTjmjPCbhe+uqhHNRy8UlbOvWsCq0tRtUykHv39DgqAfJNrE8
# OSt835rBzDprrcAhwmgfhoVi4AKeqwikY0nUa48K0Qy80XT4fiEA3ExEZNaRFo9N
# q/GwbfgqKqGmc9xhKuRFcjtua4KHZvnAvpWgEFSOCkovXs/BcLnkEHM9xZ8iUag5
# CyhNqXYYE/z0pcXdYaNIkQ68EWmuvLm7g9oofV2vOm5GVNoghnkWG6nGPo/JwEgm
# A9oSS0EfvFRMWPA/gpSvF3shArKHnaEpVSSi3DNbyiuYiEs9Ko0IkZc8xKFeQRaq
# GRxrB+2r/7B3X81Tps99KhFwg+wD87od22F2MUg1x7twt3gaVnFk0IZIwUPCGwID
# AQABo4IBSTCCAUUwHQYDVR0OBBYEFF3hn9fYJN2Y/Z9LVbBPIxAzXHsQMB8GA1Ud
# IwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRp
# bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYI
# KwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMv
# TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1Ud
# EwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeA
# MA0GCSqGSIb3DQEBCwUAA4ICAQA2Ux0tr9sYCjsq0FRyiVpx15OurNXv6Qk7iX+A
# rVPlz3w4tqjcTNm1dt3tTua2wJMpJhPH8n7UXhmT98d5Du44Ll4adnse4SQfVg3Q
# L6aRkXHnJUn8y9iftB/Py22n9xnwPFfj3QlDOSgLuHleu97U0iH2ZaluYabWXJih
# diYpK8cPHFlqZOAiot0+GD8dP+RMuvpxt/F2LmYelpoZwriiFOUmlxEUV7xJHyZZ
# lDquskeyuq01DTv91N4qM8cfPPhl/2pc4HeMf/nd2HouifJbDQFNd4WPhLzn0Sy3
# u1Zh3+S3tjQdqN+dyw60RaV+RXCoOLgFZ3MAg/GoDl+fvb5hy/1a71ctX8wEad1P
# f6def2pqfl3wFc++hkF8DXXTZofJN4YVaN3InwbAGQDDkNK4lqecCixxmSKwidPy
# nGeE5OtvNoK1pkLsm/i8F1RjGczZ/kSF2VDkqG866iQ+jVbGOQ6Du3eyyFcFKZoD
# J4B5mEAS9aT2SKqllLeybOboH6r67siR5B/2Hnu7+KYuYZy0BEadtA6ngG4cnSR9
# JsrkhhsKmb11ujqwgJyNx92MsoGGwNgN1aI0QID8CsjCFwpfmMzlA44xHKYv3hmj
# xeqBS4uU5rQeiAnVgpJeaVGKm/lzPDtnppGV+7XhRp5b1ZxT/Z7Xxc+I7H7/jCtQ
# DZoaZTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcN
# 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/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQMIIC
# OAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUG
# A1UECxMeblNoaWVsZCBUU1MgRVNOOjk2MDAtMDVFMC1EOTQ3MSUwIwYDVQQDExxN
# aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCi/fMx
# Ftkqr7XMXdsRyWU0lSKHZ6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBD
# QSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7oJghjAiGA8yMDI2MTAyMDIxNDk1OFoY
# DzIwMjYxMDIxMjE0OTU4WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDugmCGAgEA
# MAoCAQACAhTtAgH/MAcCAQACAh8KMAoCBQDug7IGAgEAMDYGCisGAQQBhFkKBAIx
# KDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZI
# hvcNAQELBQADggEBACFgs5xAFq4ILNnvYkUryqbVqcmwN5k4rttKLM3gxbv+M9Nm
# VGcOUxxnKSEmVAMRiTo/5na/9DfXRdRSMQJNtz9qkWxRBn1tXz1WQozmj/G7fvqo
# L6qH5aCwLnLYmHUsw6Uest+SHcxyfgevXCBJTB2JzZZm/QkDmLlXMOCyriC3ki5W
# tGB+iNHz1qgF3y5KMGqI1uZ+tcL+h3bmeWPlEJ9I4tbpHdxU1ersplPJ9/vIIhDh
# sAi4j46J/XTcIu6+jDavkg+jgK5x48MVPNtqSdF2e8eiVIVRg7E+zeobuRrXTU72
# BQinlnRfN3QoKv/2MSGRXLM+MjP9CTa37Cr2hxYxggQNMIIECQIBATCBkzB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiY1tD5nQ5P2HwABAAACJjAN
# BglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8G
# CSqGSIb3DQEJBDEiBCBqz7BFXjvqhN1jPQdY0kEBypZqLaQvMMq6FI+z39ugBDCB
# +gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIMwyXGFnTNsZRBrs6GN/BbV0okaN
# P3VBYqLFjUsFnbgqMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh
# c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD
# b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw
# MTACEzMAAAImNbQ+Z0OT9h8AAQAAAiYwIgQgYNeNyYq5tYDLa7Pm0AAau8I7/oXd
# DdBuRUMqvFMWQdswDQYJKoZIhvcNAQELBQAEggIAebFyddEIdEinIzXRWs6h9FYB
# 39BguaLb9Bn2fQcokghwM+gcc9zpQ+8Pu3uVlYKj505IDktwu/W+h8wY8QwWVhx4
# prRhjXvjtC9Au0N7Vq+0f0iSUGUkHS0IzDxMk5Md70R4OJ0rpR1NY4a1vxFS/XNx
# 0QkI8f4LBykiV7y/PWxUBhztO3P9MRiqKVcO3POeqRza3ZTI3RWCxBcRfkeUdMd8
# NMoNLqk5M1vyHgVLFzczkb9NZ8fPlKboKoZMNcukpPinh5GMG4qi2yQbGHjfAzRN
# mPT/FJJC5daEgAO/tHdexBmVPKTJO0QFf18YdnBxP//CrKksMlWmwMJGEVTbc64v
# WMvWuqaOABOcNv3UtiftQPNqN+hN9gX1WzbkaezUkL6piMjug0Jd1zyerN7wbwki
# SiyjN/odVz2uLGHkwv8LghnvuylGU+evRDwYL+CWA6x24B58QcpE4wI/mMUsK3fr
# GTQk3wY40h2XB+snwnkRdB6IbgePOPNl/aC0tjNdgdiRr0CsCR7LginsVUm1plHA
# z1Nz04hq3HO2XPvZW3Mn3Aq5zk8TeeeVCzu6SYTYnDNFadTlalm06D620zFKNhU1
# RaG4cQ7i99vZy78JudSfW6NPz6fVdGhNvufOo68dooXWqRhcaaNzmFkEqjPi5ur3
# QPrHn3Q78sl1UYqJJK4=
# SIG # End signature block