Saltar al contenido
MS Scripts

Scripts de Exchange

Alias de correo por destinatario

Muestra la dirección principal y los alias SMTP de cada destinatario, del que más tiene al que menos. Te ayuda a limpiar direcciones viejas después de un cambio de dominio o una fusión.

Solo lecturaSin permisos de GraphProbado con respuestas simuladas. Sin ejecutar en un tenant real.

Antes de ejecutarloExchange Online limita las direcciones proxy por destinatario. La tabla de Microsoft 365 indica 300 y la de planes independientes 400, pero Microsoft recomienda tomar 300 como tope seguro porque el límite depende de los caracteres. Ese límite incluye SIP, X500 y SPO, que no cuentan como alias: compáralo contra TotalDirecciones.

Ejemplo en México

Escuela privada en Puebla, 80 personas

Cambiaron de dominio hace tres años y cada coordinación fue agregando direcciones. Antes del nuevo ciclo escolar quieren saber qué quedó.

.\get-alias-por-buzon.ps1 -Minimo 3 -RutaCsv .\alias.csv

Obtiene los destinatarios con tres alias o más y prepara con cada coordinación la lista de direcciones del dominio anterior que ya pueden quitar.

Cómo implementarlo

  1. Instala el módulo si no lo tienes: Install-Module ExchangeOnlineManagement -Scope CurrentUser.
  2. Ejecuta el script con -Minimo para ver solo a quienes tienen N alias o más, y con -RutaCsv para guardar el resultado.
  3. Inicia sesión con una cuenta que tenga el rol indicado.
  4. Revisa NumeroAlias y Alias en el CSV, y define con cada área qué direcciones se quedan antes de quitar alguna.
Ver el código completo de get-alias-por-buzon.ps1
<#
.SYNOPSIS
    Responde cuántos alias SMTP tiene cada destinatario y cuáles son.

.DESCRIPTION
    Lista la dirección principal y los alias smtp: de cada destinatario, ordenados por número de alias. No cuenta
    direcciones SIP, X500 ni SPO como alias, pero sí las suma en TotalDirecciones, porque el límite de Exchange Online
    es por direcciones proxy: 300 en Microsoft 365 según "Exchange Online limits", que recomienda tomar ese número como
    tope seguro. Los que lo alcanzan se marcan en Aviso.
    Solo lectura: no cambia nada en el tenant.

.PARAMETER Minimo
    Muestra solo los destinatarios con este número de alias o más. Por defecto, todos.

.PARAMETER Tipo
    Tipo de destinatario: UserMailbox, SharedMailbox, RoomMailbox, EquipmentMailbox, GroupMailbox,
    MailUniversalDistributionGroup, MailUniversalSecurityGroup, MailContact, MailUser o Todos (por defecto).

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

.EXAMPLE
    .\get-alias-por-buzon.ps1 -Minimo 3 -RutaCsv .\alias.csv

.NOTES
    Permisos: ninguno de Graph; usa ExchangeOnlineManagement.
    Rol mínimo: Lector global (View-Only Organization Management en Exchange Online).
    Licencia: Exchange Online Plan 1 o superior, incluido en Microsoft 365 Business Premium.
    Licencia del código: MIT. Se entrega TAL CUAL, sin garantía. Pruébalo primero en un tenant de pruebas.
#>
#Requires -Version 7.0
[CmdletBinding()]
param(
    [ValidateRange(0, [int]::MaxValue)]
    [int]$Minimo = 0,

    [ValidateSet('UserMailbox', 'SharedMailbox', 'RoomMailbox', 'EquipmentMailbox', 'GroupMailbox', 'MailUniversalDistributionGroup', 'MailUniversalSecurityGroup', 'MailContact', 'MailUser', 'Todos')]
    [string]$Tipo = 'Todos',

    [string]$RutaCsv
)

$ErrorActionPreference = 'Stop'

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

function Limpiar-Fila($fila) {
    foreach ($p in $fila.PSObject.Properties) {
        if ($p.Value -is [string] -and $p.Value -match '^[=+\-@\t\r]') { $p.Value = "'$($p.Value)" }
    }
    $fila
}

# El CSV se vacía desde el inicio: si algo truena o no hay resultados, no queda uno viejo que parezca actual.
if ($RutaCsv) { Set-Content -LiteralPath $RutaCsv -Value $null -Encoding utf8BOM }

$filtro = @{}
if ($Tipo -ne 'Todos') { $filtro.RecipientTypeDetails = $Tipo }

Connect-ExchangeOnline -ShowBanner:$false
try {
    $destinatarios = @(Get-EXORecipient @filtro -ResultSize Unlimited -Properties DisplayName, PrimarySmtpAddress, EmailAddresses -ErrorAction Stop)
} finally {
    Disconnect-ExchangeOnline -Confirm:$false
}

$filas = foreach ($d in $destinatarios) {
    $direcciones = @($d.EmailAddresses)
    # smtp: en minúsculas es alias y SMTP: en mayúsculas es la principal: la comparación distingue mayúsculas.
    $alias = @($direcciones | Where-Object { "$_" -clike 'smtp:*' } | ForEach-Object { "$_".Substring(5) })
    if ($alias.Count -lt $Minimo) { continue }
    [pscustomobject]@{
        Destinatario      = $d.DisplayName
        DireccionPrincipal = $d.PrimarySmtpAddress
        Tipo              = $d.RecipientTypeDetails
        NumeroAlias       = $alias.Count
        TotalDirecciones  = $direcciones.Count
        Alias             = $alias -join '; '
        Aviso             = if ($direcciones.Count -ge 300) { 'En el límite de 300 direcciones de Exchange Online' } else { '' }
    }
}
$salida = @($filas | Sort-Object NumeroAlias -Descending)

Write-Host "Destinatarios en el reporte: $($salida.Count)"
if ($RutaCsv) {
    if ($salida.Count) {
        # UTF-8 con BOM para que Excel en español muestre bien los acentos.
        $salida | ForEach-Object { Limpiar-Fila $_ } | Export-Csv -LiteralPath $RutaCsv -NoTypeInformation -Encoding utf8BOM
    }
    Write-Host "CSV guardado en $RutaCsv"
} else {
    $salida | Format-Table -AutoSize
}

Parte del playbook

Relacionados