Find unused SMTP proxy addresses (90 days)
Check user and shared mailboxes to find SMTP proxy addresses that have not been used to send or receive email in the last 90 days, using historical message trace data downloaded from Exchange Online.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop# Get accepted domains
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
[array]$Modules = Get-Module | Select-Object -ExpandProperty NameIf (!($Modules -contains "ExchangeOnlineManagement")) {Write-Host "Connecting to Exchange Online..."Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop# Get accepted domains[array]$Domains = Get-AcceptedDomain | Select-Object -ExpandProperty DomainName}# Folder where the historical message tracking logs downloaded from Exchange Online are stored. Change this to the folder you use.$DataFolder = "c:\temp\MtData\"# Find the set of downloaded historical message trace data files to process[array]$DataFiles = Get-ChildItem -Path $DataFolder | Select-Object -ExpandProperty NameIf (!($DataFiles)) {Write-Host "No historical message tracking logs to analyze - exiting"Break}Write-Host ("Preparing to process {0} historical message trace data files..." -f $DataFiles.count)$Report = [System.Collections.Generic.List[Object]]::new() # Create output file for report# Define a set of bad outcomes from message trace data entries that we don't need to report on. These include# messages intercepted as spam, quarantined messages, and failed messages.[array]$BadOutcomes = "Receive, Fail", "Receive, Deliver, Quarantined", "Receive, Deliver, FilteredAsSpam"Write-Host "Analyzing historical message trace data..."ForEach ($File in $DataFiles) {$MtDataFile = $DataFolder + $File[array]$MtData = Import-CSV -Path $MtDataFile -Encoding unicodeForEach ($Line in $MtData) {If (!([string]::IsNullOrEmpty($Line.origin_timestamp_utc))){[array]$RecipientStatus = $Line.Recipient_Status.split(";")# array of individual recipients for a message$RecipientInfo = [System.Collections.Generic.List[Object]]::new()ForEach ($RecipientDetail in $RecipientStatus) {$Recipient = $RecipientDetail.Split("##")[0]$RecipientOutcome = $RecipientDetail.Split("##")[1]$RecipientLine = [PSCustomObject]@{Recipient = $RecipientOutcome = $RecipientOutcome}$RecipientInfo.Add($RecipientLine)}$SenderDomain = $Line.Sender_address.Split("@")[1]If ($SenderDomain -in $Domains) {$Direction = "Originating"} Else {$Direction = "Incoming"}# Only report on messages with a good outcomeIf (!($RecipientOutcome -in $BadOutcomes)) {$ReportLine = [PSCustomObject]@{Timestamp = $Line.origin_timestamp_utcSender = $Line.sender_addressSubject = $Line.message_subjectRecipient = $RecipientInfo.RecipientRecipientDomain = $RecipientInfo.Recipient.Split("@")[1]RecipientStatus = $Line.Recipient_StatusRecipientInfo = $RecipientInfoOutcome = $RecipientOutcomeBytes = $Line.total_bytesMessage_id = $Line.message_idSender_Domain = $SenderDomainClient_IP = $Line.original_client_ipDirection = $Direction}$Report.Add($ReportLine)}}}}$Report = $Report | Sort-Object Sender, @{Expression = { $_.Timestamp -as [datetime] }; Descending = $true}# Create an array of the latest messages sent or received by each email address in the tenant[array]$SenderAddresses = $Report | Where-Object {$_.Sender_domain -in $Domains} | Sort-Object {$_.Timestamp -as [datetime]} -Descending | Group-Object Sender | ForEach-Object { $_.Group | Select-Object -First 1 }[array]$RecipientAddresses = $Report | Where-Object {$_.RecipientDomain -in $Domains} | Sort-Object {$_.Timestamp -as [datetime]} -Descending | Group-Object Recipient | ForEach-Object { $_.Group | Select-Object -First 1 }Write-Host ("{0} unique sender addresses found in the last 90 days" -f $SenderAddresses.count)Write-Host ("{0} unique recipient addresses found in the last 90 days" -f $RecipientAddresses.count)Write-Host "Now checking for unused proxy addresses assigned to mailboxes..."[array]$Mailboxes = Get-ExoMailbox -ResultSize Unlimited -Properties EmailAddresses -RecipientTypeDetails UserMailbox, SharedMailbox | Sort-Object DisplayName$UnusedProxyReport = [System.Collections.Generic.List[Object]]::new()ForEach ($Mbx in $Mailboxes) {$MbxAddresses = $Mbx.EmailAddresses | Where-Object {$_ -like "smtp:*"}ForEach ($Address in $MbxAddresses) {$SmtpAddress = $Address.Substring(5) # Remove the smtp: prefixIf ($Address.SubString(0,4) -ceq "SMTP") {$AddressType = "Primary"} Else {$AddressType = "Proxy"}If ($SmtpAddress -notin $SenderAddresses.Sender -and $SmtpAddress -notlike "*.onmicrosoft.com") {$ReportLine = [PSCustomObject]@{DisplayName = $Mbx.DisplayNamePrimarySmtpAddress = $Mbx.PrimarySmtpAddressAlias = $Mbx.AliasMailboxType = $Mbx.RecipientTypeDetailsUnusedProxyAddress = $SmtpAddressAddressType = $AddressTypeCheckType = "Sender"}$UnusedProxyReport.Add($ReportLine)}If ($SmtpAddress -notin $RecipientAddresses.Recipient -and $SmtpAddress -notlike "*.onmicrosoft.com") {$ReportLine = [PSCustomObject]@{DisplayName = $Mbx.DisplayNamePrimarySmtpAddress = $Mbx.PrimarySmtpAddressAlias = $Mbx.AliasMailboxType = $Mbx.RecipientTypeDetailsUnusedProxyAddress = $SmtpAddressAddressType = $AddressTypeCheckType = "Recipient"}$UnusedProxyReport.Add($ReportLine)}}}# Show what we've found$UnusedProxyReport | Select-Object DisplayName, UnusedProxyAddress, AddressType, CheckType, MailboxType, PrimarySmtpAddress | Out-GridView -Title "Unused Proxy Addresses"# And generate an output fileIf (Get-Module ImportExcel -ListAvailable) {$ExcelGenerated = $TrueImport-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}$UnusedProxyReport | 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"$UnusedProxyReport | 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)}