Private/RoleManagement/Get-EntraIDRoles.ps1
function Get-EntraIDRoles { <# .SYNOPSIS Retrieves Entra ID directory roles for a specified user. .DESCRIPTION Gets both active and eligible Entra ID directory roles from Microsoft Graph API. Returns a collection of role objects with standardized properties for PIM management. .PARAMETER UserId The user ID (Object ID) to retrieve roles for. This should be the user's Azure AD Object ID. .EXAMPLE Get-EntraIDRoles -UserId "12345678-1234-1234-1234-123456789012" Retrieves all active and eligible Entra ID roles for the specified user. .EXAMPLE Get-EntraIDRoles -UserId $env:USER_OBJECT_ID -Verbose Retrieves roles with verbose output showing the operation progress. .OUTPUTS System.Management.Automation.PSCustomObject[] Returns an array of role objects with properties: Id, Name, Type, Status, Source, ResourceId, ResourceName, StartDateTime, EndDateTime, MemberType, DirectoryScopeId, PrincipalId, and Assignment. .NOTES Requires Microsoft Graph PowerShell SDK with appropriate permissions: - RoleManagement.Read.Directory - Directory.Read.All #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$UserId ) $roles = @() try { Write-Verbose "Retrieving Entra ID roles for user: $UserId" # Get eligible role assignments Write-Verbose "Querying eligible role assignments..." $eligibleAssignments = Get-MgRoleManagementDirectoryRoleEligibilityScheduleInstance ` -Filter "principalId eq '$UserId'" ` -ExpandProperty roleDefinition -ErrorAction Stop $eligibleAssignments = @($eligibleAssignments) Write-Verbose "Processing $($eligibleAssignments.Count) eligible role assignment(s)" foreach ($assignment in $eligibleAssignments) { # Determine member type $memberType = if ($assignment.MemberType) { switch ($assignment.MemberType) { "Direct" { "Direct" } "Group" { "Group" } "Inherited" { "Inherited" } default { $assignment.MemberType } } } else { "Direct" } $roles += [PSCustomObject]@{ Id = $assignment.RoleDefinitionId Name = $assignment.RoleDefinition.DisplayName Type = 'Entra' Status = 'Eligible' Source = 'EntraID' ResourceId = $null ResourceName = 'Entra ID Directory' StartDateTime = $assignment.StartDateTime EndDateTime = $assignment.EndDateTime MemberType = $memberType # ADD THIS DirectoryScopeId = $assignment.DirectoryScopeId PrincipalId = $assignment.PrincipalId Assignment = $assignment } } # Get active role assignments Write-Verbose "Querying active role assignments..." $activeAssignments = Get-MgRoleManagementDirectoryRoleAssignmentScheduleInstance ` -Filter "principalId eq '$UserId'" ` -ErrorAction Stop $activeAssignments = @($activeAssignments) Write-Verbose "Processing $($activeAssignments.Count) active role assignment(s)" foreach ($assignment in $activeAssignments) { try { # Retrieve role definition details $roleDetails = Get-MgDirectoryRole -Filter "roleTemplateId eq '$($assignment.RoleDefinitionId)'" -ErrorAction Stop if ($roleDetails) { $roles += [PSCustomObject]@{ Id = $assignment.RoleDefinitionId Name = $roleDetails.DisplayName Type = 'Entra' Status = 'Active' Source = 'EntraID' ResourceId = $null ResourceName = 'Entra ID Directory' StartDateTime = $assignment.StartDateTime EndDateTime = $assignment.EndDateTime MemberType = $assignment.MemberType DirectoryScopeId = $assignment.DirectoryScopeId PrincipalId = $assignment.PrincipalId Assignment = $assignment } } } catch { Write-Verbose "Unable to retrieve role definition for ID '$($assignment.RoleDefinitionId)': $($_.Exception.Message)" } } Write-Verbose "Successfully retrieved $($roles.Count) total Entra ID role(s)" } catch { Write-Warning "Error retrieving Entra ID roles: $($_.Exception.Message)" Write-Verbose "Entra ID role retrieval error: $($_.Exception.GetType().Name) - $($_.ScriptStackTrace)" } # Ensure we always return an array if ($null -eq $roles) { return @() } elseif ($roles -isnot [array]) { return @($roles) } return $roles } |