Private/Resolve-GraphObjectNames.ps1
|
function Resolve-GraphObjectNames { <# .SYNOPSIS Batch-resolves Microsoft Graph object GUIDs to display names. .DESCRIPTION Internal helper that eliminates N+1 query patterns by batch-fetching groups, service principals, and applications, returning ID-to-DisplayName lookup hashtables. For groups and service principals, if the count exceeds BatchSizeThreshold the function fetches all objects in a single call and filters locally. Below the threshold it makes individual calls (cheaper for small sets). Well-known Microsoft application IDs are resolved from a local cache without API calls. .PARAMETER GroupIds Array of group object IDs to resolve. .PARAMETER ServicePrincipalIds Array of service principal object IDs to resolve. .PARAMETER ApplicationIds Array of application (service principal) AppIds to resolve via filter. .PARAMETER BatchSizeThreshold When object count exceeds this value, fetch all objects instead of individual calls. Default: 10 .OUTPUTS System.Management.Automation.PSCustomObject with GroupLookup, SPLookup, AppLookup hashtables. .EXAMPLE $Resolved = Resolve-GraphObjectNames -GroupIds @($Id1, $Id2) $GroupName = $Resolved.GroupLookup[$Id1] .NOTES Author: Tom de Leeuw Website: https://systom.dev Module: TenantReports Internal helper function for batch GUID resolution. .LINK https://systom.dev #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter()] [string[]]$GroupIds, [Parameter()] [string[]]$ServicePrincipalIds, [Parameter()] [string[]]$ApplicationIds, [Parameter()] [ValidateRange(1, 100)] [int]$BatchSizeThreshold = 10 ) $GroupLookup = @{} $SPLookup = @{} $AppLookup = @{} # Well-known Microsoft application IDs (no API call needed) $WellKnownApps = @{ '00000002-0000-0000-c000-000000000000' = 'Azure AD Graph (Legacy)' '00000003-0000-0000-c000-000000000000' = 'Microsoft Graph' '00000002-0000-0ff1-ce00-000000000000' = 'Office 365 Exchange Online' '00000003-0000-0ff1-ce00-000000000000' = 'Office 365 SharePoint Online' '00000004-0000-0ff1-ce00-000000000000' = 'Office 365 Lync Online' '797f4846-ba00-4fd7-ba43-dac1f8f63013' = 'Azure Service Management' 'c5393580-f805-4401-95e8-94b7a6ef2fc2' = 'Office 365 Management APIs' '0000000c-0000-0000-c000-000000000000' = 'Azure AD' } # --- Groups --- if ($GroupIds -and $GroupIds.Count -gt 0) { $UniqueGroupIds = [System.Collections.Generic.HashSet[string]]::new( [string[]]$GroupIds, [StringComparer]::OrdinalIgnoreCase ) Write-Verbose "Resolving $($UniqueGroupIds.Count) group GUIDs..." if ($UniqueGroupIds.Count -gt $BatchSizeThreshold) { # Batch: fetch all groups, filter locally Write-Verbose 'Using batch approach for groups (threshold exceeded)' $AllGroups = Get-MgGroup -All -Property Id, DisplayName -ErrorAction SilentlyContinue foreach ($Group in $AllGroups) { if ($UniqueGroupIds.Contains($Group.Id)) { $GroupLookup[$Group.Id] = $Group.DisplayName } } } else { foreach ($GroupId in $UniqueGroupIds) { try { $Group = Get-MgGroup -GroupId $GroupId -Property Id, DisplayName -ErrorAction SilentlyContinue if ($Group) { $GroupLookup[$GroupId] = $Group.DisplayName } } catch { Write-Verbose "Could not resolve group: $GroupId" } } } } # Service Principals (by object ID) if ($ServicePrincipalIds -and $ServicePrincipalIds.Count -gt 0) { $UniqueSPIds = [System.Collections.Generic.HashSet[string]]::new( [string[]]$ServicePrincipalIds, [StringComparer]::OrdinalIgnoreCase ) Write-Verbose "Resolving $($UniqueSPIds.Count) service principal GUIDs..." if ($UniqueSPIds.Count -gt $BatchSizeThreshold) { Write-Verbose 'Using batch approach for service principals (threshold exceeded)' $AllSPs = Get-MgServicePrincipal -All -Property Id, DisplayName -ErrorAction SilentlyContinue foreach ($SP in $AllSPs) { if ($UniqueSPIds.Contains($SP.Id)) { $SPLookup[$SP.Id] = $SP.DisplayName } } } else { foreach ($SPId in $UniqueSPIds) { try { $SP = Get-MgServicePrincipal -ServicePrincipalId $SPId -Property Id, DisplayName -ErrorAction SilentlyContinue if ($SP) { $SPLookup[$SPId] = $SP.DisplayName } } catch { Write-Verbose "Could not resolve service principal: $SPId" } } } } # Applications (by AppId, resolved via service principal filter) if ($ApplicationIds -and $ApplicationIds.Count -gt 0) { $UniqueAppIds = [System.Collections.Generic.HashSet[string]]::new( [string[]]$ApplicationIds, [StringComparer]::OrdinalIgnoreCase ) Write-Verbose "Resolving $($UniqueAppIds.Count) application GUIDs..." foreach ($AppId in $UniqueAppIds) { if ($WellKnownApps.ContainsKey($AppId)) { $AppLookup[$AppId] = $WellKnownApps[$AppId] } else { try { $ServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$AppId'" -Property AppId, DisplayName -ErrorAction SilentlyContinue | Select-Object -First 1 if ($ServicePrincipal) { $AppLookup[$AppId] = $ServicePrincipal.DisplayName } } catch { Write-Verbose "Could not resolve application: $AppId" } } } } [PSCustomObject]@{ GroupLookup = $GroupLookup SPLookup = $SPLookup AppLookup = $AppLookup } } |