Private/RoleManagement/Get-PIMActiveRoles.ps1
function Get-PIMActiveRoles { <# .SYNOPSIS Retrieves currently active PIM role assignments for the authenticated user. .DESCRIPTION Gets all active Privileged Identity Management (PIM) role assignments across: - Entra ID directory roles - PIM-enabled groups - Azure resource roles (if configured) The function filters all available roles to return only those currently active, with formatted output suitable for display or further processing. .PARAMETER None This function uses module-level configuration variables for scope determination. .OUTPUTS System.Object[] Array of custom objects representing active PIM roles with the following properties: - Type: Role type (EntraRole, Group, AzureResource) - DisplayName: Human-readable role name - EndDateTime: When the activation expires - ResourceName: Name of the resource the role applies to - Scope: Formatted scope display (Directory, AU name, or resource path) - MemberType: How the role was assigned (Direct, Eligible, etc.) - ScheduleId: Internal ID for deactivation operations .EXAMPLE PS> Get-PIMActiveRoles Returns all currently active PIM roles for the authenticated user. .NOTES Requires an authenticated user context and appropriate permissions to query PIM assignments. Uses module-scope variables: $script:CurrentUser, $script:IncludeEntraRoles, $script:IncludeGroups, $script:IncludeAzureResources #> [CmdletBinding()] param() Write-Verbose "Starting active PIM role retrieval" # Validate user context if (-not $script:CurrentUser -or -not $script:CurrentUser.Id) { Write-Warning "No authenticated user context available. Please ensure you're logged in." return @() } Write-Verbose "Retrieving roles for user: $($script:CurrentUser.Id)" # Prepare parameters for role query $roleParams = @{ UserId = $script:CurrentUser.Id IncludeEntraRoles = $script:IncludeEntraRoles IncludeGroups = $script:IncludeGroups IncludeAzureResources = $script:IncludeAzureResources } try { # Get all roles for the user $allRoles = Get-PIMRoles @roleParams Write-Verbose "Retrieved $($allRoles.Count) total role assignments" # Filter to active roles only $activeRoles = @($allRoles | Where-Object { $_.Status -eq 'Active' }) Write-Verbose "Found $($activeRoles.Count) active role assignments" if ($activeRoles.Count -eq 0) { Write-Verbose "No active PIM roles found" return @() } # Process and format each active role $formattedRoles = foreach ($role in $activeRoles) { Write-Verbose "Processing role: $($role.Name) ($($role.Type))" # Determine assignment type $memberType = if ($role.MemberType) { $role.MemberType } else { Get-MembershipType -Assignment $role.Assignment -RoleType $role.Type } # Extract schedule identifier for deactivation $scheduleId = $null if ($role.Assignment) { $scheduleId = if ($role.Assignment.Id) { $role.Assignment.Id } else { $role.Assignment.RoleAssignmentScheduleId } } # Format scope display $scopeDisplay = Get-FormattedScope -DirectoryScopeId $role.DirectoryScopeId -RoleType $role.Type # Create formatted role object $formattedRole = [PSCustomObject]@{ Type = $role.Type DisplayName = $role.Name EndDateTime = $role.EndDateTime ResourceName = $role.ResourceName DirectoryScopeId = $role.DirectoryScopeId Assignment = $role.Assignment ResourceId = $role.ResourceId GroupId = ($role.Type -eq 'Group') ? $role.ResourceId : $null Scope = $scopeDisplay MemberType = $memberType ScheduleId = $scheduleId RoleDefinitionId = $role.Id } # Adjust scope for Azure resources if ($role.Type -eq 'AzureResource') { $formattedRole.Scope = $role.DirectoryScopeId } $formattedRole } Write-Verbose "Successfully processed $($formattedRoles.Count) active roles" return $formattedRoles } catch { $errorMessage = "Failed to retrieve active PIM roles: $($_.Exception.Message)" Write-Warning $errorMessage Write-Verbose "Exception details: $($_.Exception.GetType().Name) - $($_.ScriptStackTrace)" return @() } } # Helper function to format scope display function Get-FormattedScope { param( [string]$DirectoryScopeId, [string]$RoleType ) if (-not $DirectoryScopeId -or $DirectoryScopeId -eq "/" -or $DirectoryScopeId -eq "Directory") { return "Directory" } # Handle Administrative Unit scopes if ($DirectoryScopeId -match "^/administrativeUnits/(.+)$") { $auId = $Matches[1] try { $au = Get-MgDirectoryAdministrativeUnit -AdministrativeUnitId $auId -ErrorAction Stop return "AU: $($au.DisplayName)" } catch { Write-Verbose "Unable to resolve Administrative Unit name for ID: $auId" return "AU: $auId" } } return $DirectoryScopeId } |