Public/Get-PrivilegedRoleAssignments.ps1

function Get-PrivilegedRoleAssignments {
    <#
    .SYNOPSIS
        Exports active (and optionally PIM-eligible) Entra ID directory role assignments to CSV.
    .DESCRIPTION
        Requires Connect-MgGraph with RoleManagement.Read.Directory. -IncludeEligible also
        needs RoleEligibilitySchedule.Read.Directory and Entra ID P2. Read-only.
    .PARAMETER PrivilegedOnly
        Only include roles flagged isPrivileged by Microsoft.
    .PARAMETER IncludeEligible
        Include PIM eligible assignments.
    .PARAMETER OutputFolder
        Folder for CSV output. Defaults to ./output.
    .EXAMPLE
        Get-PrivilegedRoleAssignments -PrivilegedOnly -IncludeEligible
    #>

    [CmdletBinding()]
    param(
        [switch]$PrivilegedOnly,
        [switch]$IncludeEligible,
        [string]$OutputFolder = (Join-Path (Get-Location) 'output')
    )
    $ErrorActionPreference = 'Stop'
    if (-not (Get-MgContext)) { throw 'Not connected. Run Connect-MgGraph first.' }
    New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null

    $roles = @{}
    Get-MgRoleManagementDirectoryRoleDefinition -All | ForEach-Object { $roles[$_.Id] = $_ }

    function ConvertTo-Row {
        param($Assignment, [string]$Type, [bool]$OnlyPrivileged)
        $role = $roles[$Assignment.RoleDefinitionId]
        if ($OnlyPrivileged -and -not $role.AdditionalProperties['isPrivileged']) { return }
        $p = $Assignment.Principal.AdditionalProperties
        [pscustomobject]@{
            Role          = $role.DisplayName
            IsPrivileged  = $role.AdditionalProperties['isPrivileged']
            AssignmentType = $Type
            PrincipalName = $p['displayName']
            PrincipalUPN  = $p['userPrincipalName']
            PrincipalType = ($p['@odata.type'] -replace '#microsoft.graph.', '')
            PrincipalId   = $Assignment.PrincipalId
            Scope         = $Assignment.DirectoryScopeId
        }
    }

    $rows = @(Get-MgRoleManagementDirectoryRoleAssignment -All -ExpandProperty Principal |
        ForEach-Object { ConvertTo-Row -Assignment $_ -Type 'Active' -OnlyPrivileged $PrivilegedOnly.IsPresent })
    if ($IncludeEligible) {
        $rows += Get-MgRoleManagementDirectoryRoleEligibilitySchedule -All -ExpandProperty Principal |
            ForEach-Object { ConvertTo-Row -Assignment $_ -Type 'Eligible' -OnlyPrivileged $PrivilegedOnly.IsPresent }
    }
    $rows = $rows | Sort-Object Role, PrincipalName
    $file = Join-Path $OutputFolder "PrivilegedRoles-$(Get-Date -Format 'yyyyMMdd-HHmm').csv"
    $rows | Export-Csv -Path $file -NoTypeInformation
    $ga = @($rows | Where-Object { $_.Role -eq 'Global Administrator' -and $_.AssignmentType -eq 'Active' }).Count
    Write-Information "Assignments: $(@($rows).Count) Active Global Admins: $ga -> $file" -InformationAction Continue
}