Public/Remove-NLBaselineCAOrphanedObjects.ps1
|
function Remove-NLBaselineCAOrphanedObjects { <# .SYNOPSIS Remove orphaned groups and users from Conditional Access policies .DESCRIPTION Scans all Conditional Access policies and removes references to deleted (orphaned) groups or users. This helps clean up policies after groups or users have been deleted. .EXAMPLE Remove-NLBaselineCAOrphanedObjects -ObjectType Group -ObjectId "guid-here" Remove-NLBaselineCAOrphanedObjects -ObjectType User -ObjectId "guid-here" #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateSet("Group", "User")] [string]$ObjectType, [Parameter(Mandatory = $true)] [string]$ObjectId ) try { # Check connection $context = Get-MgContext -ErrorAction SilentlyContinue if (-not $context -or -not $context.TenantId) { Write-Host "Not connected to Microsoft 365. Connecting..." -ForegroundColor Yellow Write-Host "" $connection = Connect-NLBaselineCA if (-not $connection) { Write-Error "Cannot connect to Microsoft 365" return } $context = Get-MgContext } Write-Host "========================================" -ForegroundColor Cyan Write-Host " REMOVE ORPHANED $ObjectType" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" Write-Host "Searching for orphaned $ObjectType : $ObjectId" -ForegroundColor Yellow Write-Host "" # Get all policies Write-Host "Retrieving all Conditional Access policies..." -ForegroundColor Gray $policies = Get-AllConditionalAccessPolicies Write-Host "Found $($policies.Count) policies to check" -ForegroundColor Green Write-Host "" $updatedCount = 0 $foundCount = 0 $errors = @() foreach ($policy in $policies) { try { $needsUpdate = $false $policyUpdate = @{} # Ensure conditions structure exists if (-not $policy.conditions -or -not $policy.conditions.users) { continue } $policyUpdate.conditions = $policy.conditions if ($ObjectType -eq "Group") { # Check excludeGroups if ($policy.conditions.users.excludeGroups -and $policy.conditions.users.excludeGroups -contains $ObjectId) { $foundCount++ $updatedExcludeGroups = $policy.conditions.users.excludeGroups | Where-Object { $_ -ne $ObjectId } $policyUpdate.conditions.users.excludeGroups = $updatedExcludeGroups $needsUpdate = $true Write-Host " Found in excludeGroups: $($policy.displayName)" -ForegroundColor Yellow } # Check includeGroups if ($policy.conditions.users.includeGroups -and $policy.conditions.users.includeGroups -contains $ObjectId) { $foundCount++ $updatedIncludeGroups = $policy.conditions.users.includeGroups | Where-Object { $_ -ne $ObjectId } $policyUpdate.conditions.users.includeGroups = $updatedIncludeGroups $needsUpdate = $true Write-Host " Found in includeGroups: $($policy.displayName)" -ForegroundColor Yellow } } elseif ($ObjectType -eq "User") { # Check excludeUsers if ($policy.conditions.users.excludeUsers -and $policy.conditions.users.excludeUsers -contains $ObjectId) { $foundCount++ $updatedExcludeUsers = $policy.conditions.users.excludeUsers | Where-Object { $_ -ne $ObjectId } $policyUpdate.conditions.users.excludeUsers = $updatedExcludeUsers $needsUpdate = $true Write-Host " Found in excludeUsers: $($policy.displayName)" -ForegroundColor Yellow } # Check includeUsers if ($policy.conditions.users.includeUsers -and $policy.conditions.users.includeUsers -contains $ObjectId) { $foundCount++ $updatedIncludeUsers = $policy.conditions.users.includeUsers | Where-Object { $_ -ne $ObjectId } $policyUpdate.conditions.users.includeUsers = $updatedIncludeUsers $needsUpdate = $true Write-Host " Found in includeUsers: $($policy.displayName)" -ForegroundColor Yellow } } if ($needsUpdate) { # Update policy using REST API $body = @{ conditions = $policyUpdate.conditions } | ConvertTo-Json -Depth 10 $invokeCmd = Get-Command Invoke-MgGraphRequest -ErrorAction SilentlyContinue if ($invokeCmd) { Invoke-MgGraphRequest -Method PATCH ` -Uri "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/$($policy.Id)" ` -Body $body ` -ContentType "application/json" ` -ErrorAction Stop $updatedCount++ Write-Host " Updated successfully" -ForegroundColor Green } else { throw "Invoke-MgGraphRequest not available" } } } catch { $errors += "Error updating $($policy.displayName): $_" Write-Host " Error: $_" -ForegroundColor Red } } Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host " SUMMARY" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "Found in: $foundCount policies" -ForegroundColor White Write-Host "Updated: $updatedCount policies" -ForegroundColor White if ($errors.Count -gt 0) { Write-Host "Errors: $($errors.Count)" -ForegroundColor Red foreach ($error in $errors) { Write-Host " - $error" -ForegroundColor Yellow } } Write-Host "" } catch { Write-Error "Error removing orphaned objects: $_" } } |