Collectors/RoleAssignments.ps1

@{
    Name           = 'RoleAssignments'
    FileName       = 'roleAssignments'
    ApiVersion     = 'v1.0'
    RequiredScopes = @('RoleManagement.Read.Directory')
    Description    = 'Directory role definitions, active role assignments and PIM eligibility (when licensed).'
    Collect        = {
        $roleDefinitions = @(Invoke-TLGraphRequest -Uri '/v1.0/roleManagement/directory/roleDefinitions?$select=id,templateId,displayName,isBuiltIn' -All)
        $assignments = @(Invoke-TLGraphRequest -Uri '/v1.0/roleManagement/directory/roleAssignments?$expand=principal' -All)

        $eligibility = @()
        $eligibilityAvailable = $false
        try {
            $eligibility = @(Invoke-TLGraphRequest -Uri '/v1.0/roleManagement/directory/roleEligibilityScheduleInstances?$expand=principal' -All)
            $eligibilityAvailable = $true
        }
        catch {
            # PIM requires Entra ID P2 - absence is a data point, not an error.
            Write-Verbose ("PIM eligibility not available: {0}" -f $_.Exception.Message)
        }

        $definitionsById = @{}
        foreach ($roleDefinition in $roleDefinitions) { $definitionsById[[string]$roleDefinition.id] = $roleDefinition }

        function Add-PrincipalInfo {
            param([object]$Item, [hashtable]$Definitions)
            $roleName = $null
            if ($Item.PSObject.Properties['roleDefinitionId'] -and $Definitions.ContainsKey([string]$Item.roleDefinitionId)) {
                $roleName = [string]$Definitions[[string]$Item.roleDefinitionId].displayName
            }
            Add-Member -InputObject $Item -NotePropertyName '_tlRoleName' -NotePropertyValue $roleName -Force
            $principalName = $null; $principalType = $null; $principalUpn = $null
            if ($Item.PSObject.Properties['principal'] -and $Item.principal) {
                $principal = $Item.principal
                if ($principal.PSObject.Properties['displayName']) { $principalName = [string]$principal.displayName }
                if ($principal.PSObject.Properties['userPrincipalName']) { $principalUpn = [string]$principal.userPrincipalName }
                if ($principal.PSObject.Properties['@odata.type']) {
                    $principalType = ([string]$principal.'@odata.type') -replace '^#microsoft\.graph\.', ''
                }
            }
            Add-Member -InputObject $Item -NotePropertyName '_tlPrincipalName' -NotePropertyValue $principalName -Force
            Add-Member -InputObject $Item -NotePropertyName '_tlPrincipalType' -NotePropertyValue $principalType -Force
            Add-Member -InputObject $Item -NotePropertyName '_tlPrincipalUpn' -NotePropertyValue $principalUpn -Force
        }

        foreach ($assignment in $assignments) { Add-PrincipalInfo -Item $assignment -Definitions $definitionsById }
        foreach ($schedule in $eligibility) { Add-PrincipalInfo -Item $schedule -Definitions $definitionsById }

        @{
            roleDefinitions      = $roleDefinitions
            assignments          = $assignments
            eligibility          = $eligibility
            eligibilityAvailable = $eligibilityAvailable
        }
    }
}