Public/Get-EhPrivilegedWithoutMfa.ps1

function Get-EhPrivilegedWithoutMfa {
    <#
    .SYNOPSIS
        Encuentra cuentas con roles privilegiados que no tienen métodos de
        autenticación fuertes registrados.

    .DESCRIPTION
        Una cuenta de administrador sin MFA fuerte es la combinación más peligrosa de
        un tenant: privilegio alto y la barrera de entrada más baja. Esta función cruza
        los miembros de roles de directorio con sus métodos de autenticación y marca a
        quienes solo tienen contraseña (o métodos débiles como SMS).

        SOLO LECTURA. Devuelve objetos.

        Permisos de Graph mínimos (solo lectura):
          RoleManagement.Read.Directory, UserAuthenticationMethod.Read.All

    .PARAMETER InvokeGraph
        Solo para pruebas: inyecta un mock de Invoke-EhGraph.

    .OUTPUTS
        PSCustomObject con: Upn, Roles, MetodosFuertes, MetodosDebiles
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [scriptblock] $InvokeGraph
    )

    $leer = if ($InvokeGraph) { $InvokeGraph } else { { param($u) Invoke-EhGraph -Uri $u } }

    # Métodos considerados FUERTES (resistentes a phishing o basados en app/FIDO).
    $fuertes = @(
        '#microsoft.graph.fido2AuthenticationMethod'
        '#microsoft.graph.microsoftAuthenticatorAuthenticationMethod'
        '#microsoft.graph.windowsHelloForBusinessAuthenticationMethod'
        '#microsoft.graph.softwareOathAuthenticationMethod'
    )

    $roles = & $leer '/directoryRoles'
    # Mapa upn -> lista de roles, para no consultar dos veces al mismo usuario.
    $porUsuario = @{}
    foreach ($rol in $roles) {
        $miembros = & $leer "/directoryRoles/$($rol.id)/members?`$select=id,userPrincipalName"
        foreach ($m in $miembros) {
            if (-not $m.userPrincipalName) { continue }
            if (-not $porUsuario.ContainsKey($m.id)) {
                $porUsuario[$m.id] = [pscustomobject]@{ Upn = $m.userPrincipalName; Roles = @() }
            }
            $porUsuario[$m.id].Roles += $rol.displayName
        }
    }

    foreach ($id in $porUsuario.Keys) {
        $entrada = $porUsuario[$id]
        $metodos = & $leer "/users/$id/authentication/methods"
        $tipos = @($metodos | ForEach-Object { $_.'@odata.type' })
        $conFuerte = @($tipos | Where-Object { $fuertes -contains $_ }).Count
        $debiles = @($tipos | Where-Object { $fuertes -notcontains $_ }).Count

        if ($conFuerte -eq 0) {
            [pscustomobject]@{
                Upn            = $entrada.Upn
                Roles          = ($entrada.Roles -join ', ')
                MetodosFuertes = $conFuerte
                MetodosDebiles = $debiles
            }
        }
    }
}