Private/Get-EntraGroupPimEligibleMembers.ps1
|
function Get-EntraGroupPimEligibleMembers { <# .SYNOPSIS Returns the ObjectIds of principals PIM-eligible for member access to a group. .DESCRIPTION Get-AzADGroupMember only returns a group's currently-active members. A PIM-for-Groups eligible assignment that nobody has activated yet is invisible to it - which silently drops people who could elevate themselves into the group (and everything it grants) on demand from this report entirely, reporting the group as empty instead. This queries Microsoft Graph's PIM-for-Groups eligibility schedule directly (there's no Az.Resources cmdlet for it). Uses Microsoft.Graph.Authentication (see Initialize-PimGraphConnection), not the existing Az token - the app Connect-AzAccount signs into has a fixed permission set that does not include PrivilegedEligibilitySchedule.Read.AzureADGroup at all, regardless of the signed-in user's directory role, so no Az token can ever carry it. If the call still fails (e.g. consent declined, or the signed-in user genuinely lacks PIM read access), this reports it as a warning and treats the group as having no eligible members rather than failing the whole report. #> param( [Parameter(Mandatory)] [string]$GroupId, [Parameter(Mandatory)] [hashtable]$Cache ) $cacheKey = "pim-eligible:$GroupId" if ($Cache.ContainsKey($cacheKey)) { return $Cache[$cacheKey] } $eligibleIds = @() try { Initialize-PimGraphConnection $uri = "https://graph.microsoft.com/v1.0/identityGovernance/privilegedAccess/group/eligibilityScheduleInstances?`$filter=groupId eq '$GroupId'" $response = Invoke-MgGraphRequest -Uri $uri -Method GET -OutputType PSObject -ErrorAction Stop $eligibleIds = @($response.value | Where-Object { $_.accessId -eq 'member' } | Select-Object -ExpandProperty principalId -Unique) } catch { Write-Warning "Could not check PIM eligibility for group '$GroupId' (treating as none): $($_.Exception.Message)" } $Cache[$cacheKey] = $eligibleIds return $eligibleIds } |