Private/Resolve-SPMEntraGroup.ps1
|
function Resolve-SPMEntraGroup { <# .SYNOPSIS Resolves the members of an Entra ID group transitively via Microsoft Graph. .DESCRIPTION Uses Invoke-PnPGraphMethod (same token as the PnP connection, no extra Graph module). Nested groups are resolved recursively while keeping the nesting visible in the resulting tree. Every group is resolved only once per scan ($GroupCache), cycles are detected via $Visited. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$GroupId, [Parameter(Mandatory)] [hashtable]$GroupCache, [ValidateSet('members', 'owners')] [string]$Relation = 'members', [System.Collections.Generic.HashSet[string]]$Visited ) $cacheKey = "$GroupId|$Relation" if ($GroupCache.ContainsKey($cacheKey)) { return $GroupCache[$cacheKey] } if (-not $Visited) { $Visited = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) } if (-not $Visited.Add($GroupId)) { return , @([pscustomobject]@{ name = '(Zyklus - Gruppe bereits weiter oben aufgeloest)'; type = 'Note' }) } $members = [System.Collections.Generic.List[object]]::new() try { $url = "v1.0/groups/$GroupId/$Relation`?`$select=id,displayName,userPrincipalName&`$top=999" while ($url) { $response = Invoke-SPMWithRetry { Invoke-PnPGraphMethod -Url $url } foreach ($m in @($response.value)) { switch ($m.'@odata.type') { '#microsoft.graph.group' { $members.Add([pscustomobject]@{ name = $m.displayName type = 'EntraGroup' members = @(Resolve-SPMEntraGroup -GroupId $m.id -GroupCache $GroupCache -Visited $Visited) }) } '#microsoft.graph.user' { $members.Add([pscustomobject]@{ name = $m.displayName type = 'User' upn = $m.userPrincipalName }) } default { $label = if ($m.displayName) { $m.displayName } else { [string]$m.id } $members.Add([pscustomobject]@{ name = $label; type = 'Other' }) } } } $url = $response.'@odata.nextLink' } } catch { Write-Warning "Mitglieder der Entra-Gruppe $GroupId konnten nicht aufgeloest werden: $($_.Exception.Message)" $members.Add([pscustomobject]@{ name = '(Aufloesung fehlgeschlagen - Graph-Berechtigung Directory.Read.All vorhanden?)'; type = 'Note' }) } $result = $members.ToArray() $GroupCache[$cacheKey] = $result [void]$Visited.Remove($GroupId) return $result } |