Saltar al contenido
Microsoft scripts

Scripts de Purview

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.

Antes de correrloCon el rol eDiscovery Manager solo verás los casos que creaste, y el reporte se verá incompleto sin avisar. El nombre de un caso puede revelar información sensible: cuida a quién le compartes el CSV.

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

Encuentran un caso activo que ya debía cerrarse y otro cerrado con error, y los pasan a Legal para decidir.

Cómo implementarlo

  1. Instala el módulo si no lo tienes: Install-Module Microsoft.Graph.Authentication -Scope CurrentUser.
  2. Ejecuta el script con -RutaCsv con una cuenta eDiscovery Administrator para ver todos los casos.
  3. Busca los casos activos sin movimiento reciente y los cerrados con error.
  4. 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
}