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.
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.csvObtiene 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
- Abre PowerShell 7 con el módulo Microsoft.Graph.Authentication instalado.
- Corre el script con -RutaCsv e inicia sesión con una cuenta con el rol Lector de seguridad.
- Empieza por el nivel Alto y por los usuarios confirmados como comprometidos.
- 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.
#>
[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
}