Private/Get-IntuneAccessRoleAssignments.ps1

function Get-IntuneAccessRoleAssignments {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [AllowEmptyCollection()] [object[]] $RoleDefinition,
        [switch] $ResolveNames
    )

    $null = Assert-IntuneAccessConnection -RequiredScope 'DeviceManagementRBAC.Read.All'
    $rawAssignments = [System.Collections.Generic.List[object]]::new()
    foreach ($definition in $RoleDefinition) {
        $definitionId = [string] (Get-IntuneAccessProperty $definition 'Id')
        if ([string]::IsNullOrWhiteSpace($definitionId)) {
            Write-Warning "A role definition did not return an ID. Its assignments cannot be retrieved, so that role is not evaluated."
            continue
        }

        # v1.0 is authoritative for stable assignment data. Beta is isolated enrichment
        # for roleScopeTagIds, scopeMembers and scopeType, which v1.0 does not expose.
        $uri = "deviceManagement/roleDefinitions/$definitionId/roleAssignments"
        $listedAssignments = @(Invoke-IntuneAccessGraphRequest -Uri $uri)

        foreach ($listedAssignment in $listedAssignments) {
            $assignment = $listedAssignment
            $assignmentId = [string] (Get-IntuneAccessProperty $listedAssignment 'id')
            $betaAssignment = $null

            # Graph's assignment collection can return the assignment identities with
            # empty members and scope arrays. Hydrate each item from its detail endpoint
            # before evaluating access so a valid positive assignment is not omitted.
            if (-not [string]::IsNullOrWhiteSpace($assignmentId)) {
                try {
                    $stableDetail = @(Invoke-IntuneAccessGraphRequest -Uri "$uri/$assignmentId")
                    if ($stableDetail.Count -gt 0) {
                        $assignment = $stableDetail[0]
                    }
                }
                catch {
                    Write-Warning "Stable detail retrieval failed for role assignment '$assignmentId'. Collection data is retained, but member and scope data may be incomplete. $($_.Exception.Message)"
                }

                try {
                    $betaDetail = @(Invoke-IntuneAccessGraphRequest -Uri "$uri/$assignmentId" -ApiVersion beta)
                    if ($betaDetail.Count -gt 0) {
                        $betaAssignment = $betaDetail[0]
                    }
                }
                catch {
                    Write-Warning "Beta enrichment failed for role assignment '$assignmentId'. Stable v1.0 assignment data is retained, while scope type and scope tags are marked missing. $($_.Exception.Message)"
                }
            }

            $rawAssignments.Add([PSCustomObject] @{
                Stable         = $assignment
                Beta           = $betaAssignment
                RoleDefinition = $definition
            })
        }
    }

    $allTags = @()
    $allGroups = @()
    if ($ResolveNames) {
        try {
            $allTags = @(Get-IntuneAccessScopeTags)
        }
        catch {
            Write-Warning "Scope tag names could not be retrieved from beta. Assignment tag IDs are retained with unresolved display names. $($_.Exception.Message)"
        }
        $allGroupIds = @($rawAssignments | ForEach-Object {
            @(Get-IntuneAccessProperty $_.Stable 'members' @())
            @(Get-IntuneAccessProperty $_.Stable 'resourceScopes' @())
        } | ForEach-Object { [string] $_ } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique)
        $allGroups = @(Resolve-IntuneAccessGroups -GroupId $allGroupIds)
    }

    $output = [System.Collections.Generic.List[object]]::new()
    foreach ($entry in $rawAssignments) {
        $assignment = $entry.Stable
        $betaAssignment = $entry.Beta
        $definition = $entry.RoleDefinition
        $missingValue = [object]::new()
        $adminGroupValue = Get-IntuneAccessProperty $assignment 'members' $missingValue
        $scopeGroupValue = Get-IntuneAccessProperty $assignment 'resourceScopes' $missingValue
        $scopeMemberValue = Get-IntuneAccessProperty $betaAssignment 'scopeMembers' $missingValue
        $scopeTagValue = Get-IntuneAccessProperty $betaAssignment 'roleScopeTagIds' $missingValue
        $scopeTypeValue = Get-IntuneAccessProperty $betaAssignment 'scopeType' $missingValue
        $adminGroupIds = @(
            $(if ([object]::ReferenceEquals($adminGroupValue, $missingValue)) { @() } else { @($adminGroupValue) }) |
                ForEach-Object { [string] $_ } |
                Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
        )
        $scopeGroupIds = @(
            $(if ([object]::ReferenceEquals($scopeGroupValue, $missingValue)) { @() } else { @($scopeGroupValue) }) |
                ForEach-Object { [string] $_ } |
                Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
        )
        $scopeMemberIds = @(
            $(if ([object]::ReferenceEquals($scopeMemberValue, $missingValue)) { @() } else { @($scopeMemberValue) }) |
                ForEach-Object { [string] $_ } |
                Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
        )
        $scopeTagIds = @(
            $(if ([object]::ReferenceEquals($scopeTagValue, $missingValue)) { @() } else { @($scopeTagValue) }) |
                ForEach-Object { [string] $_ } |
                Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
        )
        $scopeType = if ([object]::ReferenceEquals($scopeTypeValue, $missingValue)) { 'unknown' } else { [string] $scopeTypeValue }

        $adminGroups = if ($ResolveNames) {
            @($adminGroupIds | ForEach-Object {
                $groupId = $_
                $allGroups | Where-Object Id -EQ $groupId | Select-Object -First 1
            })
        }
        else { @() }
        $scopeGroups = if ($ResolveNames) {
            @($scopeGroupIds | ForEach-Object {
                $groupId = $_
                $allGroups | Where-Object Id -EQ $groupId | Select-Object -First 1
            })
        }
        else { @() }
        $scopeTags = if ($ResolveNames) {
            @($scopeTagIds | ForEach-Object {
                $tagId = $_
                $resolved = $allTags | Where-Object Id -EQ $tagId | Select-Object -First 1
                if ($null -ne $resolved) {
                    $resolved
                }
                else {
                    [PSCustomObject] @{ Id = $tagId; DisplayName = '[Unresolved scope tag]'; IsBuiltIn = $null; SourceApiVersion = 'beta' }
                }
            })
        }
        else { @() }

        $output.Add([PSCustomObject] @{
            PSTypeName          = 'IntuneAccess.RoleAssignment'
            Id                  = [string] (Get-IntuneAccessProperty $assignment 'id')
            Name                = [string] (Get-IntuneAccessProperty $assignment 'displayName')
            Description         = [string] (Get-IntuneAccessProperty $assignment 'description')
            RoleDefinition      = $definition
            AdminGroups         = $adminGroups
            AdminGroupDataState = if ([object]::ReferenceEquals($adminGroupValue, $missingValue)) { 'Missing' } else { 'Available' }
            ScopeGroups         = $scopeGroups
            ScopeType           = $scopeType
            ScopeTypeDataState  = if ([object]::ReferenceEquals($scopeTypeValue, $missingValue)) { 'Missing' } else { 'Available' }
            ScopeTags           = $scopeTags
            ScopeTagDataState   = if ([object]::ReferenceEquals($scopeTagValue, $missingValue)) { 'Missing' } else { 'Available' }
            ScopeGroupDataState = if ([object]::ReferenceEquals($scopeGroupValue, $missingValue)) { 'Missing' } else { 'Available' }
            Permissions         = @(Get-IntuneAccessAllowedActions -RoleDefinition $definition)
            Applicability       = 'NotAssessed'
            AdminGroupEvidence  = @()
            SourceApiVersion    = 'v1.0+beta'
            PropertySource      = [PSCustomObject] @{
                IdentityAndMembers = 'v1.0'
                ScopeGroups       = 'v1.0'
                ScopeType         = if ([object]::ReferenceEquals($scopeTypeValue, $missingValue)) { 'Missing' } else { 'beta' }
                ScopeMembers      = if ([object]::ReferenceEquals($scopeMemberValue, $missingValue)) { 'Missing' } else { 'beta' }
                ScopeTags         = if ([object]::ReferenceEquals($scopeTagValue, $missingValue)) { 'Missing' } else { 'beta' }
            }
            RawIds              = [PSCustomObject] @{
                AdminGroupIds  = $adminGroupIds
                ScopeGroupIds  = $scopeGroupIds
                ScopeMemberIds = $scopeMemberIds
                ScopeTagIds    = $scopeTagIds
            }
        })
    }

    return $output.ToArray()
}