Saltar al contenido
Microsoft scripts

Scripts de Purview

Políticas de retención y lo que cubren

Muestra tus políticas de retención y las cargas de trabajo donde de verdad aplican: Exchange, SharePoint, OneDrive, Teams y aplicaciones.

Antes de correrloEl script no usa la propiedad Workload porque Microsoft documenta que no muestra dónde aplica la política. Hace una consulta con -DistributionDetail por cada política, así que puede tardar. Las políticas con ámbito adaptativo pueden no mostrar ubicaciones: revísalas en el portal. Una política sin reglas queda marcada para revisar, porque no retiene nada aunque se vea habilitada.

Ejemplo en México

Agencia aduanal en Veracruz, 60 personas

Necesitan confirmar que los correos y documentos de los expedientes de importación se conservan y no solo los correos.

.\get-politicas-retencion.ps1 -RutaCsv .\retencion.csv

Ven que la política cubre Exchange en todos los buzones, pero SharePoint en solo dos sitios, y la amplían a los sitios de expedientes.

Cómo implementarlo

  1. Instala el módulo si no lo tienes: Install-Module ExchangeOnlineManagement -Scope CurrentUser.
  2. Ejecuta el script con -RutaCsv e inicia sesión en Security & Compliance PowerShell.
  3. Lee la columna Cargas y detecta lo que falta, por ejemplo chats de Teams sin política.
  4. Filtra Revisar por Sí y corrige las políticas deshabilitadas o sin ubicaciones.
Ver el código completo de get-politicas-retencion.ps1
<#
.SYNOPSIS
    Lista las políticas de retención y las cargas de trabajo que realmente cubren.

.DESCRIPTION
    Usa Get-RetentionCompliancePolicy y Get-AppRetentionCompliancePolicy. Consulta cada política con
    -DistributionDetail porque sin ese modificador las ubicaciones salen inexactas, y no usa la propiedad
    Workload porque Microsoft documenta que no refleja dónde aplica. Solo lectura: no cambia nada en el tenant.

.PARAMETER RutaCsv
    Ruta del CSV de salida. Si se omite, solo se muestra en pantalla.

.EXAMPLE
    .\get-politicas-retencion.ps1 -RutaCsv .\retencion.csv

.NOTES
    Permisos: ninguno de Graph; usa ExchangeOnlineManagement.
    Rol mínimo: rol View-Only Retention Management en un grupo de roles, o grupo Compliance Administrator.
    Licencia: Microsoft 365 E5/A5/G5/E3/A3/G3, Business Premium u Office 365 E5/A5/G5/E3/A3/G3 (hay otras opciones por carga de trabajo).
    Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
[CmdletBinding()]
param(
    [string]$RutaCsv
)

$ErrorActionPreference = 'Stop'

if (-not (Get-Command Connect-IPPSSession -ErrorAction SilentlyContinue)) {
    throw 'Falta el módulo. Instálalo con: Install-Module ExchangeOnlineManagement -Scope CurrentUser'
}

function Resumir($valor) {
    $lista = @($valor | Where-Object { $_ })
    if (-not $lista.Count) { return $null }
    if ("$lista" -match '(^|\s)All($|\s)') { 'todos' } else { "$($lista.Count) específicos" }
}

function Cargas($p) {
    $mapa = [ordered]@{
        'Exchange'                 = $p.ExchangeLocation
        'SharePoint'               = $p.SharePointLocation
        'OneDrive'                 = $p.OneDriveLocation
        'Grupos de Microsoft 365'  = $p.ModernGroupLocation
        'Carpetas públicas'        = $p.PublicFolderLocation
        'Skype Empresarial'        = $p.SkypeLocation
        'Chats de Teams'           = $p.TeamsChatLocation
        'Canales de Teams'         = $p.TeamsChannelLocation
    }
    $partes = foreach ($nombre in $mapa.Keys) {
        $alcance = Resumir $mapa[$nombre]
        if ($alcance) { "$nombre ($alcance)" }
    }
    if ($p.Applications) { $partes = @($partes) + "Aplicaciones ($(@($p.Applications) -join ', '))" }
    @($partes) -join '; '
}

function Detalle($politicas, $comando) {
    foreach ($p in $politicas) {
        # Un -Identity inexistente devuelve todas las políticas; por eso se vuelve a filtrar por GUID.
        $d = & $comando -Identity $p.Guid -DistributionDetail | Where-Object { "$($_.Guid)" -eq "$($p.Guid)" } | Select-Object -First 1
        if ($d) { $d } else { $p }
    }
}

Connect-IPPSSession -ShowBanner:$false
try {
    $normales = @(Detalle @(Get-RetentionCompliancePolicy) 'Get-RetentionCompliancePolicy')
    $deApps = @(Detalle @(Get-AppRetentionCompliancePolicy) 'Get-AppRetentionCompliancePolicy')
    # Una política sin regla no es válida: no retiene nada aunque se vea habilitada.
    $sinRegla = @{}
    foreach ($p in $normales) { if (-not @(Get-RetentionComplianceRule -Policy "$($p.Guid)")) { $sinRegla["$($p.Guid)"] = $true } }
    foreach ($p in $deApps) { if (-not @(Get-AppRetentionComplianceRule -Policy "$($p.Guid)")) { $sinRegla["$($p.Guid)"] = $true } }
} finally {
    Disconnect-ExchangeOnline -Confirm:$false
}

$filas = @(
    foreach ($p in $normales + $deApps) {
        $cargas = Cargas $p
        [pscustomobject]@{
            Politica  = $p.Name
            Habilitada = $p.Enabled
            Modo      = $p.Mode
            Cargas    = if ($cargas) { $cargas } else { 'Sin ubicaciones visibles' }
            Reglas    = if ($sinRegla["$($p.Guid)"]) { 'Sin reglas' } else { 'Con reglas' }
            Revisar   = if ($p.Enabled -eq $false -or $p.Mode -notin @('Enforce', $null) -or -not $cargas -or $sinRegla["$($p.Guid)"]) { 'Sí' } else { 'No' }
        }
    }
)

Write-Host "Políticas: $($filas.Count). Por revisar: $(@($filas | Where-Object Revisar -eq 'Sí').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