Public/PrivilegedAccess/Update-EntraOpsPrivilegedUnprotectedAdministrativeUnit.ps1
|
<# .SYNOPSIS Add privileged users without any protection (by role-assignable groups, assigned directory role or existing RMAU membership) to an Restricted Management Administrative Units to avoid delegated management by lower privileged user. .DESCRIPTION Add privileged users without any protection (by role-assignable groups, assigned directory role or existing RMAU membership) to an Restricted Management Administrative Units to avoid delegated management by lower privileged user. .PARAMETER ApplyToAccessTierLevel Array of Access Tier Levels to be processed. Default is ControlPlane, ManagementPlane. .PARAMETER FilterObjectType Array of object types to be processed. Default is User, Group. Service Principal and application objects are not supported to be protected by RMAU. .PARAMETER RbacSystems Array of RBAC systems to be processed. Default is Azure, AzureBilling, EntraID, IdentityGovernance, DeviceManagement, ResourceApps. .PARAMETER IncludeUnprotectedDevices Also sync devices owned by/associated to privileged users (OwnedDevices, AssociatedPawDevice) into the same tier's RMAU, unless already protected by another RMAU. .PARAMETER RemovalSafetyThreshold Fraction of current members that may be removed in a single run. Principal and device removals are planned together; an oversized plan applies no removals to that RMAU. Default is 0.5 (50%). .PARAMETER ForceRemovalBeyondSafetyThreshold Apply a reviewed removal plan even when it exceeds RemovalSafetyThreshold. .PARAMETER IncludeObjectDetails Include object display names and detailed API errors in console output. Defaults to ConsoleOutput.IncludeObjectDetails from EntraOpsConfig.json. Object IDs are always shown. .EXAMPLE Assign privileged users without any protection but privileges in RBAC Systems "IdentityGovernance" to Restricted Management Administrative Units Update-EntraOpsPrivilegedUnprotectedAdministrativeUnit -RbacSystems ("IdentityGovernance") #> function Update-EntraOpsPrivilegedUnprotectedAdministrativeUnit { [CmdletBinding()] param ( [Parameter(Mandatory = $False)] [ValidateSet("ControlPlane", "ManagementPlane")] [Array]$ApplyToAccessTierLevel = ("ControlPlane", "ManagementPlane") , [Parameter(Mandatory = $False)] [ValidateSet("User", "Group")] [Array]$FilterObjectType = ("User", "Group") , [Parameter(Mandatory = $False)] [ValidateSet("Azure", "EntraID", "IdentityGovernance", "ResourceApps", "DeviceManagement", "Defender")] [Array]$RbacSystems = ("Azure", "EntraID", "IdentityGovernance", "DeviceManagement", "Defender") , # Maximum fraction of current RMAU members removable per synchronization. [Parameter(Mandatory = $False)] [ValidateRange(0, 1)] [double]$RemovalSafetyThreshold = 0.5 , # Permits removals that exceed RemovalSafetyThreshold. [Parameter(Mandatory = $False)] [switch]$ForceRemovalBeyondSafetyThreshold , [Parameter(Mandatory = $False)] [switch]$IncludeUnprotectedDevices , [Parameter(Mandatory = $False)] [boolean]$IncludeObjectDetails = [bool]$Global:EntraOpsIncludeObjectDetails , [Parameter(Mandatory = $False)] [boolean]$ApplyRmauAssignmentsForUnprotectedObjects = $false ) # Get all privileged EAM objects $PrivilegedEamObjects = foreach ($RbacSystem in $RbacSystems) { Get-Content "$DefaultFolderClassifiedEam/$RbacSystem/$($RbacSystem).json" | ConvertFrom-Json } # Get all privileged EAM objects without any restricted management and their tier levels $UnprotectedPrivilegedUser = $PrivilegedEamObjects | Where-Object { $_.RestrictedManagementByRAG -ne $True -and ` $_.RestrictedManagementByAadRole -ne $True -and ` $_.RestrictedManagementByRMAU -ne $True -and ` $_.ObjectType -in $FilterObjectType } # Get all unique AdminTierLevels which needs to be iterated for assigning objects to Conditional Access Target Groups $PrivilegedEamTierLevels = Get-ChildItem -Path "$($DefaultFolderClassification)/Templates" -File -Recurse -Exclude *.Param.json | foreach-object { Get-Content $_.FullName -Filter "*.json" | ConvertFrom-Json } $SelectedPrivilegedEamTierLevels = $PrivilegedEamTierLevels | where-object { $_.EAMTierLevelName -in $ApplyToAccessTierLevel } | select-object -unique @{Name = 'AdminTierLevel'; Expression = 'EAMTierLevelTagValue' }, @{Name = 'AdminTierLevelName'; Expression = 'EAMTierLevelName' } # Always process the most privileged tier first, independent of how $ApplyToAccessTierLevel is ordered # in the config, so a device claimed by Tier 0 reaches its AU before a lower tier evaluates it. $SelectedPrivilegedEamTierLevels = @($SelectedPrivilegedEamTierLevels | Sort-Object { [int]$_.AdminTierLevel }) #endregion # Summary tracking $SyncSummary = [System.Collections.Generic.List[psobject]]::new() $TotalAdded = 0 $TotalRemoved = 0 $TotalKept = 0 $WarningMessages = New-Object -TypeName "System.Collections.Generic.List[psobject]" $TenantId = (Get-AzContext).Tenant.Id # Devices carry no EAM classification of their own - they inherit their tier from the privileged users # that own or use them. A user classified at more than one tier would otherwise make the same device # "desired" in every tier's AU, leaving the winner dependent on tier iteration order. Resolve each # device to its most privileged (lowest) tier once, up front, so the outcome is deterministic. $DeviceTierAssignment = @{} # AU names this run manages, so a sibling tier's AU is not mistaken for unrelated RMAU protection. $ManagedAdminUnitNames = @($SelectedPrivilegedEamTierLevels | ForEach-Object { "Tier" + $_.AdminTierLevel + "-" + $_.AdminTierLevelName + ".UnprotectedObjects" }) if ($IncludeUnprotectedDevices) { foreach ($Tier in $SelectedPrivilegedEamTierLevels) { $TierDeviceOwners = $PrivilegedEamObjects | Where-Object { $_.ObjectType -eq "user" -and ` $_.Classification.AdminTierLevel -contains $Tier.AdminTierLevel -and ` $_.Classification.AdminTierLevelName -contains $Tier.AdminTierLevelName -and ` ([string]::IsNullOrEmpty($_.ObjectTenantId) -or $_.ObjectTenantId -eq $TenantId) } foreach ($OwnedDeviceId in @($TierDeviceOwners | ForEach-Object { @($_.OwnedDevices) + @($_.AssociatedPawDevice) } | Where-Object { $_ })) { if (-not $DeviceTierAssignment.ContainsKey($OwnedDeviceId) -or [int]$Tier.AdminTierLevel -lt [int]$DeviceTierAssignment[$OwnedDeviceId]) { $DeviceTierAssignment[$OwnedDeviceId] = $Tier.AdminTierLevel } } } } Write-Host "" Write-Host "=========================================================" -ForegroundColor Cyan Write-Host " EntraOps - Unprotected Objects RMAU Sync" -ForegroundColor Cyan Write-Host " RBAC Systems : $($RbacSystems -join ', ')" -ForegroundColor Cyan Write-Host " Access Tiers : $($ApplyToAccessTierLevel -join ', ')" -ForegroundColor Cyan Write-Host " Object Types : $($FilterObjectType -join ', ')" -ForegroundColor Cyan Write-Host " Unprotected : $(@($UnprotectedPrivilegedUser).Count) objects identified across all tiers" -ForegroundColor Cyan Write-Host "=========================================================" -ForegroundColor Cyan Write-Host "" #region Assign all unprotected principals in Privileged EAM to restricted AUs or remove from RMAU if no longer privileged foreach ($TierLevel in $SelectedPrivilegedEamTierLevels) { $AdminUnitId = $null $AdminUnitName = "Tier" + $TierLevel.AdminTierLevel + "-" + $TierLevel.AdminTierLevelName + ".UnprotectedObjects" $AuAdded = 0 $AuRemoved = 0 $AuKept = 0 $AuFailed = 0 $AuStatus = "OK" Write-Host "---------------------------------------------------------" -ForegroundColor DarkCyan Write-Host " AU: $AdminUnitName" -ForegroundColor DarkCyan Write-Host "---------------------------------------------------------" -ForegroundColor DarkCyan $AdministrativeUnits = @(Invoke-EntraOpsMsGraphQuery -Method "GET" -Uri "/beta/administrativeUnits?`$filter=DisplayName eq '$(ConvertTo-EntraOpsODataStringLiteral -Value $AdminUnitName)'" -DisableCache -OutputType PSObject) # -AllowNotFound: a missing AU is handled by the SKIP branch below so the remaining # tiers still reconcile; duplicates still throw (F-10 safety). $AdminUnitId = (Select-EntraOpsUniqueGraphObject -InputObject $AdministrativeUnits -ObjectDescription "administrative unit '$AdminUnitName'" -AllowNotFound).id if ($null -eq $AdminUnitId) { Write-Warning " [SKIP] Could not find AU: $AdminUnitName" $WarningMessages.Add([PSCustomObject]@{ Type = "AuNotFound"; Message = "Could not find AU: $AdminUnitName" }) $SyncSummary.Add([PSCustomObject]@{ AdminUnit = $AdminUnitName MembersBefore = "N/A" Added = 0 Removed = 0 Kept = 0 Failed = 0 Status = "NOT FOUND" }) continue } # Get privileged objects for this tier level $UnprotectedPrivilegedUserOnTierLevel = @($UnprotectedPrivilegedUser | Where-Object { $_.Classification.AdminTierLevel -contains $TierLevel.AdminTierLevel -and $_.Classification.AdminTierLevelName -contains $TierLevel.AdminTierLevelName }) $CurrentAdminUnitMembers = @() $CurrentAdminUnitMembers = (Invoke-EntraOpsMsGraphQuery -Method "GET" -Uri "/beta/administrativeUnits/$($AdminUnitId)/members" -OutputType PSObject -DisableCache) $CurrentMemberCount = @($CurrentAdminUnitMembers.Id).Count $DesiredMemberCount = $UnprotectedPrivilegedUserOnTierLevel.Count Write-Host " Current members : $CurrentMemberCount | Unprotected objects for tier: $DesiredMemberCount" -ForegroundColor Gray $DesiredPrincipalIds = @($UnprotectedPrivilegedUserOnTierLevel.ObjectId | Where-Object { $_ } | Select-Object -Unique) $CurrentPrincipalMembers = @($CurrentAdminUnitMembers | Where-Object { $_.'@odata.type' -in @('#microsoft.graph.user', '#microsoft.graph.group') }) $CurrentPrincipalIds = @($CurrentPrincipalMembers.Id | Select-Object -Unique) $PrincipalIdsToAdd = @($DesiredPrincipalIds | Where-Object { $_ -notin $CurrentPrincipalIds }) $PrincipalRemovalPlan = [System.Collections.Generic.List[psobject]]::new() foreach ($CurrentMember in @($CurrentPrincipalMembers | Where-Object { $_.id -notin $DesiredPrincipalIds })) { $EamEntry = $PrivilegedEamObjects | Where-Object { $_.ObjectId -eq $CurrentMember.id } | Select-Object -First 1 $RemoveReason = $null if ($null -eq $EamEntry) { $RemoveReason = "no longer privileged" } elseif ($EamEntry.RestrictedManagementByAadRole -eq $True -or $EamEntry.RestrictedManagementByRAG -eq $True) { $RemoveReason = "protected by AadRole or RAG" } else { $MemberType = $CurrentMember.'@odata.type'.Replace('#microsoft.graph.', '') $MemberAUs = Invoke-EntraOpsMsGraphQuery -Uri "/beta/$($MemberType)s/$($CurrentMember.id)/memberOf/Microsoft.Graph.AdministrativeUnit" -OutputType PSObject -DisableCache $OtherRMAUs = $MemberAUs | Where-Object { $_.isMemberManagementRestricted -eq $True -and $_.id -ne $AdminUnitId } if ($null -ne $OtherRMAUs) { $RemoveReason = "protected by another RMAU" } } if ($null -ne $RemoveReason) { $PrincipalRemovalPlan.Add([PSCustomObject]@{ Id = $CurrentMember.id Type = $CurrentMember.'@odata.type'.Replace('#microsoft.graph.', '') Name = $CurrentMember.displayName Reason = $RemoveReason }) } else { if ($IncludeObjectDetails) { Write-Host " [~] KEEP [$($CurrentMember.'@odata.type'.Replace('#microsoft.graph.', ''))] $($CurrentMember.displayName) (this AU is the only RMAU)" -ForegroundColor DarkYellow } else { Write-Host " [~] KEEP [$($CurrentMember.'@odata.type'.Replace('#microsoft.graph.', ''))] $($CurrentMember.id) (this AU is the only RMAU)" -ForegroundColor DarkYellow } $AuKept++ } } foreach ($UnsupportedMember in @($CurrentAdminUnitMembers | Where-Object { $_.'@odata.type' -notin @('#microsoft.graph.user', '#microsoft.graph.group', '#microsoft.graph.device') })) { Write-Warning " [!] Unsupported object type $($UnsupportedMember.'@odata.type') - skipping" $WarningMessages.Add([PSCustomObject]@{ Type = "UnsupportedObjectType"; Message = "Unsupported object type $($UnsupportedMember.'@odata.type') in $AdminUnitName - skipped" }) $AuKept++ } $DesiredDeviceIds = @() $CurrentDeviceMemberIds = @() $DeviceIdsToAdd = [System.Collections.Generic.List[string]]::new() $DeviceRemovalPlan = [System.Collections.Generic.List[psobject]]::new() if ($IncludeUnprotectedDevices) { $PrivilegedUsersAtTier = $PrivilegedEamObjects | Where-Object { $_.ObjectType -eq "user" -and ` $_.Classification.AdminTierLevel -contains $TierLevel.AdminTierLevel -and ` $_.Classification.AdminTierLevelName -contains $TierLevel.AdminTierLevelName } $ForeignTenantDeviceOwners = @($PrivilegedUsersAtTier | Where-Object { -not [string]::IsNullOrEmpty($_.ObjectTenantId) -and $_.ObjectTenantId -ne $TenantId }) if ($ForeignTenantDeviceOwners.Count -gt 0) { Write-Warning " [SKIP] Excluded devices for $($ForeignTenantDeviceOwners.Count) user(s) not owned by tenant $TenantId" $WarningMessages.Add([PSCustomObject]@{ Type = "ForeignTenantDeviceOwner"; Message = "Skipped devices for $($ForeignTenantDeviceOwners.Count) user(s) not owned by tenant $TenantId for $AdminUnitName" }) } $PrivilegedUsersAtTier = @($PrivilegedUsersAtTier | Where-Object { [string]::IsNullOrEmpty($_.ObjectTenantId) -or $_.ObjectTenantId -eq $TenantId }) # Restricted to devices pinned to this tier by $DeviceTierAssignment - a device owned by a user # classified at several tiers belongs to the most privileged one only. $DesiredDeviceIds = @($PrivilegedUsersAtTier | ForEach-Object { @($_.OwnedDevices) + @($_.AssociatedPawDevice) } | Where-Object { $_ } | Select-Object -Unique | Where-Object { $DeviceTierAssignment[$_] -eq $TierLevel.AdminTierLevel }) $DeviceMembersUri = "/v1.0/directory/administrativeUnits/$AdminUnitId/members" $CurrentDeviceMemberIds = @((Invoke-EntraOpsMsGraphQuery -Method "GET" -Uri "$DeviceMembersUri/microsoft.graph.device" -OutputType PSObject -DisableCache) | Where-Object { $null -ne $_.id } | Select-Object -ExpandProperty id) if ($DesiredDeviceIds.Count -gt 0) { Write-Host " Checking $($DesiredDeviceIds.Count) device(s) owned by/associated to Tier $($TierLevel.AdminTierLevelName) users..." -ForegroundColor Gray } foreach ($DeviceId in $DesiredDeviceIds) { $Device = Invoke-EntraOpsMsGraphQuery -Method Get -Uri "/v1.0/devices/$DeviceId" -OutputType PSObject -DisableCache -SuppressNotFoundWarning if ($null -eq $Device -or [string]::IsNullOrEmpty($Device.id)) { $DeviceNotFoundMessage = if ($IncludeObjectDetails) { "Device $DeviceId was not found in tenant $TenantId" } else { "Device $DeviceId was not found in the target tenant" } Write-Warning " [SKIP] $DeviceNotFoundMessage" $WarningMessages.Add([PSCustomObject]@{ Type = "DeviceNotFound"; Message = "$DeviceNotFoundMessage for $AdminUnitName" }) continue } $DeviceAUs = @(Invoke-EntraOpsMsGraphQuery -Method Get -Uri "/beta/devices/$DeviceId/memberOf/microsoft.graph.administrativeUnit" -OutputType PSObject -DisableCache -SuppressNotFoundWarning | Where-Object { $null -ne $_.id }) # Treat memberships in sibling EntraOps tier AUs as managed rather than external protection. $HasOtherRMAU = ($DeviceAUs | Where-Object { $_.isMemberManagementRestricted -eq $True -and $_.id -ne $AdminUnitId -and $_.displayName -notin $ManagedAdminUnitNames }).Count -gt 0 if ($HasOtherRMAU) { if ($DeviceId -in $CurrentDeviceMemberIds) { $DeviceRemovalPlan.Add([PSCustomObject]@{ Id = $DeviceId; Reason = "protected by another RMAU" }) } continue } if ($DeviceId -notin $CurrentDeviceMemberIds) { $DeviceIdsToAdd.Add($DeviceId) } else { $AuKept++ } } foreach ($StaleDeviceId in @($CurrentDeviceMemberIds | Where-Object { $_ -notin $DesiredDeviceIds })) { $DeviceRemovalPlan.Add([PSCustomObject]@{ Id = $StaleDeviceId; Reason = "no longer associated to a privileged Tier $($TierLevel.AdminTierLevelName) user" }) } } $PlannedRemovalCount = $PrincipalRemovalPlan.Count + $DeviceRemovalPlan.Count $SafetyCheck = Test-EntraOpsRemovalSafetyThreshold -CurrentCount $CurrentMemberCount -RemovalCount $PlannedRemovalCount -RemovalSafetyThreshold $RemovalSafetyThreshold $SkipRemovals = $SafetyCheck.Exceeds -and -not $ForceRemovalBeyondSafetyThreshold Write-Host " Delta: +$($PrincipalIdsToAdd.Count + $DeviceIdsToAdd.Count) to add -$PlannedRemovalCount to remove" -ForegroundColor Yellow if ($SkipRemovals) { Write-Warning " [ABORT] $PlannedRemovalCount removals exceeds $($SafetyCheck.ThresholdPercent)% safety threshold ($($SafetyCheck.RemovalThreshold) of $CurrentMemberCount members). No removals were applied; additions still proceed. Review the plan, then re-run with -ForceRemovalBeyondSafetyThreshold to apply." $WarningMessages.Add([PSCustomObject]@{ Type = "SafetyAbort"; Message = "Aborted removals in $AdminUnitName`: $PlannedRemovalCount exceeds the $($SafetyCheck.ThresholdPercent)% safety threshold ($($SafetyCheck.RemovalThreshold) of $CurrentMemberCount members) - re-run with -ForceRemovalBeyondSafetyThreshold to apply" }) $AuStatus = "ABORTED" $AuKept += $PlannedRemovalCount } elseif ($SafetyCheck.Exceeds) { Write-Warning " [FORCED] Applying $PlannedRemovalCount removals despite exceeding the $($SafetyCheck.ThresholdPercent)% safety threshold - requested via -ForceRemovalBeyondSafetyThreshold." $WarningMessages.Add([PSCustomObject]@{ Type = "SafetyOverride"; Message = "Forced $PlannedRemovalCount removals in $AdminUnitName beyond the $($SafetyCheck.ThresholdPercent)% safety threshold" }) $AuStatus = "FORCED" } if (-not $SkipRemovals) { foreach ($Removal in $PrincipalRemovalPlan) { try { $AdminUnitMember = Invoke-EntraOpsMsGraphQuery -Method GET -Uri "/beta/directoryObjects/$($Removal.Id)" -OutputType PSObject -DisableCache if ($IncludeObjectDetails) { Write-Host " [-] REM [$($Removal.Type)] $($AdminUnitMember.displayName) ($($Removal.Reason))" -ForegroundColor Yellow } else { Write-Host " [-] REM [$($Removal.Type)] $($Removal.Id)" -ForegroundColor Yellow } Invoke-EntraOpsMsGraphQuery -Method DELETE -Uri "/beta/administrativeUnits/$($AdminUnitId)/members/$($Removal.Id)/`$ref" -OutputType PSObject -DisableCache -ThrowOnFailure $AuRemoved++ } catch { $RemoveFailureMessage = if ($IncludeObjectDetails) { "FAIL REMOVE $($Removal.Id) from $AdminUnitName`: $_" } else { "Failed to remove $($Removal.Type) object $($Removal.Id) from $AdminUnitName" } Write-Warning " [!] $RemoveFailureMessage" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = $RemoveFailureMessage }) $AuFailed++ } } foreach ($Removal in $DeviceRemovalPlan) { try { Invoke-EntraOpsMsGraphQuery -Method DELETE -Uri "$DeviceMembersUri/$($Removal.Id)/`$ref" -OutputType PSObject -ThrowOnFailure if ($IncludeObjectDetails) { Write-Host " [-] REM [device] $($Removal.Id) ($($Removal.Reason))" -ForegroundColor Yellow } else { Write-Host " [-] REM [device] $($Removal.Id)" -ForegroundColor Yellow } $AuRemoved++ } catch { $RemoveFailureMessage = if ($IncludeObjectDetails) { "FAIL REMOVE device $($Removal.Id) from $AdminUnitName`: $_" } else { "Failed to remove device $($Removal.Id) from $AdminUnitName" } Write-Warning " [!] $RemoveFailureMessage" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = $RemoveFailureMessage }) $AuFailed++ } } } foreach ($PrincipalId in $PrincipalIdsToAdd) { try { $AdminUnitMember = Invoke-EntraOpsMsGraphQuery -Method GET -Uri "/beta/directoryObjects/$PrincipalId" -OutputType PSObject -DisableCache $MemberType = $AdminUnitMember.'@odata.type'.Replace('#microsoft.graph.', '') if ($IncludeObjectDetails) { Write-Host " [+] ADD [$MemberType] $($AdminUnitMember.displayName)" -ForegroundColor Green } else { Write-Host " [+] ADD [$MemberType] $PrincipalId" -ForegroundColor Green } $OdataBody = @{ '@odata.id' = "https://graph.microsoft.com/beta/directoryObjects/$PrincipalId" } | ConvertTo-Json Invoke-EntraOpsMsGraphQuery -Method POST -Uri "/beta/administrativeUnits/$($AdminUnitId)/members/`$ref" -Body $OdataBody -OutputType PSObject -ThrowOnFailure $AuAdded++ } catch { $AddFailureMessage = if ($IncludeObjectDetails) { "FAIL ADD $PrincipalId to $AdminUnitName`: $_" } else { "Failed to add principal object $PrincipalId to $AdminUnitName" } Write-Warning " [!] $AddFailureMessage" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = $AddFailureMessage }) $AuFailed++ } } foreach ($DeviceId in $DeviceIdsToAdd) { try { $OdataBody = @{ '@odata.id' = "https://graph.microsoft.com/v1.0/directoryObjects/$DeviceId" } | ConvertTo-Json Invoke-EntraOpsMsGraphQuery -Method POST -Uri "$DeviceMembersUri/`$ref" -Body $OdataBody -OutputType PSObject -ThrowOnFailure if ($IncludeObjectDetails) { Write-Host " [+] ADD [device] $DeviceId" -ForegroundColor Green } else { Write-Host " [+] ADD [device] $DeviceId" -ForegroundColor Green } $AuAdded++ } catch { $AddFailureMessage = if ($IncludeObjectDetails) { "FAIL ADD device $DeviceId to $AdminUnitName`: $_" } else { "Failed to add device $DeviceId to $AdminUnitName" } Write-Warning " [!] $AddFailureMessage" $WarningMessages.Add([PSCustomObject]@{ Type = "ApiError"; Message = $AddFailureMessage }) $AuFailed++ } } if ($AuFailed -gt 0) { $AuStatus = "ERRORS" } $TotalAdded += $AuAdded $TotalRemoved += $AuRemoved $TotalKept += $AuKept $SyncSummary.Add([PSCustomObject]@{ AdminUnit = $AdminUnitName MembersBefore = $CurrentMemberCount Added = $AuAdded Removed = $AuRemoved Kept = $AuKept Failed = $AuFailed Status = $AuStatus }) } # Final summary Write-Host "" Write-Host "=========================================================" -ForegroundColor Cyan Write-Host " Unprotected Objects RMAU Sync - Complete" -ForegroundColor Cyan Write-Host " Total added : $TotalAdded" -ForegroundColor Green Write-Host " Total removed: $TotalRemoved" -ForegroundColor Yellow if ($TotalKept -gt 0) { Write-Host " Kept (no other RMAU): $TotalKept" -ForegroundColor DarkYellow } if (($SyncSummary | Where-Object { $_.Failed -gt 0 }).Count -gt 0) { Write-Host " Failures : $(($SyncSummary | Measure-Object -Property Failed -Sum).Sum)" -ForegroundColor Red } Write-Host "=========================================================" -ForegroundColor Cyan Write-Host "" Show-EntraOpsWarningSummary -WarningMessages $WarningMessages -IncludeObjectDetails $IncludeObjectDetails $SyncSummary | Format-Table -AutoSize -Property AdminUnit, MembersBefore, @{Name = 'Added'; Expression = { $_.Added }; Align = 'Right' }, @{Name = 'Removed'; Expression = { $_.Removed }; Align = 'Right' }, @{Name = 'Kept'; Expression = { $_.Kept }; Align = 'Right' }, @{Name = 'Failed'; Expression = { $_.Failed }; Align = 'Right' }, Status $FailureCount = ($SyncSummary | Measure-Object -Property Failed -Sum).Sum $AbortedCount = @($SyncSummary | Where-Object { $_.Status -eq 'ABORTED' }).Count if ($FailureCount -gt 0 -or $AbortedCount -gt 0) { $Reasons = [System.Collections.Generic.List[string]]::new() if ($FailureCount -gt 0) { $Reasons.Add("$FailureCount membership operation(s) failed") | Out-Null } if ($AbortedCount -gt 0) { $Reasons.Add("$AbortedCount RMAU synchronization(s) aborted by the removal safety threshold") | Out-Null } throw "RMAU sync did not complete: $($Reasons -join '; '). Review the warning summary above." } } # SIG # Begin signature block # MIIniAYJKoZIhvcNAQcCoIIneTCCJ3UCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAOPgtrh9mOwcL+ # 7I19ER7q8QYUQKi3k7YLb/JPM5j3KaCCIDwwggXJMIIEsaADAgECAhAbtY8lKt8j # 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 # ARUwLwYJKoZIhvcNAQkEMSIEIN2QnjuQUx5OX3rfkDpNrz9G0EIxKLqLYsPhYEL4 # uCqdMA0GCSqGSIb3DQEBAQUABIIBgGl0uBJu+vEvPYoaRpCZ1qDHCjF/AB7T0wyh # YX+SS5JRUqB/E21KSOK9rAtc26X/jM8dqUPiz5YpFxZmymt+xB95QX0qpwslypjO # /oR9nFm4uylIqihuIRDpyHlPQQpfFk0PiPTVXNZBZEVnbRaOBeGR4Od933mxSoXN # 2EWQ4OFOEPBvGxxopu4n/w0iJBoRW4TRcY9ohbl3znHBgziuwXAGHiYKE2gPBWBA # r8J/gqNzRaYWi8LlzcwfofeTKsdzUlDEBLXyklw/AJVJuzCJjx7nUMLlSV0c8gsw # E+mbhDXz8M0OQ+/5SaINWp/7hFjH/Iy4GkZg++CV2Oy2Ger7U8r6BviRtliKmXOz # mIQtDCIFuh2qwfNvflWqwsb7dfvel5ZlIGQkyPcAjQb0tvgKtpgxgRGHykRxjaOa # T9/iCUkLcdqrmAla25o+c8gO83QwF7OUayDBFy/TuuGwTDJ8YOLDQzPr9lrTFYFT # wYonGs9WXdNBIanDwkGAF5ju8/bcqaGCBAIwggP+BgkqhkiG9w0BCQYxggPvMIID # 6wIBATBqMFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0 # ZW1zIFMuQS4xJDAiBgNVBAMTG0NlcnR1bSBUaW1lc3RhbXBpbmcgMjAyMSBDQQIQ # KPB3wRw2vf5fdDJHcCcuAzANBglghkgBZQMEAgIFAKCCAVYwGgYJKoZIhvcNAQkD # MQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjEwMTMwOTQxNDRaMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIIW+kOEK0kONfMkotq9IsJqyCBd87PiwEmxY # 05EFJcQ8MD8GCSqGSIb3DQEJBDEyBDBUVgFnLD/+u+pBdM7as2H+j2xFj3qgaET9 # xSIDwfzf8wfUx+d7NjrV11Kane5YzxEwgZ8GCyqGSIb3DQEJEAIMMYGPMIGMMIGJ # MIGGBBRXFGhBDKha80JO+RZKUTYQ9NONmDBuMFqkWDBWMQswCQYDVQQGEwJQTDEh # MB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMSQwIgYDVQQDExtDZXJ0 # dW0gVGltZXN0YW1waW5nIDIwMjEgQ0ECECjwd8EcNr3+X3QyR3AnLgMwDQYJKoZI # hvcNAQEBBQAEggIAlkeA43fCf5Tb66i/D6+XfE9J44TOw5bOS0lyC1OUZeNPNLm8 # Ay7G/HkYgkyugCgXoYSoeMBcdMRQvAH98JjgX4G6H3T29DFsIiNtHVv5CnDwoR8W # a0dL5YYCsKyJ7Nyz79ELDbPaqe92QhagwkJoV5Dq4mIKH/ZnezeQGR84YnWfPQaf # z2yVdDow7ktJ22oEHwsEeg1VzmhZUfcBDYQed5E1PU6f+GMOMIzFqE2KMbMHAoGk # 2yE4ECLz3DRHOGNXJxvEFy67w58ZHXTz8M6EtOWkz1zTE5uJ8i9f6YcRDI6KK9Vm # 9B5gWWmN6FXgHNrK7Lku82w10PylF2Rt8vhxpnpsgB/AcZZyHidtF7vQi7KQGGjJ # CTC/9SGw5ex1whkktfztsttT5zX78TE+1tTaTKAno359QkDESuWoTqVY2UAA5z9R # 8xRGI8uzmuf6mzjtD9bpYKzm299li4IjrA+uBCGeeGNflrxC86eBSz6MyGOyTojM # eMa2pmC5MCevK5Pyf2EcAQfuTbSKZPwpySzBVkZhTXItGJWptHNHRqJN6CgfHqrm # Z7kiJI4kqNDCQAb3WBirSjb0KKtaJ4y1lEGdZJ7yTYf22HDXK9/FXoUceM7KozdE # a/JKhblMNZkEjWyGJEYiWzom1yOQMwQsuRCvJkehGsHVO0TGQDAFU346rRk= # SIG # End signature block |