Export-EmailAliasReport
Exports all email addresses and aliases to Excel.
Example Output

Syntax
Description
Retrieves all mail-enabled recipients from Exchange Online (excluding contacts and guest mail users) along with Microsoft 365 Groups, and exports all email addresses (primary and aliases) to an Excel report.
Examples
Example 1
Example 2
.\Export-EmailAliasReport.ps1 -Tenant 'contoso.onmicrosoft.com' -Out 'C:\Reports\Aliases.xlsx'
# Exports email aliases for the specified tenant.
Parameters
-Tenant
Optional tenant ID or domain for specific tenant connection.
- Type:
String - Required: No
-Cloud
Cloud environment to connect to. Defaults to 'Global'. Valid values: Global, USGov, USGovDoD, Germany, China
- Type:
String - Required: No
- Valid values:
Global,GCC,GCCH,DoD,Germany,China
-Out
Output file path for the Excel export. If not specified, generates a filename based on tenant name and current timestamp.
- Type:
String - Required: No
Notes
Requires ExchangeOnlineManagement and ImportExcel modules.
Full Script
Copy this complete standalone script (includes all helper functions):
#Requires -Version 7.0
#Requires -Modules ExchangeOnlineManagement, ImportExcel
<#
.SYNOPSIS
Exports all email addresses and aliases to Excel.
.DESCRIPTION
Retrieves all mail-enabled recipients from Exchange Online (excluding contacts
and guest mail users) along with Microsoft 365 Groups, and exports all email
addresses (primary and aliases) to an Excel report.
.PARAMETER Tenant
Optional tenant ID or domain for specific tenant connection.
.PARAMETER Cloud
Cloud environment to connect to. Defaults to 'Global'.
Valid values: Global, USGov, USGovDoD, Germany, China
.PARAMETER Out
Output file path for the Excel export. If not specified, generates
a filename based on tenant name and current timestamp.
.EXAMPLE
.\Export-EmailAliasReport.ps1
# Exports email aliases to an auto-named Excel file.
.EXAMPLE
.\Export-EmailAliasReport.ps1 -Tenant 'contoso.onmicrosoft.com' -Out 'C:\Reports\Aliases.xlsx'
# Exports email aliases for the specified tenant.
.NOTES
Requires ExchangeOnlineManagement and ImportExcel modules.
#>
[CmdletBinding()]
param(
[string]$Tenant,
[ValidateSet('Global', 'GCC', 'GCCH', 'DoD', 'Germany', 'China')
#region Helper Functions
function Write-TimeLog {
[CmdletBinding()]
param(
[Parameter(Mandatory, Position = 0)]
[string]$Message,
[Parameter()]
[ConsoleColor]$Color = 'White'
)
Write-Host "[$(Get-Date -Format 'HH:mm')] $Message" -ForegroundColor $Color
}
function Connect-ExoIfNeeded {
[CmdletBinding()]
param(
[Parameter()]
[string]$Tenant,
[Parameter()]
[ValidateSet('Global', 'GCC', 'GCCH', 'DoD', 'Germany', 'China')]
[string]$Cloud = 'Global'
)
$envMap = @{
'Global' = 'O365Default'
'GCC' = 'O365Default'
'GCCH' = 'O365USGovGCCHigh'
'DoD' = 'O365USGovDoD'
'Germany' = 'O365GermanyCloud'
'China' = 'O365China'
}
try {
$connections = Get-ConnectionInformation -ErrorAction SilentlyContinue
if ($connections) {
$exoConnection = $connections | Where-Object { $_.ConnectionUri -like '*outlook.office*' -or $_.ConnectionUri -like '*protection.office*' }
if ($exoConnection) {
$org = Get-OrganizationConfig -ErrorAction Stop
Write-TimeLog "Using existing Exchange Online connection to $($org.DisplayName)" -Color Cyan
return $org
}
}
}
catch {
# Not connected or error checking - will connect below
}
$connectParams = @{
ExchangeEnvironmentName = $envMap[$Cloud]
ShowBanner = $false
ErrorAction = 'Stop'
}
if ($Tenant) {
$connectParams['Organization'] = $Tenant
}
try {
Connect-ExchangeOnline @connectParams
$org = Get-OrganizationConfig
Write-TimeLog "Connected to Exchange Online: $($org.DisplayName)" -Color Green
return $org
}
catch {
Write-TimeLog "Failed to connect to Exchange Online: $($_.Exception.Message)" -Color Red
throw
}
}
function Get-DefaultExcelParams {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[string]$Path
)
@{
Path = $Path
TableStyle = 'Medium2'
AutoSize = $true
FreezeTopRow = $true
}
}
function New-ReportPath {
[CmdletBinding()]
param(
[Parameter()]
[string]$Out,
[Parameter(Mandatory)]
[string]$FriendlyName,
[Parameter(Mandatory)]
[string]$OrgDisplayName,
[Parameter()]
[string]$Extension = 'xlsx'
)
if (!$Out) {
"$OrgDisplayName-$FriendlyName-$(Get-Date -Format 'yyyy-MM-dd_HH-mm').$Extension"
}
elseif ($Out -notlike "*.$Extension") {
"$Out.$Extension"
}
else {
$Out
}
}
#endregion Helper Functions
]
[string]$Cloud = 'Global',
[string]$Out
)
$org = Connect-ExoIfNeeded -Tenant $Tenant -Cloud $Cloud
$Out = New-ReportPath -Out $Out -FriendlyName 'EmailAliases' -OrgDisplayName $org.DisplayName
# All mail-enabled recipients, excluding contacts and guest mail users
Write-TimeLog "Fetching mailboxes..."
$recipients = Get-EXORecipient -ResultSize Unlimited -PropertySets All | Where-Object {
$_.RecipientType -ne 'MailContact' -and
$_.RecipientTypeDetails -ne 'GuestMailUser'
}
Write-TimeLog "Fetched $($recipients.Count) mailboxes"
# Microsoft 365 Groups with extended properties
Write-TimeLog "Fetching M365 groups..."
$unifiedGroups = Get-UnifiedGroup -ResultSize Unlimited -IncludeAllProperties
Write-TimeLog "Fetched $($unifiedGroups.Count) M365 groups"
# Map M365 group info by primary SMTP (lowercased)
$groupMeta = @{}
foreach ($g in $unifiedGroups) {
if ($g.PrimarySmtpAddress) {
$key = $g.PrimarySmtpAddress.ToString().ToLower()
$groupMeta[$key] = $g
}
}
$rows = [System.Collections.Generic.List[object]]::new()
$typeMap = @{
'PrimarySmtpAddress' = 'Primary'
'EmailAddresses' = 'Alias'
}
Write-TimeLog "Processing recipients..."
foreach ($r in $recipients) {
$seen = [System.Collections.Generic.HashSet[string]]::new()
$primarySmtp = $null
if ($r.PrimarySmtpAddress) {
$primarySmtp = $r.PrimarySmtpAddress.ToString()
}
$primaryKey = $null
if ($primarySmtp) {
$primaryKey = $primarySmtp.ToLower()
}
$groupInfo = $null
if ($primaryKey -and $groupMeta.ContainsKey($primaryKey)) {
$groupInfo = $groupMeta[$primaryKey]
}
$groupType = $null
switch ($r.RecipientTypeDetails) {
'GroupMailbox' { $groupType = 'M365 Group' }
'MailUniversalDistributionGroup' { $groupType = 'Distribution Group' }
'MailUniversalSecurityGroup' { $groupType = 'Mail-enabled Security Group' }
'DynamicDistributionGroup' { $groupType = 'Dynamic Distribution Group' }
'RoomList' { $groupType = 'Room List' }
}
$mkRow = {
param(
[string]$Email,
[string]$Source
)
if (-not $Email) { return }
$key = $Email.ToLower()
if ($seen.Contains($key)) { return }
[void]$seen.Add($key)
$localPart = $null
$domain = $null
if ($Email -like '*@*') {
$localPart = $Email.Split('@')[0]
$domain = $Email.Split('@')[1]
}
$friendlyType = $typeMap[$Source]
if (-not $friendlyType) {
$friendlyType = $Source
}
$rows.Add([pscustomobject]@{
EmailAddress = $Email
LocalPart = $localPart
Domain = $domain
Type = $friendlyType
UPN = $r.WindowsLiveID
DisplayName = $r.DisplayName
Mailbox = $r.RecipientTypeDetails
Hidden = $r.HiddenFromAddressListsEnabled
GroupType = $groupType
AllowExternalSenders = if ($groupInfo) { -not $groupInfo.RequireSenderAuthenticationEnabled } else { $null }
CopyToMemberInbox = if ($groupInfo) { $groupInfo.AutoSubscribeNewMembers } else { $null }
AccessType = if ($groupInfo) { $groupInfo.AccessType } else { $null }
})
}
# Primary SMTP
if ($primarySmtp) {
& $mkRow -Email $primarySmtp -Source 'PrimarySmtpAddress'
}
# All SMTP proxy addresses (primary + aliases)
if ($r.EmailAddresses) {
foreach ($addr in $r.EmailAddresses) {
$s = $addr.ToString()
if ($s -like 'SMTP:*' -or $s -like 'smtp:*') {
$smtp = $s.Substring(5)
& $mkRow -Email $smtp -Source 'EmailAddresses'
}
}
}
}
# Export to Excel
$excelParams = Get-DefaultExcelParams -Path $Out
$rows |
Sort-Object EmailAddress, Mailbox, DisplayName |
Export-Excel @excelParams -WorksheetName 'Emails' -TableName 'Emails'
Write-TimeLog "Exported to: $Out" -Color Green