Saltar al contenido
Microsoft scripts

Scripts de Defender

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.

Antes de correrloCon Microsoft 365 Business Premium aplica, pero solo verás lo que generen sus servicios: Defender for Business en las computadoras y Defender for Office 365 Plan 1 en el correo. Microsoft ubica la investigación de incidentes de correo en Defender for Office 365 Plan 2, así que no esperes ahí la misma cobertura de correo que en Microsoft 365 E5.

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.csv

Obtiene los incidentes ordenados por severidad y reparte primero los de severidad alta que siguen sin responsable.

Cómo implementarlo

  1. Instala el módulo si no lo tienes: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser.
  2. Corre el script e inicia sesión con una cuenta con el rol Lector de seguridad.
  3. Abre el CSV y empieza por las severidades Alta y Media.
  4. 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
}

Relacionados