<# .SYNOPSIS Lista las alertas de DLP recientes desde Microsoft Graph. .DESCRIPTION Consulta security/alerts_v2 filtrando por serviceSource dataLossPrevention. Si no hay alertas, avisa que desde octubre de 2026 una regla integrada de Microsoft Defender XDR puede estar convirtiéndolas en comportamientos. Solo lectura: no cambia nada en el tenant. .PARAMETER Dias Días hacia atrás. Por defecto 30. .PARAMETER ZonaHoraria Zona horaria para mostrar las fechas. .PARAMETER RutaCsv Ruta del CSV de salida. Si se omite, solo se muestra en pantalla. .EXAMPLE .\get-alertas-dlp.ps1 -Dias 30 -RutaCsv .\alertas-dlp.csv .NOTES Permisos: SecurityAlert.Read.All Rol mínimo: Lector de seguridad Licencia: para investigar en el portal de Defender, Microsoft 365 E5/A5, Office 365 E5/A5, Microsoft 365 E5/A5 Compliance o Information Protection and Governance. Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas. #> [CmdletBinding()] param( [ValidateRange(1, 3650)] [int]$Dias = 30, [string]$ZonaHoraria = 'Central Standard Time (Mexico)', [string]$RutaCsv ) $ErrorActionPreference = 'Stop' $permisos = @('SecurityAlert.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' } function HoraLocal($valor) { if (-not $valor) { return '' } $utc = if ($valor -is [datetime]) { if ($valor.Kind -eq 'Unspecified') { [datetime]::SpecifyKind($valor, 'Utc') } else { $valor.ToUniversalTime() } } else { [datetimeoffset]::Parse("$valor", [cultureinfo]::InvariantCulture, 'AssumeUniversal').UtcDateTime } [TimeZoneInfo]::ConvertTimeFromUtc($utc, $zona).ToString('yyyy-MM-dd HH:mm') } $severidades = @{ high = 'Alta'; medium = 'Media'; low = 'Baja'; informational = 'Informativa'; unknown = 'Desconocida' } $estados = @{ new = 'Nueva'; inProgress = 'En curso'; resolved = 'Resuelta'; unknown = 'Desconocido' } Connect-MgGraph -Scopes $permisos -NoWelcome $desde = (Get-Date).ToUniversalTime().AddDays(-$Dias).ToString('yyyy-MM-ddTHH:mm:ssZ') $filtro = [uri]::EscapeDataString("serviceSource eq 'dataLossPrevention' and createdDateTime ge $desde") $uri = "https://graph.microsoft.com/v1.0/security/alerts_v2?`$filter=$filtro" $alertas = while ($uri) { $pagina = Invoke-MgGraphRequest -Method GET -Uri $uri $pagina.value $uri = $pagina.'@odata.nextLink' } $filas = @( foreach ($a in $alertas) { [pscustomobject]@{ Creada = HoraLocal $a.createdDateTime Titulo = $a.title Severidad = if ($severidades[[string]$a.severity]) { $severidades[[string]$a.severity] } else { $a.severity } Estado = if ($estados[[string]$a.status]) { $estados[[string]$a.status] } else { $a.status } AsignadaA = $a.assignedTo Clasificacion = $a.classification Incidente = $a.incidentId Liga = $a.alertWebUrl } } ) Write-Host "Alertas de DLP en $Dias días: $($filas.Count). Sin resolver: $(@($filas | Where-Object Estado -ne 'Resuelta').Count)." if (-not $filas.Count) { Write-Warning 'Sin alertas. Revisa que tus reglas DLP generen alertas y, en Microsoft Defender XDR, la regla integrada de Alert tuning para señales DLP: desde octubre de 2026 las maneja como comportamientos en lugar de alertas.' } 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 }