Private/Get-NSPConditionalAccessAdminRoleHolder.ps1

function Get-NSPConditionalAccessAdminRoleHolder {
    <#
    .SYNOPSIS
        Users who hold an active, tenant-wide assignment of a role NSP-ADMIN-MFA-01 targets, directly or through a role-assignable group.
    .DESCRIPTION
        Returns a map of user ID to { Id, Roles (role names), Via (role names with 'via <group>'
        when assigned through a group) }. These users are covered by their role: the admin policy
        requires MFA at every sign-in, so coverage leaves cloud-only admins out of the groups.
 
        Only what Conditional Access sees counts: active assignments scoped to the whole directory
        ('/'). Eligible (PIM) assignments and administrative-unit scopes are not read. Service
        principals are ignored. Any read failure throws: an unread role is never an empty one.
        Needs RoleManagement.Read.Directory.
    .EXAMPLE
        (Get-NSPConditionalAccessAdminRoleHolder).Keys
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param()

    $table = Get-NSPConditionalAccessRoleTable
    $definition = @(Get-NSPConditionalAccessDefinition | Where-Object { $_['Id'] -eq 'NSP-ADMIN-MFA-01' })[0]
    $names = if ($definition) {
        @($definition['Users']['IncludeRoles'] | Where-Object { $_ -match '^\{Role:(.+)\}$' } | ForEach-Object { $Matches[1] })
    } else { @($table.Keys | Sort-Object) }

    $holders = @{}
    function Add-Holder {
        param([string]$Id, [string]$Role)
        if (-not $holders.ContainsKey($Id)) { $holders[$Id] = [pscustomobject]@{ Id = $Id; Roles = @(); Via = @() } }
        if ($holders[$Id].Roles -notcontains $Role) { $holders[$Id].Roles = @($holders[$Id].Roles) + $Role }
    }
    $groupMembers = @{}
    foreach ($name in $names) {
        $templateId = $table[$name]
        if (-not $templateId) { continue }
        $filter = [uri]::EscapeDataString("roleDefinitionId eq '$templateId'")
        foreach ($assignment in Invoke-NSPGraphCollection -Uri "roleManagement/directory/roleAssignments?`$filter=$filter&`$expand=principal") {
            $scope = Get-NSPConditionalAccessMapValue $assignment 'directoryScopeId'
            if ($scope -and $scope -ne '/') { continue }
            $principal = Get-NSPConditionalAccessMapValue $assignment 'principal'
            $principalId = [string](Get-NSPConditionalAccessMapValue $assignment 'principalId')
            switch ([string](Get-NSPConditionalAccessMapValue $principal '@odata.type')) {
                '#microsoft.graph.user' {
                    Add-Holder $principalId $name
                    $holders[$principalId].Via = @($holders[$principalId].Via) + $name
                }
                '#microsoft.graph.group' {
                    if (-not $groupMembers.ContainsKey($principalId)) {
                        $groupMembers[$principalId] = @(Invoke-NSPGraphCollection -Uri "groups/$principalId/transitiveMembers/microsoft.graph.user?`$select=id" |
                                ForEach-Object { Get-NSPConditionalAccessMapValue $_ 'id' })
                    }
                    $groupName = Get-NSPConditionalAccessMapValue $principal 'displayName'
                    foreach ($memberId in $groupMembers[$principalId]) {
                        Add-Holder $memberId $name
                        $holders[$memberId].Via = @($holders[$memberId].Via) + "$name via $groupName"
                    }
                }
            }
        }
    }
    $holders
}