Saltar al contenido
MS Scripts

Scripts de Entra

Usuarios en riesgo de Identity Protection

Lista a los usuarios que Microsoft Entra ID Protection considera en riesgo o confirmados como comprometidos, ordenados por nivel. Es tu lista de quién atender primero.

Solo lecturaPide 1 permisoProbado con respuestas simuladas. Sin ejecutar en un tenant real.

Antes de ejecutarloSin Microsoft Entra ID P2 la API de Microsoft Graph no entrega estos reportes. Microsoft 365 Business Premium trae P1, así que necesitas P2, por ejemplo con el complemento Microsoft Defender Suite for Business Premium. Usa -IncluirResueltos si también quieres ver a los remediados o descartados.

Ejemplo en México

Universidad privada en Puebla, 1,200 personas

Tras una campaña de phishing dirigida a docentes, el área de sistemas quiere saber qué cuentas Microsoft ya detectó como sospechosas.

.\get-usuarios-en-riesgo.ps1 -RutaCsv .\riesgo.csv

Obtiene 14 usuarios en riesgo, tres de nivel alto. Les restablece la contraseña ese mismo día y revisa sus reglas de bandeja.

Cómo implementarlo

  1. Abre PowerShell 7 con el módulo Microsoft.Graph.Authentication instalado.
  2. Ejecuta el script con -RutaCsv e inicia sesión con una cuenta con el rol Lector de seguridad.
  3. Empieza por el nivel Alto y por los usuarios confirmados como comprometidos.
  4. Restablece la contraseña, revoca las sesiones y revisa en el portal las detecciones de cada usuario.
Ver el código completo de get-usuarios-en-riesgo.ps1
<#
.SYNOPSIS
    ¿Qué usuarios marca Microsoft Entra ID Protection como en riesgo?

.DESCRIPTION
    Lista los usuarios en riesgo o confirmados como comprometidos, con su nivel y el último cambio de riesgo.
    Te da una lista priorizada para restablecer contraseñas y revocar sesiones.
    Solo lectura: no cambia nada en el tenant.

.PARAMETER RutaCsv
    Ruta del CSV de salida. Si se omite, solo se muestra en pantalla.

.PARAMETER IncluirResueltos
    Incluye también a los usuarios remediados, descartados o confirmados como seguros.

.PARAMETER ZonaHoraria
    Zona horaria para mostrar las fechas. Por defecto, Central Standard Time (Mexico).

.EXAMPLE
    .\get-usuarios-en-riesgo.ps1 -RutaCsv .\riesgo.csv

.NOTES
    Permisos: IdentityRiskyUser.Read.All
    Rol mínimo: Lector de seguridad (Security Reader)
    Licencia: Microsoft Entra ID P2
    Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
#Requires -Version 7.0
[CmdletBinding()]
param(
    [string]$RutaCsv,
    [switch]$IncluirResueltos,
    [string]$ZonaHoraria = 'Central Standard Time (Mexico)'
)

$ErrorActionPreference = 'Stop'
$permisos = @('IdentityRiskyUser.Read.All')

if (-not (Get-Command Connect-MgGraph -ErrorAction SilentlyContinue)) {
    throw 'Falta el módulo. Instálalo con: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser'
}
$zona = [TimeZoneInfo]::FindSystemTimeZoneById($ZonaHoraria)
Connect-MgGraph -Scopes $permisos -NoWelcome

function Format-FechaLocal {
    param($Valor)
    if (-not $Valor) { return $null }
    $utc = if ($Valor -is [datetimeoffset]) { $Valor.UtcDateTime }
           elseif ($Valor -is [datetime]) { if ($Valor.Kind -eq 'Unspecified') { [datetime]::SpecifyKind($Valor, 'Utc') } else { $Valor.ToUniversalTime() } }
           else { [datetimeoffset]::Parse("$Valor", [cultureinfo]::InvariantCulture).UtcDateTime }
    [TimeZoneInfo]::ConvertTimeFromUtc($utc, $zona).ToString('yyyy-MM-dd HH:mm', [cultureinfo]::InvariantCulture)
}

$uri = 'https://graph.microsoft.com/v1.0/identityProtection/riskyUsers'
$usuarios = while ($uri) {
    $pagina = Invoke-MgGraphRequest -Method GET -Uri $uri
    $pagina.value
    $uri = $pagina.'@odata.nextLink'
}

$nivel = @{ high = 'Alto'; medium = 'Medio'; low = 'Bajo'; hidden = 'Oculto'; none = 'Ninguno' }
$estado = @{
    atRisk = 'En riesgo'; confirmedCompromised = 'Confirmado comprometido'; remediated = 'Remediado'
    dismissed = 'Descartado'; confirmedSafe = 'Confirmado seguro'; none = 'Ninguno'
}
$orden = @{ high = 0; medium = 1; low = 2 }

$filas = foreach ($u in $usuarios) {
    if (-not $IncluirResueltos -and $u.riskState -notin 'atRisk', 'confirmedCompromised') { continue }
    [pscustomobject]@{
        Nombre         = $u.userDisplayName
        Cuenta         = $u.userPrincipalName
        Nivel          = if ($nivel[$u.riskLevel]) { $nivel[$u.riskLevel] } else { $u.riskLevel }
        Estado         = if ($estado[$u.riskState]) { $estado[$u.riskState] } else { $u.riskState }
        Detalle        = $u.riskDetail
        UsuarioBorrado = if ($u.isDeleted) { 'Sí' } else { 'No' }
        UltimoCambio   = Format-FechaLocal $u.riskLastUpdatedDateTime
        Prioridad      = if ($null -ne $orden[$u.riskLevel]) { $orden[$u.riskLevel] } else { 3 }
    }
}
$filas = $filas | Sort-Object Prioridad, Nombre | Select-Object -ExcludeProperty Prioridad -Property *

Write-Host "Usuarios en el reporte: $(@($filas).Count)"
if ($RutaCsv) {
    # Excel ejecutaría como fórmula un nombre que empiece con estos caracteres.
    foreach ($fila in $filas) { foreach ($p in $fila.PSObject.Properties) { if ($p.Value -is [string] -and $p.Value -match '^[=+\-@]') { $p.Value = "'" + $p.Value } } }
    # UTF-8 con BOM para que Excel en español muestre bien los acentos.
    $filas | Export-Csv -Path $RutaCsv -NoTypeInformation -Encoding utf8BOM
    Write-Host "CSV guardado en $RutaCsv"
} else {
    $filas | Format-Table -AutoSize
}

Parte del playbook

Relacionados