Private/Get-UserRoleInfo.ps1
|
function Get-UserRoleInfo { <# .SYNOPSIS Retrieves the signed-in user's active and PIM-eligible Entra roles (best-effort). .DESCRIPTION Queries Microsoft Graph for the signed-in user's directory role assignments (both active and PIM-eligible). Returns a safe empty object on any failure — this function is always best-effort and never blocks core LAPS functionality. Requires RoleManagement.Read.Directory scope. Returns empty object gracefully if this scope is not consented. .OUTPUTS PSCustomObject with: ActiveRoles [string[]] - Display names of active Entra roles PimEligibleRoles [string[]] - Display names of PIM-eligible (inactive) roles HasPimEligible [bool] - Quick flag: $true if any PIM-eligible roles exist AuScopedRoles [hashtable[]] - AU-scoped active roles: @{ RoleName; AuId; AuDisplayName } HasAuScopedRoles [bool] - Quick flag: $true if any AU-scoped roles exist #> [CmdletBinding()] [OutputType([PSCustomObject])] param() # Safe empty result returned on any failure [PSCustomObject]$EmptyResult = [PSCustomObject]@{ ActiveRoles = [string[]]@() ActiveRoleIds = [string[]]@() PimEligibleRoles = [string[]]@() PimEligibleRoleIds = [string[]]@() HasPimEligible = $false AuScopedRoles = [hashtable[]]@() HasAuScopedRoles = $false } try { # Step 1: Get the signed-in user's object ID $MeResponse = Invoke-MgGraphRequestWithRetry -Parameters @{ Method = 'GET' Uri = 'https://graph.microsoft.com/v1.0/me?$select=id' } [string]$UserId = $MeResponse.id if ([string]::IsNullOrEmpty($UserId)) { Write-Verbose 'Get-UserRoleInfo: Could not retrieve user object ID.' return $EmptyResult } # Step 2: Get active directory role assignments [string[]]$ActiveRoleNames = @() [string[]]$ActiveRoleIds = @() $AuScopedRolesList = [System.Collections.Generic.List[hashtable]]::new() try { [string]$ActiveUri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments?`$filter=principalId eq '$UserId'&`$expand=roleDefinition(`$select=id,displayName)" $AllActive = Invoke-MgGraphPagedRequest -Uri $ActiveUri if ($AllActive.Count -gt 0) { # Collect names and IDs in parallel (same index = same role) $ActiveNamesList = [System.Collections.Generic.List[string]]::new() $ActiveIdsList = [System.Collections.Generic.List[string]]::new() foreach ($Assignment in $AllActive) { if (-not ($Assignment.roleDefinition -and $Assignment.roleDefinition.displayName)) { continue } $ActiveNamesList.Add($Assignment.roleDefinition.displayName) # Prefer roleDefinition.id (always present via expand) over top-level roleDefinitionId [string]$RoleGuid = if ($Assignment.roleDefinition.id) { $Assignment.roleDefinition.id } else { $Assignment.roleDefinitionId } if ($RoleGuid) { $ActiveIdsList.Add($RoleGuid) } } $ActiveRoleNames = $ActiveNamesList.ToArray() $ActiveRoleIds = $ActiveIdsList.ToArray() # Collect AU-scoped assignments with display name (best-effort lookup per AU) foreach ($Assignment in $AllActive) { if (-not ($Assignment.roleDefinition -and $Assignment.roleDefinition.displayName)) { continue } if ($Assignment.directoryScopeId -eq '/') { continue } [string]$AuId = $Assignment.directoryScopeId [string]$AuDisplayName = '' try { # directoryScopeId for AUs is '/administrativeUnits/<guid>' [string]$AuGuid = $AuId -replace '^/administrativeUnits/', '' $AuResponse = Invoke-MgGraphRequestWithRetry -Parameters @{ Method = 'GET' Uri = "https://graph.microsoft.com/v1.0/directory/administrativeUnits/$AuGuid`?`$select=displayName" } $AuDisplayName = $AuResponse.displayName } catch { Write-Verbose "Get-UserRoleInfo: Could not retrieve AU display name for '$AuId': $_" } $AuScopedRolesList.Add(@{ RoleName = $Assignment.roleDefinition.displayName AuId = $AuId AuDisplayName = $AuDisplayName }) } } } catch { Write-Verbose "Get-UserRoleInfo: Could not retrieve active roles: $_" } # Step 3: Get PIM-eligible role assignments [string[]]$EligibleRoleNames = @() [string[]]$EligibleRoleIds = @() try { [string]$EligibleUri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleInstances?`$filter=principalId eq '$UserId'&`$expand=roleDefinition(`$select=id,displayName)" $AllEligible = Invoke-MgGraphPagedRequest -Uri $EligibleUri if ($AllEligible.Count -gt 0) { $EligibleNamesList = [System.Collections.Generic.List[string]]::new() $EligibleIdsList = [System.Collections.Generic.List[string]]::new() foreach ($Instance in $AllEligible) { if (-not ($Instance.roleDefinition -and $Instance.roleDefinition.displayName)) { continue } $EligibleNamesList.Add($Instance.roleDefinition.displayName) [string]$RoleGuid = if ($Instance.roleDefinition.id) { $Instance.roleDefinition.id } else { $Instance.roleDefinitionId } if ($RoleGuid) { $EligibleIdsList.Add($RoleGuid) } } $EligibleRoleNames = $EligibleNamesList.ToArray() $EligibleRoleIds = $EligibleIdsList.ToArray() } } catch { Write-Verbose "Get-UserRoleInfo: Could not retrieve PIM eligible roles: $_" } [hashtable[]]$AuScopedRolesArray = if ($AuScopedRolesList.Count -gt 0) { $AuScopedRolesList.ToArray() } else { [hashtable[]]@() } return [PSCustomObject]@{ ActiveRoles = [string[]]$ActiveRoleNames ActiveRoleIds = [string[]]$ActiveRoleIds PimEligibleRoles = [string[]]$EligibleRoleNames PimEligibleRoleIds = [string[]]$EligibleRoleIds HasPimEligible = ($EligibleRoleNames.Count -gt 0) AuScopedRoles = $AuScopedRolesArray HasAuScopedRoles = ($AuScopedRolesArray.Count -gt 0) } } catch { Write-Verbose "Get-UserRoleInfo: Role lookup failed, returning empty result: $_" return $EmptyResult } } |