Casos de eDiscovery y su estado
Lista los casos de eDiscovery, quién los modificó y cómo están. Señala los cerrados con error al liberar retenciones, que pueden dejar contenido retenido.
Ejemplo en México
Productora agrícola en Hermosillo, 220 personas
Un litigio laboral terminó hace meses y no saben si quedaron casos abiertos o contenido retenido de exempleados.
.\get-casos-ediscovery.ps1 -RutaCsv .\casos-ediscovery.csvEncuentran un caso activo que ya debía cerrarse y otro cerrado con error, y los pasan a Legal para decidir.
Cómo implementarlo
- Instala el módulo si no lo tienes: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser.
- Ejecuta el script con -RutaCsv con una cuenta eDiscovery Administrator para ver todos los casos.
- Busca los casos activos sin movimiento reciente y los cerrados con error.
- Revisa esos casos con Legal y resuelve las retenciones pendientes en el portal de Microsoft Purview.
Ver el código completo de get-casos-ediscovery.ps1
<#
.SYNOPSIS
Lista los casos de eDiscovery y su estado desde Microsoft Graph.
.DESCRIPTION
Consulta security/cases/ediscoveryCases y señala los casos cerrados con error, porque ese estado indica
fallas al liberar retenciones. Con el rol eDiscovery Manager solo se ven los casos propios; con eDiscovery
Administrator, todos. Solo lectura: no cambia nada en el tenant.
.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-casos-ediscovery.ps1 -RutaCsv .\casos-ediscovery.csv
.NOTES
Permisos: eDiscovery.Read.All
Rol mínimo: eDiscovery Manager para casos propios; eDiscovery Administrator para todos los casos de la organización.
Licencia: eDiscovery (Standard) en Microsoft 365 E3/E5/A5/F5/G5 u Office 365 E3/A3/G3/F3/E5/A5/G5; eDiscovery (Premium) requiere E5 o un complemento equivalente.
Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
[CmdletBinding()]
param(
[string]$ZonaHoraria = 'Central Standard Time (Mexico)',
[string]$RutaCsv
)
$ErrorActionPreference = 'Stop'
$permisos = @('eDiscovery.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')
}
$estados = @{
active = 'Activo'
closed = 'Cerrado'
closing = 'Cerrándose'
closedWithError = 'Cerrado con error al liberar retenciones'
pendingDelete = 'Eliminación pendiente'
unknown = 'Desconocido'
}
Connect-MgGraph -Scopes $permisos -NoWelcome
$uri = 'https://graph.microsoft.com/v1.0/security/cases/ediscoveryCases'
$casos = while ($uri) {
$pagina = Invoke-MgGraphRequest -Method GET -Uri $uri
$pagina.value
$uri = $pagina.'@odata.nextLink'
}
$filas = @(
foreach ($c in $casos) {
[pscustomobject]@{
Caso = $c.displayName
Estado = if ($estados[[string]$c.status]) { $estados[[string]$c.status] } else { $c.status }
NumeroExterno = $c.externalId
Creado = HoraLocal $c.createdDateTime
Modificado = HoraLocal $c.lastModifiedDateTime
ModificadoPor = $c.lastModifiedBy.user.displayName
Cerrado = HoraLocal $c.closedDateTime
CerradoPor = $c.closedBy.user.displayName
}
}
)
Write-Host "Casos: $($filas.Count). Activos: $(@($filas | Where-Object Estado -eq 'Activo').Count). Con error: $(@($filas | Where-Object Estado -like '*error*').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
}