Back to script library
Entra / Microsoft 365 · Exchange Online

Find unused SMTP proxy addresses (10 days)

Check user and shared mailboxes to find SMTP proxy addresses that have not been used to send or receive email in the last 10 days, using Exchange Online message trace data.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-ExchangeOnline -ShowBanner:$False -ErrorAction Stop

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

param(
[int] $LookbackDays = 10,
[string] $StartDate = (Get-Date).AddDays(-$LookbackDays),
[string] $EndDate = (Get-Date),
[int] $BatchSize = 2000
)
If ($Null -eq (Get-ConnectionInformation)) {
Connect-ExchangeOnline -ShowBanner:$False -ErrorAction Stop
}
# Message trace date is kept for a maximum of 10 days
Write-Host ("Message trace data will be analyzed between {0} and {1}" -f (Get-Date $StartDate -format 'dd-MMM-yyyy HH:mm'), (Get-Date $EndDate -format 'dd-MMM-yyyy HH:mm'))
Write-Host "Fetching message trace data to analyze"
[array]$Messages = $Null
[int]$BatchSizeForMessages = 2000
# original code [array]$MessagePage = Get-MessageTrace -StartDate $StartDate -EndDate $EndDate -PageSize 1000 -Page $i -Status "Delivered"
Try {
# The warning action is suppressed here because we don't want to see warnings when more data is available
[array]$MessagePage = Get-MessageTraceV2 -StartDate $StartDate -EndDate $EndDate `
-ResultSize $BatchSizeForMessages -Status "Delivered" -ErrorAction Stop -WarningAction SilentlyContinue
$Messages += $MessagePage
} Catch {
Write-Host ("Error fetching message trace data: {0}" -f $_.Exception.Message)
Break
}
If ($MessagePage.count -eq $BatchSizeForMessages) {
Do {
Write-Host ("Fetched {0} messages so far" -f $Messages.count)
$LastMessageFetched = $MessagePage[-1]
$LastMessageFetchedDate = $LastMessageFetched.Received.ToString("O")
$LastMessageFetchedRecipient = $LastMessageFetched.RecipientAddress
# Fetch the next page of messages
[array]$MessagePage = Get-MessageTraceV2 -StartDate $StartDate -EndDate $LastMessageFetchedDate `
-StartingRecipientAddress $LastMessageFetchedRecipient -ResultSize $BatchSizeForMessages -Status "Delivered" -ErrorAction Stop -WarningAction SilentlyContinue
If ($MessagePage) {
$Messages += $MessagePage
}
} While ($MessagePage.count -eq $BatchSizeForMessages)
}
# Remove Exchange Online public folder hierarchy synchronization messages
$Messages = $Messages | Where-Object {$_.Subject -NotLike "*HierarchySync*"}
# Now, do we have any messsages to process?
If ($Messages.count -eq 0) {
Write-Host "No messages found for analysis"
Break
} Else {
Write-Host ("After excluding system messages, there are {0} messages for analysis" -f $Messages.count)
}
[array]$Domains = Get-AcceptedDomain | Select-Object -ExpandProperty DomainName
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($M in $Messages) {
$Direction = "Inbound"
$SenderDomain = $M.SenderAddress.Split("@")[1]
$RecipientDomain = $M.RecipientAddress.Split("@")[1]
If ($SenderDomain -in $Domains) {
$Direction = "Outbound"
}
$ReportLine = [PSCustomObject]@{
TimeStamp = $M.Received
Sender = $M.SenderAddress
Recipient = $M.RecipientAddress
Subject = $M.Subject
Status = $M.Status
Direction = $Direction
SenderDomain = $SenderDomain
RecipientDomain = $RecipientDomain
}
$Report.Add($ReportLine)
}
# Build an array of email addresses from this tenant used to send or receive messages
[array]$Senders = $Report | Where-Object {$_.SenderDomain -in $Domains} | Sort-Object Sender -Unique | Select-Object -ExpandProperty Sender
[array]$Recipients = $Report | Where-Object {$_.RecipientDomain -in $Domains} | Sort-Object Recipient -Unique | Select-Object -ExpandProperty Recipient
[array]$AllAddresses = $Senders + $Recipients | Sort-Object -Unique
$ReportAddresses = [System.Collections.Generic.List[Object]]::new()
# Find mailboxes
[array]$Mailboxes = Get-ExoMailbox -ResultSize Unlimited -Properties EmailAddresses -RecipientTypeDetails UserMailbox, SharedMailbox | Sort-Object DisplayName
# This code to check against the array of all senders and recipients in the last 10 days
ForEach ($Mbx in $Mailboxes) {
$MbxAddresses = $Mbx.EmailAddresses | Where-Object {$_ -like "smtp:*"}
ForEach ($Address in $MbxAddresses) {
$SmtpAddress = $Address.Substring(5) # Remove the smtp: prefix
If ($SmtpAddress -notin $AllAddresses) {
$ReportLine = [PSCustomObject]@{
DisplayName = $Mbx.DisplayName
PrimarySmtpAddress = $Mbx.PrimarySmtpAddress
Alias = $Mbx.Alias
MailboxType = $Mbx.RecipientTypeDetails
UnusedProxyAddress = $SmtpAddress
CheckType = "Sender"
}
$ReportAddresses.Add($ReportLine)
}
}
}
$ReportAddresses | Out-GridView
generate an output file
If (Get-Module ImportExcel -ListAvailable) {
$ExcelGenerated = $True
Import-Module ImportExcel -ErrorAction SilentlyContinue
$ExcelOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\UnusedProxyAddresses.xlsx"
If (Test-Path $ExcelOutputFile) {
Remove-Item $ExcelOutputFile -ErrorAction SilentlyContinue
}
$ReportAddresses | Export-Excel -Path $ExcelOutputFile -WorksheetName "Unused Proxy Addresses" -Title ("Unused Proxy Addresses Report {0}" -f (Get-Date -format 'dd-MMM-yyyy')) -TitleBold -TableName "UnusedProxyAddresses"
} Else {
$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\UnusedProxyAddresses.CSV"
$ReportAddresses | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding Utf8
}
If ($ExcelGenerated) {
Write-Host ("Excel worksheet output written to {0}" -f $ExcelOutputFile)
} Else {
Write-Host ("CSV output file written to {0}" -f $CSVOutputFile)
}

Parameters

ParameterDefaultNotes
-LookbackDays10Number of days back to search message trace data (Exchange Online limit is 10 days).
-StartDate(Get-Date).AddDays(-10)Start of the message trace search window.
-EndDate(Get-Date)End of the message trace search window.
-BatchSize2000Maximum number of message trace records to fetch per batch.
Attribution