Private/Resolve-EntraObject.ps1
|
function Resolve-EntraObject { <# .SYNOPSIS Looks up and classifies a single Entra ID object, caching the result. .DESCRIPTION Classifies an object as Group, User, Managed Identity, or Service Principal, using the same try-in-order pattern as Test-EntraIdPrincipal (User, then Service Principal, then Group). Looks up by display name/UPN for a top-level login name, or by ObjectId when resolving a group member found during recursion. Results are cached in the caller-supplied hashtable by ObjectId (and, for a name-based lookup, by name too) so the same object is never looked up twice in one run - important once the same group/user shows up under several logins or several nested groups. A Group result also carries EligibleMembers - PIM-for-Groups eligible member assignments nobody has activated yet, via Get-EntraGroupPimEligibleMembers - separately from Members (currently-active membership, via Get-AzADGroupMember), so a caller can tell "has access right now" apart from "could elevate into having access". #> param( [string]$Name, [string]$ObjectId, [Parameter(Mandatory)] [hashtable]$Cache ) $cacheKey = if ($ObjectId) { $ObjectId } else { "name:$Name" } if ($Cache.ContainsKey($cacheKey)) { return $Cache[$cacheKey] } $result = $null if ($ObjectId) { $user = Get-AzADUser -ObjectId $ObjectId -ErrorAction SilentlyContinue if ($user) { $result = [PSCustomObject]@{ Type = 'User'; Id = $ObjectId; DisplayName = $user.UserPrincipalName; Members = $null; EligibleMembers = $null } } else { $sp = Get-AzADServicePrincipal -ObjectId $ObjectId -ErrorAction SilentlyContinue if ($sp) { $type = if ($sp.ServicePrincipalType -eq 'ManagedIdentity') { 'Managed Identity' } else { 'Service Principal' } $result = [PSCustomObject]@{ Type = $type; Id = $ObjectId; DisplayName = $sp.DisplayName; Members = $null; EligibleMembers = $null } } else { $grp = Get-AzADGroup -ObjectId $ObjectId -ErrorAction SilentlyContinue if ($grp) { $memberIds = @(Get-AzADGroupMember -GroupObjectId $ObjectId -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id) $eligibleIds = @(Get-EntraGroupPimEligibleMembers -GroupId $ObjectId -Cache $Cache | Where-Object { $_ -notin $memberIds }) $result = [PSCustomObject]@{ Type = 'Group'; Id = $ObjectId; DisplayName = $grp.DisplayName; Members = $memberIds; EligibleMembers = $eligibleIds } } } } } else { $odataFilter = "displayName eq '$($Name.Replace("'", "''"))'" $user = Get-AzADUser -UserPrincipalName $Name -ErrorAction SilentlyContinue if (-not $user) { $user = Get-AzADUser -Filter $odataFilter -ErrorAction SilentlyContinue } if ($user) { $result = [PSCustomObject]@{ Type = 'User'; Id = $user.Id; DisplayName = $user.UserPrincipalName; Members = $null; EligibleMembers = $null } } else { $sp = Get-AzADServicePrincipal -Filter $odataFilter -ErrorAction SilentlyContinue if ($sp) { $type = if ($sp.ServicePrincipalType -eq 'ManagedIdentity') { 'Managed Identity' } else { 'Service Principal' } $result = [PSCustomObject]@{ Type = $type; Id = $sp.Id; DisplayName = $sp.DisplayName; Members = $null; EligibleMembers = $null } } else { $grp = Get-AzADGroup -Filter $odataFilter -ErrorAction SilentlyContinue if ($grp) { $memberIds = @(Get-AzADGroupMember -GroupObjectId $grp.Id -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Id) $eligibleIds = @(Get-EntraGroupPimEligibleMembers -GroupId $grp.Id -Cache $Cache | Where-Object { $_ -notin $memberIds }) $result = [PSCustomObject]@{ Type = 'Group'; Id = $grp.Id; DisplayName = $grp.DisplayName; Members = $memberIds; EligibleMembers = $eligibleIds } } } } } if (-not $result) { $result = [PSCustomObject]@{ Type = 'Not Found in Entra'; Id = $ObjectId; DisplayName = $Name; Members = $null; EligibleMembers = $null } } $Cache[$cacheKey] = $result if ($result.Id) { $Cache[$result.Id] = $result } return $result } |