Unexpected access is almost never a broken permission system; it is a working one traversing structure nobody remembers. Unlike licensing, access does flow through nested membership, so the investigation is a path-tracing exercise: which chain connects this user to that grant.
Trace the paths#
Establish every chain from the user to the granting group, because there is often more than one:
Connect-MgGraph -Scopes "User.Read.All"
# Confirm the user transitively reaches the granting group
Get-MgUserTransitiveMemberOf -UserId user@contoso.com |
Where-Object { $_.Id -eq "<granting-group-id>" }
# Then walk memberships level by level to reconstruct each chainDecide which edge to cut#
The remediation is removing one membership edge, and choosing it needs the full picture: cutting the edge closest to the user is precise but may recur if automation re-adds it; cutting higher up affects everyone below that point. Every candidate edge deserves a blast-radius check before it is cut.
VisualizerEngine
How VisualizerEngine does it
The graph renders every path between the user and the grant instantly, which replaces the level-by-level reconstruction entirely. What-if simulation previews each candidate cut's full consequences, and the change-history panel shows when and how the unexpected path appeared.