Public/PrivilegedAccess/Update-EntraOpsPrivilegedUnprotectedElmCatalog.ps1
|
<# .SYNOPSIS Sync privilege level of Entitlement Management catalogs based on EntraOps classification: Catalogs with Control Plane scope will be protected as "privileged" catalog, catalogs without privileged scope will be reverted to "standard". .DESCRIPTION Identifies Entitlement Management (Access Package) catalogs which have been classified as Control Plane scope in EntraOps (RBAC system "IdentityGovernance") and syncs their privilege level (Preview) by using Microsoft Graph API: - Catalog with Control Plane scope and privilege level "standard" -> updated to "privileged" (PROTECT) - Catalog with Control Plane scope and privilege level "privileged" -> no change (KEEP) - Catalog without Control Plane scope and privilege level "privileged" -> reverted to "standard" (REVERT) - Catalog without Control Plane scope and privilege level "standard" -> no change (ignored) Privileged catalogs apply stricter controls: - Applications must have directory role management permissions to write to a privileged catalog. - Only Global Administrators, or Privileged Role Administrators who also have the Identity Governance Administrator role, can perform create, update, or delete actions. - No new auto-assignment policies can be created for privileged catalogs. CAUTION: This cmdlet requires the elevated Microsoft Graph permission "EntitlementManagement.ReadWrite.All" and should be used with caution. The permission is only assigned by New-EntraOpsWorkloadIdentity when the EntraOpsConfig.json setting AutomatedElmCatalogProtection.ApplyPrivilegedElmCatalogProtection is enabled. Reference for required permissions: https://learn.microsoft.com/en-us/graph/api/entitlementmanagement-update?view=graph-rest-beta CAUTION: EntraOps cannot distinguish whether a catalog has been set to "privileged" by EntraOps or manually by an administrator. Catalogs without Control Plane scope in EntraOps will be reverted to "standard", even if they have been protected manually. NOTE: Updating the privilege level protects the catalog from delegated modification, but the existing role assignments on the catalog will still be classified as Control Plane by EntraOps and should be reviewed and removed as soon as possible. .PARAMETER ApplyToAccessTierLevel Array of Access Tier Levels which will be protected as privileged catalog. Default is ControlPlane. .PARAMETER RemovalSafetyThreshold Fraction of currently privileged catalogs that may be reverted in a single run before catalog reverts abort. Default is 0.5 (50%). .PARAMETER ForceRemovalBeyondSafetyThreshold Apply a reviewed catalog-revert plan even when it exceeds RemovalSafetyThreshold. .EXAMPLE Sync privilege level of all Entitlement Management catalogs based on Control Plane scope classification Update-EntraOpsPrivilegedUnprotectedElmCatalog #> function Update-EntraOpsPrivilegedUnprotectedElmCatalog { [CmdletBinding()] param ( [Parameter(Mandatory = $False)] [ValidateSet("ControlPlane", "ManagementPlane")] [Array]$ApplyToAccessTierLevel = ("ControlPlane") , # Maximum fraction of privileged catalogs reversible per synchronization. [Parameter(Mandatory = $False)] [ValidateRange(0, 1)] [double]$RemovalSafetyThreshold = 0.5 , # Permits catalog reverts that exceed RemovalSafetyThreshold. [Parameter(Mandatory = $False)] [switch]$ForceRemovalBeyondSafetyThreshold , [Parameter(Mandatory = $False)] [boolean]$ApplyPrivilegedElmCatalogProtection = $false ) Write-Warning "This cmdlet updates the privilege level of Entitlement Management catalogs and requires the elevated Microsoft Graph permission 'EntitlementManagement.ReadWrite.All'. Use with caution." # Get all privileged EAM objects from IdentityGovernance $PrivilegedEamFilePath = "$DefaultFolderClassifiedEam/IdentityGovernance/IdentityGovernance.json" if (-not (Test-Path -Path $PrivilegedEamFilePath)) { Write-Error "No classified EAM data found at $PrivilegedEamFilePath. Run Save-EntraOpsPrivilegedEAMJson for IdentityGovernance first." return } $PrivilegedEamObjects = Get-Content -Path $PrivilegedEamFilePath | ConvertFrom-Json # Identify unique catalogs with role assignments classified at the selected tier levels (desired privileged catalogs) $DesiredPrivilegedCatalogIds = [System.Collections.Generic.List[string]]::new() foreach ($PrivilegedEamObject in $PrivilegedEamObjects) { foreach ($RoleAssignment in @($PrivilegedEamObject.RoleAssignments)) { if ("$($RoleAssignment.RoleAssignmentScopeId)" -notlike "/AccessPackageCatalog/*") { continue } $TierLevelMatch = @($RoleAssignment.Classification | Where-Object { $_.AdminTierLevelName -in $ApplyToAccessTierLevel }) if ($TierLevelMatch.Count -eq 0) { continue } $CatalogId = "$($RoleAssignment.RoleAssignmentScopeId)".Replace("/AccessPackageCatalog/", "") if (-not $DesiredPrivilegedCatalogIds.Contains($CatalogId)) { $DesiredPrivilegedCatalogIds.Add($CatalogId) } } } # Get all Entitlement Management catalogs with their current privilege level try { $AllCatalogs = Invoke-EntraOpsMsGraphQuery -Method "GET" -Uri "/beta/identityGovernance/catalogs?`$select=id,displayName,privilegeLevel" -OutputType PSObject -DisableCache } catch { Write-Error "Could not get Entitlement Management catalogs: $_" return } $CurrentPrivilegedCatalogs = @($AllCatalogs | Where-Object { $_.privilegeLevel -eq "privileged" }) # Evaluate catalog reverts before applying catalog protection updates. $PlannedReverts = @($CurrentPrivilegedCatalogs | Where-Object { -not $DesiredPrivilegedCatalogIds.Contains("$($_.id)") }) $RevertSafetyCheck = Test-EntraOpsRemovalSafetyThreshold -CurrentCount $CurrentPrivilegedCatalogs.Count -RemovalCount $PlannedReverts.Count -RemovalSafetyThreshold $RemovalSafetyThreshold $SkipReverts = $false if ($RevertSafetyCheck.Exceeds -and -not $ForceRemovalBeyondSafetyThreshold) { $SkipReverts = $true Write-Warning "[ABORT] $($PlannedReverts.Count) catalog revert(s) exceed the $($RevertSafetyCheck.ThresholdPercent)% safety threshold ($($RevertSafetyCheck.RemovalThreshold) of $($CurrentPrivilegedCatalogs.Count) currently privileged). This may indicate an upstream data issue. Reverts are skipped (protect operations still apply) - review the summary, then re-run with -ForceRemovalBeyondSafetyThreshold to apply." } elseif ($RevertSafetyCheck.Exceeds) { Write-Warning "[FORCED] Applying $($PlannedReverts.Count) catalog revert(s) despite exceeding the $($RevertSafetyCheck.ThresholdPercent)% safety threshold - requested via -ForceRemovalBeyondSafetyThreshold." } # Summary tracking $SyncSummary = [System.Collections.Generic.List[psobject]]::new() $WarningMessages = New-Object -TypeName "System.Collections.Generic.List[psobject]" $TotalProtected = 0 $TotalReverted = 0 $TotalKept = 0 $TotalFailed = 0 $TotalAborted = 0 if ($SkipReverts) { $WarningMessages.Add([PSCustomObject]@{ Type = "SafetyAbort"; Message = "$($PlannedReverts.Count) catalog revert(s) exceed the $($RevertSafetyCheck.ThresholdPercent)% removal safety threshold - reverts skipped (re-run with -ForceRemovalBeyondSafetyThreshold to apply)" }) } Write-Host "" Write-Host "=========================================================" -ForegroundColor Cyan Write-Host " EntraOps - Privileged ELM Catalog Sync" -ForegroundColor Cyan Write-Host " Access Tiers : $($ApplyToAccessTierLevel -join ', ')" -ForegroundColor Cyan Write-Host " Catalogs : $(@($AllCatalogs).Count) total | $($DesiredPrivilegedCatalogIds.Count) with privileged scope | $($CurrentPrivilegedCatalogs.Count) currently privileged" -ForegroundColor Cyan Write-Host "=========================================================" -ForegroundColor Cyan Write-Host "" foreach ($Catalog in $AllCatalogs) { $IsDesiredPrivileged = $DesiredPrivilegedCatalogIds.Contains("$($Catalog.id)") $IsCurrentlyPrivileged = $Catalog.privilegeLevel -eq "privileged" # Case 1: Catalog with privileged scope is already protected - no change if ($IsDesiredPrivileged -and $IsCurrentlyPrivileged) { Write-Host " [~] KEEP $($Catalog.displayName) - already privileged" -ForegroundColor DarkGreen $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = "privileged"; After = "privileged"; Action = "Keep"; Status = "OK" }) $TotalKept++ continue } # Case 2: Catalog with privileged scope is not protected yet - update to privileged if ($IsDesiredPrivileged -and -not $IsCurrentlyPrivileged) { try { $Body = @{ privilegeLevel = "privileged" } | ConvertTo-Json Invoke-EntraOpsMsGraphQuery -Method "PATCH" -Uri "/beta/identityGovernance/entitlementManagement/catalogs/$($Catalog.id)" -Body $Body -OutputType PSObject -ThrowOnFailure | Out-Null Write-Host " [+] PROTECT $($Catalog.displayName) - privilege level: $($Catalog.privilegeLevel) -> privileged" -ForegroundColor Green Write-Warning " Role assignments to catalog '$($Catalog.displayName)' will still be classified as Control Plane by EntraOps and should be removed as soon as possible." $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = $Catalog.privilegeLevel; After = "privileged"; Action = "Protect"; Status = "OK" }) $TotalProtected++ } catch { Write-Warning " [!] FAIL PROTECT $($Catalog.displayName) ($($Catalog.id)): $_" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = "FAIL PROTECT catalog $($Catalog.displayName) ($($Catalog.id)): $_" }) $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = $Catalog.privilegeLevel; After = $Catalog.privilegeLevel; Action = "Protect"; Status = "FAILED" }) $TotalFailed++ } continue } # Case 3: Catalog without privileged scope is still protected - revert to standard if (-not $IsDesiredPrivileged -and $IsCurrentlyPrivileged) { if ($SkipReverts) { Write-Host " [!] ABORT $($Catalog.displayName) - revert skipped by removal safety threshold" -ForegroundColor Yellow $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = "privileged"; After = "privileged"; Action = "Revert"; Status = "ABORTED" }) $TotalAborted++ continue } try { $Body = @{ privilegeLevel = "standard" } | ConvertTo-Json Invoke-EntraOpsMsGraphQuery -Method "PATCH" -Uri "/beta/identityGovernance/entitlementManagement/catalogs/$($Catalog.id)" -Body $Body -OutputType PSObject -ThrowOnFailure | Out-Null Write-Host " [-] REVERT $($Catalog.displayName) - no longer in scope of $($ApplyToAccessTierLevel -join ', ') - privilege level: privileged -> standard" -ForegroundColor Yellow $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = "privileged"; After = "standard"; Action = "Revert"; Status = "OK" }) $TotalReverted++ } catch { Write-Warning " [!] FAIL REVERT $($Catalog.displayName) ($($Catalog.id)): $_" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = "FAIL REVERT catalog $($Catalog.displayName) ($($Catalog.id)): $_" }) $SyncSummary.Add([PSCustomObject]@{ Catalog = $Catalog.displayName; Before = "privileged"; After = "privileged"; Action = "Revert"; Status = "FAILED" }) $TotalFailed++ } continue } # Case 4: Catalog without privileged scope and standard privilege level - nothing to do (not shown in summary) } # Warn about catalogs with privileged scope that could not be found in the tenant (e.g., deleted catalogs) foreach ($DesiredCatalogId in $DesiredPrivilegedCatalogIds) { if ("$DesiredCatalogId" -notin @($AllCatalogs.id)) { Write-Warning " [!] Catalog $DesiredCatalogId with privileged scope not found in tenant (likely deleted)." $WarningMessages.Add([PSCustomObject]@{ Type = "CatalogNotFound"; Message = "Catalog $DesiredCatalogId with privileged scope not found in tenant (likely deleted)." }) $SyncSummary.Add([PSCustomObject]@{ Catalog = $DesiredCatalogId; Before = "N/A"; After = "N/A"; Action = "Protect"; Status = "NOT FOUND" }) } } # Final summary Write-Host "" Write-Host "=========================================================" -ForegroundColor Cyan Write-Host " Privileged ELM Catalog Sync Complete" -ForegroundColor Cyan Write-Host " Protected: $TotalProtected | Reverted: $TotalReverted | Kept: $TotalKept | Failed: $TotalFailed" -ForegroundColor Cyan Write-Host "=========================================================" -ForegroundColor Cyan Show-EntraOpsWarningSummary -WarningMessages $WarningMessages if ($SyncSummary.Count -gt 0) { $SyncSummary | Format-Table -AutoSize -Property Catalog, Before, After, Action, Status } else { Write-Host " No catalog changes required - all catalogs are already in sync." -ForegroundColor Gray } if ($TotalFailed -gt 0 -or $TotalAborted -gt 0) { $Reasons = [System.Collections.Generic.List[string]]::new() if ($TotalFailed -gt 0) { $Reasons.Add("$TotalFailed privilege level operation(s) failed") | Out-Null } if ($TotalAborted -gt 0) { $Reasons.Add("$TotalAborted catalog revert(s) aborted by the removal safety threshold") | Out-Null } throw "Entitlement Management catalog sync did not complete: $($Reasons -join '; '). Review the warning summary above." } } # SIG # Begin signature block # MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCDHRg2bRth9O/u # rKTFBBlg3U2V4BhgRWxSgun8FRU7LqCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # AEkoya49fu0nMA0GCSqGSIb3DQEBDAUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK # ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy # dGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5l # dHdvcmsgQ0EwHhcNMjEwNTMxMDY0MzA2WhcNMjkwOTE3MDY0MzA2WjCBgDELMAkG # A1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAl # BgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMb # Q2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEAvfl4+ObVgAxknYYblmRnPyI6HnUBfe/7XGeMycxca6mR5rlC # 5SBLm9qbe7mZXdmbgEvXhEArJ9PoujC7Pgkap0mV7ytAJMKXx6fumyXvqAoAl4Va # qp3cKcniNQfrcE1K1sGzVrihQTib0fsxf4/gX+GxPw+OFklg1waNGPmqJhCrKtPQ # 0WeNG0a+RzDVLnLRxWPa52N5RH5LYySJhi40PylMUosqp8DikSiJucBb+R3Z5yet # /5oCl8HGUJKbAiy9qbk0WQq/hEr/3/6zn+vZnuCYI+yma3cWKtvMrTscpIfcRnNe # GWJoRVfkkIJCu0LW8GHgwaM9ZqNd9BjuiMmNF0UpmTJ1AjHuKSbIawLmtWJFfzcV # WiNoidQ+3k4nsPBADLxNF8tNorMe0AZa3faTz1d1mfX6hhpneLO/lv403L3nUlbl # s+V1e9dBkQXcXWnjlQ1DufyDljmVe2yAWk8TcsbXfSl6RLpSpCrVQUYJIP4ioLZb # MI28iQzV13D4h1L92u+sUS4Hs07+0AnacO+Y+lbmbdu1V0vc5SwlFcieLnhO+Nqc # noYsylfzGuXIkosagpZ6w7xQEmnYDlpGizrrJvojybawgb5CAKT41v4wLsfSRvbl # jnX98sy50IdbzAYQYLuDNbdeZ95H7JlI8aShFf6tjGKOOVVPORa5sWOd/7cCAwEA # AaOCAT4wggE6MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLahVDkCw6A/joq8 # +tT4HKbROg79MB8GA1UdIwQYMBaAFAh2zcsH/yT2xc3tu5C84oQ3RnX3MA4GA1Ud # DwEB/wQEAwIBBjAvBgNVHR8EKDAmMCSgIqAghh5odHRwOi8vY3JsLmNlcnR1bS5w # bC9jdG5jYS5jcmwwawYIKwYBBQUHAQEEXzBdMCgGCCsGAQUFBzABhhxodHRwOi8v # c3ViY2Eub2NzcC1jZXJ0dW0uY29tMDEGCCsGAQUFBzAChiVodHRwOi8vcmVwb3Np # dG9yeS5jZXJ0dW0ucGwvY3RuY2EuY2VyMDkGA1UdIAQyMDAwLgYEVR0gADAmMCQG # CCsGAQUFBwIBFhhodHRwOi8vd3d3LmNlcnR1bS5wbC9DUFMwDQYJKoZIhvcNAQEM # BQADggEBAFHCoVgWIhCL/IYx1MIy01z4S6Ivaj5N+KsIHu3V6PrnCA3st8YeDrJ1 # BXqxC/rXdGoABh+kzqrya33YEcARCNQOTWHFOqj6seHjmOriY/1B9ZN9DbxdkjuR # mmW60F9MvkyNaAMQFtXx0ASKhTP5N+dbLiZpQjy6zbzUeulNndrnQ/tjUoCFBMQl # lVXwfqefAcVbKPjgzoZwpic7Ofs4LphTZSJ1Ldf23SIikZbr3WjtP6MZl9M7JYjs # NhI9qX7OAo0FmpKnJ25FspxihjcNpDOO16hO0EoXQ0zF8ads0h5YbBRRfopUofbv # n3l6XYGaFpAP4bvxSgD5+d2+7arszgowggZrMIIEU6ADAgECAhBX5gfP8LabvnxC # XoItEBa/MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhB # c3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNp # Z25pbmcgMjAyMSBDQTAeFw0yNjEwMDYwODI4MjNaFw0yNzEwMDYwODI4MjJaMIGQ # MQswCQYDVQQGEwJERTEdMBsGA1UECAwUUmhpbmVsYW5kLVBhbGF0aW5hdGUxEjAQ # BgNVBAcMCUxhaG5zdGVpbjEeMBwGA1UECgwVT3BlbiBTb3VyY2UgRGV2ZWxvcGVy # MS4wLAYDVQQDDCVPcGVuIFNvdXJjZSBEZXZlbG9wZXIgVGhvbWFzIE5hdW5oZWlt # MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAphp2ljSmVsRB6gvkEq7A # DUMENpKZiDFoAfF1ktD1nCwaVyI+bJ0ROjHwAn4iJgS+vlBDPOUIw2GLHkiqUhOg # HSwzg6gj45GNmt6zxsKP0zPlhMB/fb0zmz7/F7e3yfKCXLfhbHuk0Es9gJ+8Wmet # 9StVwwSE/K0MpW06GKA9nPWyE4N/ADwigDIj9rsTKJR4cve9w8G1b3zohpUc/qMr # onab+HVw5r9b3sOBnnaXC6zvrX4fovRz1ztwfWjRjILLoFG6mW/TuK5F2ICD3Rhl # 9R2rJWWWhzTDMsQlzvX+/SaM74q3OFpIVNdhvtmWU9pKUBRAyYMijcDqKEFGSzrc # 8JFmGMpP5TuroWF+N21d3px2eARvW1mV0DCfFAH+Og20SH/gVW6/ltQQEkH/z/Zt # 49ARyJrgESLOQtlMCIYSoKISdkk4ml4VUS1E/PcbzXEYj3a7m6DOxcd3WuY03UT9 # cnk3p0LMN6LeLYAZgVY+HRloZlH+kW2SkOHNuWgtXvZhAgMBAAGjggF4MIIBdDAM # BgNVHRMBAf8EAjAAMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6Ly9jY3NjYTIwMjEu # Y3JsLmNlcnR1bS5wbC9jY3NjYTIwMjEuY3JsMHMGCCsGAQUFBwEBBGcwZTAsBggr # BgEFBQcwAYYgaHR0cDovL2Njc2NhMjAyMS5vY3NwLWNlcnR1bS5jb20wNQYIKwYB # BQUHMAKGKWh0dHA6Ly9yZXBvc2l0b3J5LmNlcnR1bS5wbC9jY3NjYTIwMjEuY2Vy # MB8GA1UdIwQYMBaAFN10XUwA23ufoHTKsW73PMAywHDNMB0GA1UdDgQWBBTBvGyH # MXj1dMbVnxuof9oP4PY7cDBLBgNVHSAERDBCMAgGBmeBDAEEATA2BgsqhGgBhvZ3 # AgUBBDAnMCUGCCsGAQUFBwIBFhlodHRwczovL3d3dy5jZXJ0dW0ucGwvQ1BTMBMG # A1UdJQQMMAoGCCsGAQUFBwMDMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF # AAOCAgEAkC165c7/r6y3VeEEpNXas9bQs6ZpKPdJffHkf9xoPi4kWvAIWGc4ocG1 # DJLb18RXiDYShDmT5sb/6I08FeipKi2DNJkuOq6yOTJ3S7+hNFIZCyd6i7WKO8ea # P5I+FAbyJP4+5coZoMg45bUa9JoocaJCm+5n7WiCfN7/EQpClW5Trm4+ZMbp7EMN # F1uLJgGCMnN0SsoUarp7PHlqupMxMC4Uhhe+lAP2ytcSIU81L97/qjrADbLWqICk # 9il5iGM+aZndguq/cmfH9rJj4GRZHWnG6WwnzYD9+KgYlRI6em+/9dq+IRiXe2Bs # thUuIaFW7U2gWw8tBTEYm1UBLHdJx+L1gjJCLs5KT/wy5wU32wZxNDqPUQejUC1x # GvU3cKqmLgRq+yNf3gFVfzqIoggyM3U9wQvnk7nBtCkpjXS2hdx4yxzcmrlWsDHr # z6+6/jeyQySzbYUlFmKnc4n9Mw2soHOajEua5poFARVRS5KgUBuiiQ21YFNYzWBo # qGuECaaAI/C5rGxapMJ3dAOBnHw6NLqBet+ZXBJEccX5y89MLlOQu+jDmJP8HieR # qDLlwfhVZnw4RY8Rus43NZBwhNlVuOUXqocHKmJhKNlekda3qsJj93eQBQOxKJcc # w5O08q9jukpH2+yzFAj+7ll33DuNUgWptogc4Awtbx3glzcEvJQwggaCMIIEaqAD # AgECAhAo8HfBHDa9/l90MkdwJy4DMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYT # AlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMT # G0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQTAeFw0yNjAzMTEwNzM0NTRaFw0z # NjAyMjcwNzM0NTRaMFAxCzAJBgNVBAYTAlBMMSEwHwYDVQQKDBhBc3NlY28gRGF0 # YSBTeXN0ZW1zIFMuQS4xHjAcBgNVBAMMFUNlcnR1bSBUaW1lc3RhbXAgMjAyNjCC # AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALi9zfBw+xI12hi2F9uNQBNO # STkTIi13fuh3DiWybI5392bBfJw6tO7zDs7UqP/rejVf4xFTU3XNOPsuOFkP5SPF # snWwn3VJL+NhNhsuujvYsQtaQnvNo1wk46reTgws1L8OFEdKaxgyqT4zOQx81mLw # NZSqXNU9Rrb9oAJWJF4Mydz+C1ebu0D0+vgM3tqAQ8TU203fqgKCPqIdHloaFqH9 # eyR+8tnpmy2hnnN59ZOtmsuHeRwf5iTOhlhf0d/qqquLOSIGPgGVL1Hj5XBUox2O # E7tQRr+l/xxNbL6OuwBO4Aa2EbuxYg37cpXv5i2/lTL4NASB/duf9W4fjA3Ro+GP # +bC54n2OYiM3Cq8z92ejx8F5HWO2e4/75eC95B9tMw/eq7109GDyW2+2ut5Bn0o8 # MXJTERkyystWbqcnUkomA97qfiDCwvpWuOMdfTOksXe9S+xDxOJaCbiyTivgPulx # cT+tfQTRXBr5UwtPvc+INnafOEilzVHA11tv+pHcylryxBc08cUihf/ttqkZS0bm # x3VxQay8uohaTp0//ECA9vYGW51SKdJMJhXvKyIdBLy8WYNchTt8yRITv9Adxg9J # qGujh+TnP1bW3aQjFtlEl7SzgNeI8OVA/CJAWWUuRT3Au9ToOpHy/flrrknhN4he # Ew7/6ILMv4HSsj0YZ4otAgMBAAGjggFQMIIBTDB1BggrBgEFBQcBAQRpMGcwOwYI # KwYBBQUHMAKGL2h0dHA6Ly9zdWJjYS5yZXBvc2l0b3J5LmNlcnR1bS5wbC9jdHNj # YTIwMjEuY2VyMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u # Y29tMB8GA1UdIwQYMBaAFL5UAi+/QGxzQ86sCSVOnkNEGu7gMAwGA1UdEwEB/wQC # MAAwOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL3N1YmNhLmNybC5jZXJ0dW0ucGwv # Y3RzY2EyMDIxLmNybDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E # BAMCB4AwIgYDVR0gBBswGTAIBgZngQwBBAIwDQYLKoRoAYb2dwIFAQswHQYDVR0O # BBYEFCM5aiirmhKnpKfyH/lFmr+N/oIDMA0GCSqGSIb3DQEBDAUAA4ICAQBm/pOb # rAveTqYFPCSthxNVwBHTW4TfuBQY2KGCdMlH6ALjVPYf2XARNGMTXScR0JQ2/c2L # xLpoHHhRwxFf3d933DhFS165rYj6iPSQKm41rc9AMpTCjMdmai1aK9o870pOzTzL # RhWFVIxm+5Qf+t4V5z14tPoUsAWdfRKD1Zzvzfe5AHlE9rxOZcGROiULxs3Edf7Z # J8RZI0jzD9juj2oEX2XS7BVKuNCK1W0h1mjapv028yqo8UtZh5/1Ib48Yyp88BDY # Shh5PSGUUuS5tYXVfcduIPBe3PdNTYRYdT5VNxkwLgjMzIcr/6SH2fejFJDjnCxj # vXLuarErJg/IdI+hXdGIBDVOGv1zrkF4ktoQvFZIp5DIAYh0GwyUhc87qqn5UaYT # j2+iyiBDSNh7aGCrkuAnqg7Qh7frhX+QhMGeJ9SpLpnG76qN85hLyjvYR6gRYiLT # j8G4fxjqV400/buPw0rBo9yJZyq/8cGdCOLPOYf9qpefTcrZKeO9pg+UJHx8UABb # aTqizRxm8sUSEycccCCFhIit3AI/3wGuSOADeiA676NcQ7+dsP04YvzvdN7pTZNy # g672WpBDJdg0eItHl4yomvNeIOWh1qs4QqOW6TI8Ocsi4uSDTLorxLUDnBTwKQzz # pxd1YUctEgRh32R0J9FkFkeEruJU94RljFr1uDCCBrkwggShoAMCAQICEQCZo4AK # JlU7ZavcboSms+o5MA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAG # A1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVt # IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3Rl # ZCBOZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjE4WhcNMzYwNTE4MDUzMjE4WjBW # MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEu # MSQwIgYDVQQDExtDZXJ0dW0gQ29kZSBTaWduaW5nIDIwMjEgQ0EwggIiMA0GCSqG # SIb3DQEBAQUAA4ICDwAwggIKAoICAQCdI88EMCM7wUYs5zNzPmNdenW6vlxNur3r # Lfi+5OZ+U3iZIB+AspO+CC/bj+taJUbMbFP1gQBJUzDUCPx7BNLgid1TyztVLn52 # NKgxxu8gpyTr6EjWyGzKU/gnIu+bHAse1LCitX3CaOE13rbuHbtrxF2tPU8f253Q # gX6eO8yTbGps1Mg+yda3DcTsOYOhSYNCJiL+5wnjZ9weoGRtvFgMHtJg6i671OPX # IciiHO4Lwo2p9xh/tnj+JmCQEn5QU0NxzrOiRna4kjFaA9ZcwSaG7WAxeC/xoZSx # F1oK1UPZtKVt+yrsGKqWONoK6f5EmBOAVEK2y4ATDSkb34UD7JA32f+Rm0wsr5aj # zftDhA5mBipVZDjHpwzv8bTKzCDUSUuUmPo1govD0RwFcTtMXcfJtm1i+P2UNXad # PyYVKRxKQATHN3imsfBiNRdN5kiVVeqP55piqgxOkyt+HkwIA4gbmSc3hD8ke66t # 9MjlcNg73rZZlrLHsAIV/nJ0mmgSjBI/TthoGJDydekOQ2tQD2Dup/+sKQptalDl # ui59SerVSJg8gAeV7N/ia4mrGoiez+SqV3olVfxyLFt3o/OQOnBmjhKUANoKLYlK # mUpKEFI0PfoT8Q1W/y6s9LTI6ekbi0igEbFUIBE8KDUGfIwnisEkBw5KcBZ3XwnH # mfznwlKo8QIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU # 3XRdTADbe5+gdMqxbvc8wDLAcM0wHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61Pgc # ptE6Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMDAGA1Ud # HwQpMCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYI # KwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0 # dW0uY29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwv # Y3RuY2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0 # cDovL3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQB1iFgP5Y9Q # KJpTnxDsQ/z0O23JmoZifZdEOEmQvo/79PQg9nLF/GJe6ZiUBEyDBHMtFRK0mXj3 # Qv3gL0sYXe+PPMfwmreJHvgFGWQ7XwnfMh2YIpBrkvJnjwh8gIlNlUl4KENTK5DL # qsYPEtRQCw7R6p4s2EtWyDDr/M58iY2UBEqfUU/ujR9NuPyKk0bEcEi62JGxauFY # zZ/yld13fHaZskIoq2XazjaD0pQkcQiIueL0HKiohS6XgZuUtCKA7S6CHttZEsOb # QJ1j2s0urIDdqF7xaXFVaTHKtAuMfwi0jXtF3JJphrJfc+FFILgCbX/uYBPBlbBI # P4Ht4xxk2GmfzMn7oxPITpigQFJFWuzTMUUgdRHTxaTSKRJ/6Uh7ki/pFjf9sUAS # WgxT69QF9Ki4JF5nBIujxZ2sOU9e1HSCJwOfK07t5nnzbs1LbHuAIGJsRJiQ6HX/ # DW1XFOlXY1rc9HufFhWU+7Uk+hFkJsfzqBz3pRO+5aI6u5abI4Qws4YaeJH7H7M8 # X/YNoaArZbV4Ql+jarKsE0+8XvC4DJB+IVcvC9Ydqahi09mjQse4fxfef0L7E3hh # o2O3bLDM6v60rIRUCi2fJT2/IRU5ohgyTch4GuYWefSBsp5NPJh4QRTP9DC3gc5Q # EKtbrTY0Ka87Web7/zScvLmvQBm8JDFpDjCCBrkwggShoAMCAQICEQDn/2nHOzXO # S5Em2HR8aKWHMA0GCSqGSIb3DQEBDAUAMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UE # ChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl # cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBO # ZXR3b3JrIENBIDIwHhcNMjEwNTE5MDUzMjA3WhcNMzYwNTE4MDUzMjA3WjBWMQsw # CQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQw # IgYDVQQDExtDZXJ0dW0gVGltZXN0YW1waW5nIDIwMjEgQ0EwggIiMA0GCSqGSIb3 # DQEBAQUAA4ICDwAwggIKAoICAQDpEh8ENe25XXrFppVBvoplf0530W0lddNmjtv4 # YSh/f7eDQKFaIqc7tHj7ox+u8vIsJZlroakUeMS3i3T8aJRC+eQs4FF0GqvkM6+W # ZO8kmzZfxmZaBYmMLs8FktgFYCzywmXeQ1fEExflee2OpbHVk665eXRHjH7MYZIz # Nnjl2m8Hy8ulB9mR8wL/W0v0pjKNT6G0sfrx1kk+3OGosFUb7yWNnVkWKU4qSxLv # 16kJ6oVJ4BSbZ4xMak6JLeB8szrK9vwGDpvGDnKCUMYL3NuviwH1x4gZG0JAXU3x # 2pOAz91JWKJSAmRy/l0s0l5bEYKolg+DMqVhlOANd8Yh5mkQWaMEvBRE/kAGzIqg # WhwzN2OsKIVtO8mf5sPWSrvyplSABAYa13rMYnzwfg08nljZHghquCJYCa/xHK9a # cev9UD7Y+usr15d7mrszzxhF1JOr1Mpup2chNSBlyOObhlSO16rwrffVrg/SzaKf # SndS5swRhr8bnDqNJY9TNyEYvBYpgF95K7p0g4LguR4A++Z1nFIHWVY5v0fNVZmg # zxD9uVo/gta3onGOQj3JCxgYx0KrCXu4yc9QiVwTFLWbNdHFSjBCt5/8Q9pLuRhV # ocdCunhcHudMS1CGQ/Rn0+7P+fzMgWdRKfEOh/hjLrnQ8BdJiYrZNxvIOhM2aa3z # EDHNwwIDAQABo4IBVTCCAVEwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUvlQC # L79AbHNDzqwJJU6eQ0Qa7uAwHwYDVR0jBBgwFoAUtqFUOQLDoD+Oirz61PgcptE6 # Dv0wDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMDAGA1UdHwQp # MCcwJaAjoCGGH2h0dHA6Ly9jcmwuY2VydHVtLnBsL2N0bmNhMi5jcmwwbAYIKwYB # BQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vc3ViY2Eub2NzcC1jZXJ0dW0u # Y29tMDIGCCsGAQUFBzAChiZodHRwOi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY3Ru # Y2EyLmNlcjA5BgNVHSAEMjAwMC4GBFUdIAAwJjAkBggrBgEFBQcCARYYaHR0cDov # L3d3dy5jZXJ0dW0ucGwvQ1BTMA0GCSqGSIb3DQEBDAUAA4ICAQC4k1l3yUwV/ZQH # CKCneqAs8EGTnwEUJLdDpokN/dMhKjK0rR5qX8nIIHzxpQR3TAw2IRw1Uxsr2Pli # G3bCFqSdQTUbfaTq6V3vBzEebDru9QFjqlKnxCF2h1jhLNFFplbPJiW+JSnJTh1f # KEqEdKdxgl9rVTvlxfEJ7exOn25MGbd/wGPwuSmMxRJVO0wnqgS7kmoJjNF9zqee # hFSDDP8ZVkWg4EZ2tIS0M3uZmByRr+1Lkwjjt8AtW83mVnZTyTsOb+FNfwJY7DS4 # FmWhkRbgcHRetreoTirPOr/ozyDKhT8MTSTf6Lttg6s6T/u08mDWw6HK04ZRDfQ9 # sb77QV8mKgO44WGP31vXnVKoWVJpFBjPvjL8/Zck/5wXX2iqjOaLStFOR/IQki+E # hn4zlcgVm22ZVCBPF+l8nAwUUShCtKuSU7GmZLKCmmxQMkSiWILTm8EtVD6AxnJh # oq8EnhjEEyUoflkeRF2WhFiVQOmWTwZRr44IxWGkNJC6tTorW5rl2Zl+2e9JLPYf # 3pStAPMDoPKIjVXd6NW2+fZrNUBeDo2eOa5Fn7Brs/HLQff5Xgris5MeUbdVgDrF # 8uxO6cLPvZPo63j62SsNg55pTWk9fUIF9iPoRbb4QurjoY/woI1RAOKtYtTic6aA # Jq3u83RIPpGXBSJKwx4KJAOZnCDCtTGCBqIwggaeAgEBMGowVjELMAkGA1UEBhMC # UEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIGA1UEAxMb # Q2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBAhBX5gfP8LabvnxCXoItEBa/MA0G # CWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZI # hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC # ARUwLwYJKoZIhvcNAQkEMSIEIBUuYxlE0R5p21mQh8ss8Sgefqm3Me7OjLkaKm6e # aFRBMA0GCSqGSIb3DQEBAQUABIIBgD0qNYuVQ05Liu5Yjmxc7Pa5dal09sASeUm3 # EJQk/+w5YLYx99548jAEujRZqSxa1Y7PDi9ukuHuDnEyi2u5R20eEu6hYkqCmrKZ # 2EZgTQ0ZLmTs0IOzierqgZrMfpyTT5AE6W3mvkDjfbtfhLSEK1WdvqC5B8QL4XHm # +GHYbmFVTtOb6S59aKnXJ9Uxtd1Qt2Q2sbx1bs276Geb+Dx20SgQhvicb7h0Q7KB # 6+qNKD54FWftVRRvAsgAAcFgb/yUPfWUlLkGk5zRiNtqaVEWYY+e0hzro3z2M1WM # BZ9A6HxitHRIoOkDQk2TfkndMzl3SQ9EqWlZlgcZyJulGm15YouF97REJjqX8qkQ # vQ92juWnytaQh4vQuTBdgcnfeoWQWcqU+foHiG7qjWYqLgyJAW6ltgwksEsgesnw # Yk149EKAc0aQ6W4NGoW6kZfznkb3drwvYqaGaOF4hPNA8F0bipFrimW8jp3TBFke # 4Rsvbrw8CcGQP58YJthELE4hAyULQaGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQxNDVaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDAcoEz3bFKDFvdFcQ53W08FCCuciQ5fwLbN # jLGBTXIDMzAagdwlp/2W0DCzqjjaytkwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIAtDD5QbzrHdZSnpxYfQ7cwP6Fuwm2SANxv8JhaIW8j7tC23fm # unhaYhPc+6PuJXXCSnouwK8jQuIS1tRIGYy4laENOuSL9qKobEFwiwrrB7/7+2VK # qneR1oWaeEK4xWJsQuTwNtZ04k1q4c6sDmmK7QAO4R8VLbR1O9Kr4maTTCxB49f7 # tjSSagrbqCHzwHbWVwkLkqZWJW/sRsBiVRx9YjK/9G+QOcTLBYvA17kkZElyMljt # C1qX5N/CcrMD51+ltvOXjBUXYmJqf+6MBkZjblIx0ycFHoD1S+Uy2ka/OALBluv9 # L1ikvkTp0bnusPLktF/K9zICQsrLeyxZPeU+F4DBYT5YHuMS9TVIA+4qkBblN9+8 # +riP3Z53cEc6GDPC23paWK+8C1ROqUUC7OStkGUlAl1QevjsrKoQKfRFULiKIVE3 # lhRqMsMSLG1o7GB1e2igY8piRXn9ijoMyG6pWkvt10/65BECrQKjRDvcSDQkn8Fg # lHhjjgf3gcOl1lqOTnkT2mK9xOYdtJID0cTTkQexGM2ZaclbnO7fYxb7ffgpspAv # tvIigaY0nAeeEGaaWDsYpE/gd7nJTtcaf16AJgnZpgPMrAd2Q7FIsHPIAz7WDJmy # KxUJF7sTYO27Wr34eYvbH2emXJnhwVdQX/XU4ijXdalljinGFXes1rn0Hfs= # SIG # End signature block |