Public/PrivilegedAccess/Get-EntraOpsPrivilegedIdGovRoles.ps1
|
<# .SYNOPSIS Get a list of delegated administrator roles in Microsoft Entra Identity Governance. .DESCRIPTION Get a list of delegated administrator roles in Microsoft Entra Identity Governance. .PARAMETER TenantId Tenant ID of the Microsoft Entra ID tenant. Default is the current tenant ID. .PARAMETER PrincipalTypeFilter Filter for principal type. Default is User, Group, ServicePrincipal. Possible values are User, Group, ServicePrincipal. .PARAMETER ExpandGroupMembers Expand group members for transitive role assignments. Default is $true. .PARAMETER SampleMode Use sample data for testing or offline mode. Default is $False. .PARAMETER ExcludeInvalidOrDeletedScopes Exclude role assignments whose RoleAssignmentScopeName could not be resolved (shown as "Invalid or deleted object", e.g. a deleted access package catalog) from the output. Default is $true. .EXAMPLE Get a list of delegated administrator assignment in Identity Governance access packages and catalogs. Get-EntraOpsPrivilegedIdGovRoles #> function Get-EntraOpsPrivilegedIdGovRoles { param ( [Parameter(Mandatory = $False)] [System.String]$TenantId , [Parameter(Mandatory = $False)] [ValidateSet("User", "Group", "ServicePrincipal")] [Array]$PrincipalTypeFilter = ("User", "Group", "ServicePrincipal") , [Parameter(Mandatory = $False)] [System.Boolean]$ExpandGroupMembers = $true , [Parameter(Mandatory = $false)] [System.Boolean]$SampleMode = $False , [Parameter(Mandatory = $False)] [System.Boolean]$ExcludeInvalidOrDeletedScopes = $true , [Parameter(Mandatory = $False)] [System.Collections.Generic.List[psobject]]$WarningMessages ) # Set Error Action $ErrorActionPreference = "Stop" $ElmRbacAssignments = @() if ($SampleMode -eq $True) { Write-Warning "Not supported yet!" } else { $ElmRoleDefinitions = Invoke-EntraOpsMsGraphQuery -Uri "/beta/roleManagement/entitlementManagement/roleDefinitions?`$select=id,displayName,description" $ElmRoleAssignments = Invoke-EntraOpsMsGraphQuery -Uri "/beta/roleManagement/entitlementManagement/roleAssignments?`$select=id,principalId,roleDefinitionId,appScopeId" } $ElmRoleAssignmentPrincipals = ($ElmRoleAssignments | select-object principalId -Unique).principalId Write-Host "Processing $($ElmRoleAssignmentPrincipals.Count) Identity Governance role principals..." # Optimization: every principal's role assignments are already present in $ElmRoleAssignments # (fetched once above) - group them in memory instead of re-querying Graph per principal with an # advanced query ($count=true&$filter=principalId eq ..., ConsistencyLevel eventual). That # per-principal call returned data already in hand, and its cost scales with the tenant's total # Entitlement Management role assignment count, not just this principal's assignments - so it gets # slower over time purely as the tenant's Identity Governance footprint grows, independent of how # many principals/assignments are actually relevant here. $ElmRoleAssignmentsByPrincipal = $ElmRoleAssignments | Group-Object -Property principalId -AsHashTable -AsString # Optimization: resolve every principal's directory object type with a single batched call instead # of one /beta/directoryObjects/$Principal GET per principal. $PrincipalTypeById = @{} if ($ElmRoleAssignmentPrincipals.Count -gt 0) { $ResolutionBatchSize = 100 for ($i = 0; $i -lt $ElmRoleAssignmentPrincipals.Count; $i += $ResolutionBatchSize) { $Batch = @($ElmRoleAssignmentPrincipals[$i..([Math]::Min($i + $ResolutionBatchSize - 1, $ElmRoleAssignmentPrincipals.Count - 1))]) $Body = @{ ids = $Batch; types = @('user', 'group', 'servicePrincipal') } | ConvertTo-Json try { $ResolvedPrincipals = Invoke-EntraOpsMsGraphQuery -Method POST -Uri "/beta/directoryObjects/getByIds" -Body $Body -OutputType PSObject foreach ($ResolvedPrincipal in $ResolvedPrincipals) { $PrincipalTypeById[$ResolvedPrincipal.id] = $ResolvedPrincipal.'@odata.type'.Replace('#microsoft.graph.', '') } } catch { Write-Verbose "Batched principal type resolution failed for this batch, falling back to per-principal lookups: $($_.Exception.Message)" } } } # Optimization: cache catalog display name lookups by CatalogId - the same catalog is commonly # referenced by role assignments across multiple principals. Pre-populated with ONE list call # instead of an individual GET per referenced catalog: role assignments regularly reference # deleted catalogs, and each of those previously cost a full (404) round-trip. $CatalogDisplayNameCache = @{} $AllCatalogsFetched = $false try { $AllCatalogs = Invoke-EntraOpsMsGraphQuery -Uri "/beta/identityGovernance/entitlementManagement/accessPackageCatalogs?`$select=id,displayName" -OutputType PSObject -ThrowOnFailure foreach ($Catalog in @($AllCatalogs)) { if ($null -ne $Catalog.id) { $CatalogDisplayNameCache[$Catalog.id] = $Catalog.displayName } } $AllCatalogsFetched = $true } catch { Write-Verbose "Bulk catalog list fetch failed, falling back to per-catalog lookups: $($_.Exception.Message)" } $PrincipalCounter = 0 $ElmRbacAssignments = foreach ($Principal in $ElmRoleAssignmentPrincipals) { $PrincipalCounter++ if (($PrincipalCounter % 10) -eq 0 -or $PrincipalCounter -eq $ElmRoleAssignmentPrincipals.Count) { $PercentComplete = [math]::Round(($PrincipalCounter / $ElmRoleAssignmentPrincipals.Count) * 100, 0) Write-Progress -Activity "Processing IdGov Role Principals" -Status "Processing principal $PrincipalCounter of $($ElmRoleAssignmentPrincipals.Count)" -PercentComplete $PercentComplete } Write-Verbose "Get identity information from permanent member $Principal" # Reset to avoid carrying over the ObjectType from a previous iteration when resolution fails $ObjectType = $null if ($PrincipalTypeById.ContainsKey($Principal)) { $ObjectType = $PrincipalTypeById[$Principal] } else { # Fallback for principals the batched resolution missed (e.g. deleted, or a type outside # user/group/servicePrincipal) try { $PrincipalProfile = Invoke-EntraOpsMsGraphQuery -Method Get -Uri "https://graph.microsoft.com/beta/directoryObjects/$($Principal)" -OutputType PSObject $ObjectType = $PrincipalProfile.'@odata.type'.Replace('#microsoft.graph.', '') } catch { $WarningMessage = "Issue to resolve directory object $Principal! $($_.Exception.Message)" if ($null -ne $WarningMessages) { $WarningMessages.Add([pscustomobject]@{ Timestamp = (Get-Date) Type = "ObjectResolutionError" ObjectId = $Principal Message = $WarningMessage }) } Write-Warning $WarningMessage } } # Optimization: filter the already-fetched bulk role assignments in memory (see # $ElmRoleAssignmentsByPrincipal above) instead of re-querying Graph per principal. $AllPrinicpalElmRoleAssignments = $ElmRoleAssignmentsByPrincipal["$Principal"] foreach ($ElmPrincipalRoleAssignment in $AllPrinicpalElmRoleAssignments) { $Role = ($ElmRoleDefinitions | where-object { $_.id -eq $ElmPrincipalRoleAssignment.roleDefinitionId }) try { if ($ElmPrincipalRoleAssignment.appScopeId -eq "/") { $AccessPackageDisplayName = "Directory" } else { $CatalogId = $($ElmPrincipalRoleAssignment.appScopeId).Replace("/AccessPackageCatalog/", "") if ($CatalogDisplayNameCache.ContainsKey($CatalogId)) { $AccessPackageDisplayName = $CatalogDisplayNameCache[$CatalogId] } elseif ($AllCatalogsFetched) { # Authoritative full catalog list was fetched - a missing id means the # catalog no longer exists, no per-catalog probe needed. $AccessPackageDisplayName = "Invalid or deleted object" if ($null -ne $WarningMessages) { $WarningMessages.Add([pscustomobject]@{ Timestamp = (Get-Date) Type = "CatalogResolution" ObjectId = $CatalogId Message = "Access Package Catalog $CatalogId not found (likely deleted)." }) } $CatalogDisplayNameCache[$CatalogId] = $AccessPackageDisplayName } else { $CatalogObj = Invoke-EntraOpsMsGraphQuery -Uri "/beta/identityGovernance/entitlementManagement/accessPackageCatalogs/$($CatalogId)" -OutputType PSObject -WarningAction SilentlyContinue if ($null -ne $CatalogObj) { $AccessPackageDisplayName = $CatalogObj.displayName } else { $AccessPackageDisplayName = "Invalid or deleted object" if ($null -ne $WarningMessages) { $WarningMessages.Add([pscustomobject]@{ Timestamp = (Get-Date) Type = "CatalogResolution" ObjectId = $CatalogId Message = "Access Package Catalog $CatalogId not found (likely deleted)." }) } } $CatalogDisplayNameCache[$CatalogId] = $AccessPackageDisplayName } } } catch { $AccessPackageDisplayName = "Invalid or deleted object" } [pscustomobject]@{ RoleAssignmentId = $ElmPrincipalRoleAssignment.Id RoleAssignmentScopeId = $ElmPrincipalRoleAssignment.appScopeId RoleAssignmentScopeName = $AccessPackageDisplayName RoleAssignmentType = "Direct" RoleAssignmentSubType = "" PIMManagedRole = $False PIMAssignmentType = "Permanent" RoleDefinitionName = $Role.displayName RoleDefinitionId = $ElmPrincipalRoleAssignment.roleDefinitionId RoleType = "BuiltInRole" RoleIsPrivileged = $Role.isPrivileged ObjectId = $Principal ObjectTenantId = $TenantId ObjectType = $ObjectType.toLower() TransitiveByObjectId = "" TransitiveByObjectDisplayName = "" TransitiveByNestingObjectIds = $null TransitiveByNestingObjectDisplayNames = $null } } } # List all eligible roleAssignment #region Collect transitive assignments by group members of Role-Assignable Groups if ($ExpandGroupMembers -eq $True) { Write-Verbose "Expanding groups for direct or transitive ELM role assignments" $GroupsWithRbacAssignment = $ElmRbacAssignments | where-object { $_.ObjectType -eq "group" } $AllTransitiveMembers = @() foreach ($GroupWithRbacAssignment in $GroupsWithRbacAssignment) { $GroupObjectDisplayName = (Invoke-EntraOpsMsGraphQuery -Method Get -Uri "https://graph.microsoft.com/beta/groups/$($GroupWithRbacAssignment.ObjectId)" -OutputType PSObject).displayName $TransitiveMembers = Get-EntraOpsPrivilegedTransitiveGroupMember -GroupObjectId $($GroupWithRbacAssignment.ObjectId) -WarningMessages $WarningMessages foreach ($TransitiveMember in $TransitiveMembers) { $Member = [pscustomobject]@{ displayName = $TransitiveMember.displayName id = $TransitiveMember.id '@odata.type' = $TransitiveMember.'@odata.type' RoleAssignmentSubType = $TransitiveMember.RoleAssignmentSubType GroupObjectDisplayName = $GroupObjectDisplayName GroupObjectId = $GroupWithRbacAssignment.ObjectId NestingObjectIds = $TransitiveMember.NestingObjectIds NestingObjectDisplayNames = $TransitiveMember.NestingObjectDisplayNames } $AllTransitiveMembers += $Member } } $ElmRbacTransitiveAssignments = [System.Collections.Generic.List[object]]::new() foreach ($RbacAssignmentByGroup in ($GroupsWithRbacAssignment | where-object { $_.ObjectType -eq "group" }) ) { $RbacAssignmentByNestedGroupMembers = $AllTransitiveMembers | Where-Object { $_.GroupObjectId -eq $RbacAssignmentByGroup.ObjectId } if ($RbacAssignmentByNestedGroupMembers.Count -gt 0) { $RbacAssignmentByNestedGroupMembers | foreach-object { $TransitiveAssignment = [pscustomobject]@{ RoleAssignmentId = $RbacAssignmentByGroup.RoleAssignmentId RoleAssignmentScopeId = $RbacAssignmentByGroup.RoleAssignmentScopeId RoleAssignmentScopeName = $RbacAssignmentByGroup.RoleAssignmentScopeName RoleAssignmentType = "Transitive" RoleAssignmentSubType = $_.RoleAssignmentSubType PIMManagedRole = $RbacAssignmentByGroup.PIMManagedRole PIMAssignmentType = $RbacAssignmentByGroup.PIMAssignmentType RoleDefinitionName = $RbacAssignmentByGroup.RoleDefinitionName RoleDefinitionId = $RbacAssignmentByGroup.RoleDefinitionId RoleType = $RbacAssignmentByGroup.RoleType RoleIsPrivileged = $RbacAssignmentByGroup.RoleIsPrivileged ObjectId = $_.Id ObjectType = $_.'@odata.type'.Replace('#microsoft.graph.', '').toLower() TransitiveByObjectId = $RbacAssignmentByGroup.ObjectId TransitiveByObjectDisplayName = $_.GroupObjectDisplayName TransitiveByNestingObjectIds = $_.NestingObjectIds TransitiveByNestingObjectDisplayNames = $_.NestingObjectDisplayNames } $ElmRbacTransitiveAssignments.Add($TransitiveAssignment) | Out-Null } } else { if ($null -ne $WarningMessages) { $WarningMessages.Add([pscustomobject]@{ Timestamp = (Get-Date) Type = "EmptyGroup" ObjectId = $RbacAssignmentByGroup.ObjectId Message = "Group has no members or members could not be resolved." }) } } } } #endregion $AllElmRbacAssignments = @() $AllElmRbacAssignments += $ElmRbacAssignments $AllElmRbacAssignments += $ElmRbacTransitiveAssignments $AllElmRbacAssignments = $AllElmRbacAssignments | where-object { $_.ObjectType -in $PrincipalTypeFilter } if ($ExcludeInvalidOrDeletedScopes -eq $true) { $AllElmRbacAssignments = $AllElmRbacAssignments | where-object { $_.RoleAssignmentScopeName -ne "Invalid or deleted object" } } $AllElmRbacAssignments = $AllElmRbacAssignments | select-object -Unique * $AllElmRbacAssignments | Sort-Object RoleAssignmentId, RoleAssignmentType, ObjectId } # SIG # Begin signature block # MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBhHHP9YylL5esu # WpegoVlZn7ILR2ynI5peQjTXpGwHp6CCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # 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 # ARUwLwYJKoZIhvcNAQkEMSIEIOWPWK/h5iK3gcfy1QU5RynnVSeeNr/uPUNBYRkZ # XnkiMA0GCSqGSIb3DQEBAQUABIIBgF4YVnZSElqkZi1BJo+hVPnAJOYF6ddD69c4 # LGlP70DjfG7Pp4tyN4HMnK9WXrDM6Lma/8URs6sJucn5wErMxjaMAfPquNSg4TpB # mj8RlL/fqIJf6I7PV6Kgiuik9athr2RUItLUdUVTUJpjPB0Dwt+vYwvZ5PjzOfyv # fcQz/NTM9xUDjKMhug9tN4d0Ba2dUd0r+eK/t9r/Hd9whYMTePnzCRtVJwtQ0wmq # ZjetNz6gqvdrp7SR8q8OR94F3uAk/cpZpO/Q5TeMnkM6dzm6scldCqikAP8WYrZd # +0OnD+uxT+6hSOi85qsR5AXw2A7slZQUcuzmwkV+4l+Fxy7BDybOufzOgMrkdb+0 # g9PJ0dGXN7RVOd7luSXKh2FExIRdKBE6UbHIfbZFSf9HQvhiuUmqJ+xRNGS9v8fg # KZADlPo8QtoYONyXRImNzQCBJvdvPuozq3Q7StCgiXhVlJHycUcRLDODpGUjQBek # fkU5che0jFsPskbZZo+FPBI+NpD4hqGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQxMjdaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDB3mGXZIM4hbkMkaiSc7O8CK/GZQtSquMQm # /AjqTimXpeJ3uqB8w/BSUa1ZV+aed38wgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIARsF5baPHcBBWcWcZ11lM2ef6yXtFyvFa2pHDVllHxYljIN4G # dgN+QDVWRPYHf2TUR6BsEmVJdvuhB6ozoqd9Uj0oIFJlf7hQAiMhGoQhuVcqmh39 # O1uuAnj6STqxWmuUpZE8UnB90ZSMwkunO1kiBkWDCDMl/v9rxfxsHegCvYXefMrA # XAUuQ0chj1esvkWbV9UfpWsdaDwXJXSNGpfCmfaXImFY+ZfpllQgaZEuwdJiz/iH # p6j08+qwCzfcKTcUkxGRwV1LEdxFQzoG7GU2VYwnRD0/lT3uDg+tIosSH+JXPYTL # KXk0rnac0AAVu0x+Y3Jr+NcGzGsuiLsQJD7zGzOhh2K37zHl2QhNDwLFnpC/Yoxv # 6og/isA/+EQHkZFG0i7ttaDcQleMYdvMWRw0+otX84QqpZBnMzZuM8OfSu+dvZM+ # GmOAdVJQOFylNYA53VuNWg38hn6qC/f9HlqB4/63dOJF+GwGo2YOQbQ3RXhB24JR # C0/fY1EHMPlovmSw39XfFdk4L3F5v2LN8Bn5D3wkYuQxTgkXA4Y753/Gxlc39VZZ # WhpMrGhJSm9WoBLC/1FVUZ3inNCSXZ7mWt5YTzj2wIjhkqNf/424tn3Dxnm4EF+z # 2Gr1ncOS8ILdHABHesEIJ32Oav31jGGwWIUOc5TMUCrn/2GN8pr9xONr6zA= # SIG # End signature block |