Incidentes abiertos de Microsoft Defender XDR
Lista los incidentes activos o en progreso, de mayor a menor severidad, para que sepas cuál atender primero y cuál no tiene responsable.
Ejemplo en México
Manufactura de autopartes en Querétaro, 180 personas
El único administrador de TI regresó de vacaciones y necesita saber qué quedó pendiente en Defender antes del cambio de turno.
.\get-incidentes-abiertos.ps1 -RutaCsv .\incidentes.csvObtiene los incidentes ordenados por severidad y reparte primero los de severidad alta que siguen sin responsable.
Cómo implementarlo
- Instala el módulo si no lo tienes: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser.
- Corre el script e inicia sesión con una cuenta con el rol Lector de seguridad.
- Abre el CSV y empieza por las severidades Alta y Media.
- Asigna responsable a los que digan Sin asignar y entra a cada uno con la columna Liga.
Ver el código completo de get-incidentes-abiertos.ps1
<#
.SYNOPSIS
¿Qué incidentes de Microsoft Defender XDR siguen abiertos y cuáles atiendo primero?
.DESCRIPTION
Lista los incidentes en estado activo o en progreso, ordenados de mayor a menor severidad,
con un resumen por severidad, el responsable y la liga al portal.
Solo lectura: no cambia nada en el tenant.
.PARAMETER RutaCsv
Ruta del CSV de salida. Si se omite, solo se muestra en pantalla.
.PARAMETER ZonaHoraria
Zona horaria para mostrar las fechas. Por defecto, Central Standard Time (Mexico).
.EXAMPLE
.\get-incidentes-abiertos.ps1 -RutaCsv .\incidentes.csv
.NOTES
Permisos: SecurityIncident.Read.All
Rol mínimo: Lector de seguridad
Licencia: Cualquier licencia con acceso a Microsoft Defender XDR, por ejemplo Microsoft 365 Business Premium, Microsoft Defender for Business, Microsoft Defender for Endpoint, Microsoft Defender for Office 365 Plan 2 o Microsoft 365 E5
Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
[CmdletBinding()]
param(
[string]$RutaCsv,
[string]$ZonaHoraria = 'Central Standard Time (Mexico)'
)
$ErrorActionPreference = 'Stop'
$permisos = @('SecurityIncident.Read.All')
$zona = [TimeZoneInfo]::FindSystemTimeZoneById($ZonaHoraria)
if (-not (Get-Command Connect-MgGraph -ErrorAction SilentlyContinue)) {
throw 'Falta el módulo. Instálalo con: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser'
}
Connect-MgGraph -Scopes $permisos -NoWelcome
function ConvertirFecha($valor) {
if (-not $valor) { return $null }
$utc = if ($valor -is [datetime]) { $valor.ToUniversalTime() } else { ([datetimeoffset]"$valor").UtcDateTime }
[TimeZoneInfo]::ConvertTimeFromUtc($utc, $zona).ToString('yyyy-MM-dd HH:mm')
}
$severidades = @{ high = 'Alta'; medium = 'Media'; low = 'Baja'; informational = 'Informativa'; unknown = 'Desconocida' }
$orden = @{ high = 0; medium = 1; low = 2; informational = 3; unknown = 4 }
$estados = @{ active = 'Activo'; inProgress = 'En progreso' }
# awaitingAction no se pide: solo lo asigna Defender Experts y exige un encabezado Prefer.
$uri = "https://graph.microsoft.com/v1.0/security/incidents?`$filter=status eq 'active' or status eq 'inProgress'"
$incidentes = while ($uri) {
$pagina = Invoke-MgGraphRequest -Method GET -Uri $uri
$pagina.value
$uri = $pagina.'@odata.nextLink'
}
$filas = $incidentes |
Sort-Object { if ($orden.ContainsKey("$($_.severity)")) { $orden["$($_.severity)"] } else { 5 } }, { -[int]$_.priorityScore } |
ForEach-Object {
[pscustomobject]@{
Severidad = if ($severidades["$($_.severity)"]) { $severidades["$($_.severity)"] } else { $_.severity }
Estado = if ($estados["$($_.status)"]) { $estados["$($_.status)"] } else { $_.status }
Incidente = $_.displayName
Id = $_.id
Prioridad = $_.priorityScore
Responsable = if ($_.assignedTo) { $_.assignedTo } else { 'Sin asignar' }
Creado = ConvertirFecha $_.createdDateTime
UltimaActualizacion = ConvertirFecha $_.lastUpdateDateTime
Liga = $_.incidentWebUrl
}
}
Write-Host "Incidentes abiertos: $(@($filas).Count)"
$filas | Group-Object Severidad | ForEach-Object { Write-Host " $($_.Name): $($_.Count)" }
if ($RutaCsv) {
# 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
}