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.
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.csvObtiene 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
- Instala el módulo si no lo tienes: Install-Module ExchangeOnlineManagement -Scope CurrentUser.
- Ejecuta el script con -Minimo para ver solo a quienes tienen N alias o más, y con -RutaCsv para guardar el resultado.
- Inicia sesión con una cuenta que tenga el rol indicado.
- 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
}