Private/Tests/Assert-AvmBicepScopedTestIsolation.ps1
|
function Assert-AvmBicepScopedTestIsolation { [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.IDictionary] $Template, [Parameter(Mandatory)] [ValidateSet('sub', 'mg', 'tenant', 'group')] [string] $Scope, [Parameter(Mandatory)] [string] $SourcePath, [switch] $AllowTelemetryOnly, [string] $OwnedGroupRunId ) Set-StrictMode -Version 3.0 $ownerTag = (Get-AvmBicepConfiguration)['e2e']['ownershipTag'] $ErrorActionPreference = 'Stop' $resources = $Template['resources'] $resourceList = [System.Collections.Generic.List[object]]::new() $symbolic = $Template.Contains('languageVersion') if ($symbolic) { if ($Template['languageVersion'] -isnot [string] -or $Template['languageVersion'] -cne '2.0' -or $resources -isnot [System.Collections.IDictionary]) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an unsupported ARM symbolic resource shape.") } $symbols = [System.Collections.Generic.HashSet[string]]::new( [System.StringComparer]::OrdinalIgnoreCase) foreach ($name in $resources.Keys) { if ($name -isnot [string] -or [string]::IsNullOrWhiteSpace($name) -or -not $symbols.Add($name)) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an invalid or duplicate ARM symbolic resource name.") } $resourceList.Add($resources[$name]) } } elseif ($resources -is [array]) { foreach ($resource in $resources) { $resourceList.Add($resource) } } else { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has no inspectable ARM resources.") } if ($resourceList.Count -eq 0) { if ($AllowTelemetryOnly) { Assert-AvmBicepScopedTelemetryTemplate -Template $Template -SourcePath $SourcePath return } throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has no inspectable ARM resources.") } $allowed = if ($Scope -eq 'group') { @('Microsoft.Network/routeTables') } else { @( 'Microsoft.Authorization/policyDefinitions' 'Microsoft.Authorization/policySetDefinitions' 'Microsoft.Authorization/roleDefinitions' ) } $ownedGroup = $null $crossGroupCount = 0 if ($Scope -eq 'sub' -and -not [string]::IsNullOrWhiteSpace($OwnedGroupRunId)) { if (-not (Test-AvmBicepRunId -RunId $OwnedGroupRunId)) { throw [AvmConfigurationException]::new('Bicep e2e run ID must be 32 lowercase hexadecimal characters.') } $groups = @($resourceList | Where-Object { $_ -is [System.Collections.IDictionary] -and $_['type'] -ceq 'Microsoft.Resources/resourceGroups' }) $crossGroupCount = @($resourceList | Where-Object { $_ -is [System.Collections.IDictionary] -and $_.Contains('resourceGroup') }).Count if ($crossGroupCount -gt 0) { if ($groups.Count -ne 1 -or $crossGroupCount -ne 1 -or $resourceList.Count -ne 2) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' requires exactly one new group and one group deployment.") } $ownedGroup = $groups[0] $tags = $ownedGroup['tags'] if ($ownedGroup['name'] -isnot [string] -or $ownedGroup['name'] -cnotmatch '^\[parameters\(''[A-Za-z][A-Za-z0-9_]*''\)\]$' -or $ownedGroup.Contains('condition') -or $ownedGroup.Contains('copy') -or $tags -isnot [System.Collections.IDictionary] -or $tags[$ownerTag] -isnot [string] -or $tags[$ownerTag] -cne $OwnedGroupRunId) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' requires an unconditional run-owned group with a parameterized name.") } } } if ($Scope -eq 'sub') { $allowed += 'Microsoft.Resources/resourceGroups' } foreach ($resource in $resourceList) { if ($resource -isnot [System.Collections.IDictionary]) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an invalid ARM resource.") } $groupDeployment = $null -ne $ownedGroup -and $resource.Contains('resourceGroup') foreach ($property in @('scope', 'subscriptionId', 'resourceGroup', 'managementGroupId', 'tenantId')) { if ($resource.Contains($property) -and -not ($groupDeployment -and $property -eq 'resourceGroup')) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' contains a cross-scope resource ($property); this scope is not yet approved.") } } $type = [string]$resource['type'] if ($type -cnotmatch '^[A-Za-z0-9.]+/[A-Za-z0-9]+$') { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has a resource type that cannot be verified.") } if ($groupDeployment) { $groupName = [string]$ownedGroup['name'] $groupReference = $groupName.Substring(1, $groupName.Length - 2) $expectedDependency = "[subscriptionResourceId('Microsoft.Resources/resourceGroups', $groupReference)]" $dependsOn = $resource['dependsOn'] if ($type -cne 'Microsoft.Resources/deployments' -or $resource['resourceGroup'] -isnot [string] -or $resource['resourceGroup'] -cne $groupName -or $resource.Contains('copy') -or $resource.Contains('condition') -or $dependsOn -isnot [array] -or $dependsOn.Count -ne 1 -or $dependsOn[0] -isnot [string] -or $dependsOn[0] -cne $expectedDependency) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an unverified or repeated group deployment.") } } if ($type -eq 'Microsoft.Resources/deployments') { $properties = $resource['properties'] if ($properties -isnot [System.Collections.IDictionary] -or $properties.Contains('templateLink') -or $properties['template'] -isnot [System.Collections.IDictionary]) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has a nested deployment without an inspectable inline template.") } if ($properties['mode'] -isnot [string] -or $properties['mode'] -cne 'Incremental') { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has a nested deployment without a literal Incremental mode.") } foreach ($property in $properties.Keys) { if ($property -cnotin @('mode', 'template', 'parameters', 'expressionEvaluationOptions')) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an unsupported nested deployment property '$property'.") } } if ($properties.Contains('parameters') -and $properties['parameters'] -isnot [System.Collections.IDictionary]) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' requires inline nested deployment parameters.") } if (($groupDeployment -or $Scope -eq 'group') -and $properties.Contains('parameters')) { foreach ($parameter in $properties['parameters'].Values) { if ($parameter -isnot [System.Collections.IDictionary] -or $parameter.Count -ne 1 -or -not $parameter.Contains('value')) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has an uninspectable group deployment parameter.") } } } if ($properties.Contains('expressionEvaluationOptions')) { $options = $properties['expressionEvaluationOptions'] if ($options -isnot [System.Collections.IDictionary] -or $options.Count -ne 1 -or $options['scope'] -isnot [string] -or $options['scope'] -cne 'inner') { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' requires inner-scope nested expression evaluation.") } } $nestedScope = if ($groupDeployment) { 'group' } else { $Scope } if ($groupDeployment) { $nestedSchema = $properties['template']['$schema'] if ($nestedSchema -isnot [string] -or $nestedSchema -cnotmatch '^https://schema\.management\.azure\.com/schemas/\d{4}-\d{2}-\d{2}/deploymentTemplate\.json#$') { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' has a group deployment without an inline resource-group template.") } } Assert-AvmBicepScopedTestIsolation -Template $properties['template'] ` -Scope $nestedScope -SourcePath $SourcePath -AllowTelemetryOnly } elseif ($type -notin $allowed) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' contains unsupported $Scope resource type '$type'; deployment was refused.") } if ($resource.Contains('resources')) { $children = $resource['resources'] if ($children -isnot [array] -and -not ($symbolic -and $children -is [System.Collections.IDictionary])) { throw [AvmConfigurationException]::new( "Bicep e2e test '$SourcePath' contains child resources that cannot be inspected.") } if ($children.Count -gt 0) { $childTemplate = @{ resources = $children } if ($children -is [System.Collections.IDictionary]) { $childTemplate.languageVersion = '2.0' } Assert-AvmBicepScopedTestIsolation -Template $childTemplate ` -Scope $Scope -SourcePath $SourcePath } } } } # SIG # Begin signature block # MIInQAYJKoZIhvcNAQcCoIInMTCCJy0CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAUp9yaKyfWpP3l # pR/OGnJzJILp2loViHLj1bsU6FgBfqCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # 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 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghncMIIZ2AIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJ # KoZIhvcNAQkEMSIEIAXlFOoO9FEO4XEUznFoFXx+nKBmbAXgwUvjKEn88ak6MEIG # CisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAblo9Bho/xHZHYsGC # v2NICCDXs8dPQn2yymUpNJvrcZutxBQwaQOJQSGulAMhhp7rmvhbgx37LKW69SPG # DAw2m4E4YPCISjc6W0UlGt2Pa6I/+kbltoxXAnacXGEz0GvOABupq71dTHC7TSOs # NO5rf8uzvN7ygHjPNHfT3ue9qCyADSScv9zSBX0myJhiUjhtaSnE4SO7162C+LF6 # LHZ8d9Cd2xB3ljPcePuYHQqRO8rujN9EFUOEnU7sQz4MR0U5Cj6Vh9kXeF8XX2co # TXLbVjNBNqThVux6S+3dj41sOSQrKEe6rzOO9pN60rbSAw3PZjdS96rFni14fRkk # oUh9kqGCF6wwgheoBgorBgEEAYI3AwMBMYIXmDCCF5QGCSqGSIb3DQEHAqCCF4Uw # gheBAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFE # MIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCDUbQVbpdXzVG2h # DEbFF+eBqhyeFioHwEytJ34IrFmVrwIGauvZ/d9iGBIyMDI2MTEwOTA5MzU0OC4x # MlowBIACAfSggdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGlt # aXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjZCMDUtMDVFMC1EOTQ3MSUw # IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIR+zCCBygwggUQ # oAMCAQICEzMAAAIvd0iHAHuFQ6gAAQAAAi8wDQYJKoZIhvcNAQELBQAwfDELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z # b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjYxMDE1MTgzNzM0WhcNMjgwMTEx # MTgzNzM0WjCB0zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEt # MCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046NkIwNS0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCkwU9iuede77Eq3o9xkPG506LoRL71d12740hx8LzkHYyd # K8HXs6Usl0d5D6s6UGNr2iPVmPOE72/zu2YitAE7AZhVo6US7Xpk2j1iLb9VXIvM # QqsrEaNibhejzZtg39Vn+VNRCjRSjovlDX+6F/yHYfME99nMDZrCUpQrQqKFBdE3 # WeeuhBXiHVG3oplZ/1eR4dDL1071Xz64G2w8InY6oAGnlsGBLDrLy35EPS3lAuNw # 7k9ZcwCo52zGe08McJVIu9Z0ymKW5muM1iv5m73UnnprIgbYlTVGsAXF9RI0Hff+ # fLyrSLV+TBq2jfH/XkzTqk/gFr6VzDIyS756u8YSSsRnL0Gdv/ZspQsyv8cZktX3 # cPYZgVc9D1/YMRUwjzjgJ+z9dbnDSTjnHl9qrYkrkANV7hvQMTRKEecKppOB9SeB # 3/X5ez6gly7KvLHxNGOaZ/+1gU9eozFAzhkUbQHq9k4/Y8T7pioXnlBFDavGhFc6 # o0T2Ye29Hdm99GdNLu8JJ8J2soDcp0/HFRjgL8y2fEnPnPc2Y4FEw4VxOq5kYbP5 # ZwoPOBITI+UKpr8TfZQnIq2PCz+cl0TcUo3uk6KYw3uBHWaa5eo+Iuc43SJqnzVk # VWGfPez8XcdnetvDKVufNWjwANH3418VrqmbgV7n5JinEG4u3SN9R+Cc1xboowID # AQABo4IBSTCCAUUwHQYDVR0OBBYEFKXmQNFp9dPojxhBQe7rGG+ib+fcMB8GA1Ud # IwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRp # bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYI # KwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMv # TWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1Ud # EwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeA # MA0GCSqGSIb3DQEBCwUAA4ICAQCsLvvnena7cK3/qrJdHcR2sn9g8wU4gB4mpwc+ # VX6qoLogE5GZoKXu7iMcRE92/PYPvH3hGtDOogIlc8emujcCzBMWQTHh31Sw/aBQ # 4WyQGtASV+/CO82jGMBQf319PZHubZpY7cm9BVsdCQRs6Fr7f6hKwJBT5x4IA8vo # CYHkNghgtgpJxcrel0oJjGbmi5nBe2OguMuZKc4OMIlCQaCIagw5/X/O3xa2hlRB # MPA3sqxu6h10jahngkXb6SBJJ/G4ddKRErd5/MwJBSic1H9CAqt8fxsCqT3EhIMC # Qj7waSlD9+ls+RdCgb5FfMOmhrd33bwE3jp+N8PtCYLZVshSFT+oyQprj5VQx+d6 # Mx4limHwWwFik7bx4SNIBhGtUHPcAQtYRFaS2PfrUe4Ifj0AmQ5K3xsyUcLRqlMR # wMYnGos65EOthRz3ZL24WjL/gJ8fkUl+9+hyfR6LvJH9LEaqOVHvZRLm0DWBXPZP # TOnTSaGdFrOWdeY1ty3hZL6vkCg8MIT76UZ6/yhW1iRsmUQ/P7p45dJVYAoKEveI # 6+tsy7jwlQ3MkDzdVKEMc8D5Z6w93k49XADQ8zXTvkoRy3Dczdsak2t99TwagogL # +LxnBWprNYRO7EItkqRb+/P6W6od3a1ftxkAOEliXKRppEA7sdiVk4Fw8cNYhyb1 # gEOJVDCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcN # 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/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNWMIIC # PgIBATCCAQGhgdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGlt # aXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjZCMDUtMDVFMC1EOTQ3MSUw # IwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4D # AhoDFQDxMDYkFHqauFfO+fRxN/jzWnM/CqCBgzCBgKR+MHwxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1l # LVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7pueCTAiGA8yMDI2MTEw # OTAxMTkwNVoYDzIwMjYxMTEwMDExOTA1WjB0MDoGCisGAQQBhFkKBAExLDAqMAoC # BQDum54JAgEAMAcCAQACAiZAMAcCAQACAhJkMAoCBQDunO+JAgEAMDYGCisGAQQB # hFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAw # DQYJKoZIhvcNAQELBQADggEBAC8DxNErexyn3VQnsUwygchsx6yPXWDSknpjd2QF # 1cuJI+I+wFtl4jODWDsxnUrq+15n0pTaiIB0+f84+AZpupWbpfJNktTrcZUXMs3y # AbqysybWzcnfr1vOVweJETSCNc1eZ4NUJEDUXf73wTrI5xDQUAtCtBv1LB0z5+Zc # /LxQ1rsXNLIkNnJ4EUbGuiX/6oz7mGqewXwhT5vbviY+/uzxkvdNFBgVPmnB8Q+6 # UDmCmyxRIVipT+iMTkXaYpQQTGfeF/xYTx74gVLzVKBy15gg969foUo1mMzgDpfc # TBxCaXFtSfWJwtwPU7D3tnWE68Xlu5c3qE/wxCQfRSrA388xggQNMIIECQIBATCB # kzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAi93SIcAe4VDqAAB # AAACLzANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJ # EAEEMC8GCSqGSIb3DQEJBDEiBCCHYnCsRio454FN+gHv/8EuETfnANl5m5SotyuL # 65YfVDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIE8hVVxj6ngdEy/rdEyh # wX5DAI1+8kiecpSlOsL7Pfj/MIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTACEzMAAAIvd0iHAHuFQ6gAAQAAAi8wIgQgEHZpsQjRiS5dBZt+zs4a # A5HwgT5F7590AbXFjPUqTQQwDQYJKoZIhvcNAQELBQAEggIAnmJl4GMeIf1KaLrH # +OJ2kWSs5g6bcn/BU6KZBsDPxLHF2piKPwwlqKzNZ0NEJ0D28fbVn7sWRNCEl06/ # jFIo2d9LfVlbuj2p1RrCIns0sGQKYEy0z4sR/b2Nab4CF1tBXZ+HhqPpN6O0RB8x # TZy4V11EH7WGRJAk6ZNDQjFg+omFIho+ACs+ugwHMx4Wz81ij8a3c+yPUggqH6ML # k4ap7z8gNMXgLToWaUgemfhWiDMuqwPrVXc6FS24QZFZyRXB+FiXTkckVyb+Zvpg # ghkO3vC17a3uubCVnH6Cmv/ikB4Ugr0FdVo5R/RzcIWM5KHKgPVf+LsEIKIAZSBx # yHDSZZy4tu4WIwaHRt70ut+xwhU5O8mdoyZ8M0b/Gh80cwByiadzosQ5DLU5U49s # mci8uM6ZShBWKgnl3RLwMy6t4hpCA1ApuoXe1KoqNx4Fu8AemcqBO8ANVLp1JmVi # HzdHzrvtDMxulr397DRRuzTuB24mmZczrmWhfyeggcP12WSZJVX8dc464Fybffx8 # aRStlOJlnhvMYpETn8m9Mcduue7eoY1M2ovcL6eFBziCbyBdzz6KKzZ9gsLtv3gi # BZ54pLK4YKfSXrLn3miBaLHZ/RK4PzSf8mjuNfgcDZ7c5PDsMgjT0/NPRDj+HZdp # rEDgLMV9TlAbEoBPmvi8MJoaT3k= # SIG # End signature block |