Entra / Microsoft 365 · Exchange Online
Find inactive distribution lists (90 days)
Find inactive distribution lists using historical message trace data downloaded from Exchange Online, covering up to 90 days.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-ExchangeOnline
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-ExchangeOnline$Report = [System.Collections.Generic.List[Object]]::new()$DataFolder = "c:\temp\MtData\"$CSVFile = "c:\temp\HistoricalDLMessageTrace.CSV"[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# Create a hash table of distribution list SMTP addresses and display names that we can use to# check if a recipient in a message trace file is a DL[array]$DLs = Get-DistributionGroup -ResultSize Unlimited$DLSMTPAddresses = @{}ForEach ($DL in $DLs) {$DLSMTPAddresses.Add([string]$DL.PrimarySMTPAddress,[string]$DL.DisplayName)}# Loop through the historical trace files to find message trace data related to messages sent to# distribution lists in the tenantForEach ($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 messageForEach ($RecipientDetail in $RecipientStatus) {$DLName = $Null$Recipient = $RecipientDetail.Split("##")[0]$DLName = $DLSMTPAddresses[$Recipient]If ($DLName) { # DL recipient found$SenderDomain = $Line.Sender_address.Split("@")[1]$ReportLine = [PSCustomObject]@{Timestamp = $Line.origin_timestamp_utcSender = $Line.sender_addressSubject = $Line.message_subjectDLSMTP = $RecipientDLName = $DLNameBytes = $Line.total_bytesMessage_id = $Line.message_idSender_Domain = $SenderDomainClient_IP = $Line.original_client_ipDirection = $Line.directionality}$Report.Add($ReportLine)}}}}}$OutputReport = [System.Collections.Generic.List[Object]]::new()[int]$TotalDLOK = 0# Check each DL to see if we can find a recordForEach ($DL in $DLs) {[array]$DLFound = $Report | Where-Object {$_.DLSMTP -eq $DL.PrimarySMTPAddress} | Sort-Object -Descending {$_.TimeStamp -as [datetime]} | `Select-Object -First 1If ($DLFound) {$DateLastMessage = (Get-Date $DLFound.TimeStamp -format g)Write-Host ("Found message for Distribution list {0} at {1}" -f $DL.DisplayName, $DateLastMessage) -Foregroundcolor Red$Text = ("DL state checked on {0} and determined as active. Last message addressed on {1}" `-f (Get-Date -format g), $DateLastMessage )Set-DistributionGroup -Identity $DL.Alias -CustomAttribute15 $Text$TotalDLOK++$ReportLine = [PSCustomObject]@{Timestamp = $DLFound.TimestampSender = $DLFound.SenderDLName = $DLFound.DLNameDLSMTP = $DLFound.DLSMTPSubject = $DLFound.Subject}$OutputReport.Add($ReportLine)} Else {Write-Host ("No messages found for distribution list {0}" -f $DL.DisplayName) -ForegroundColor Yellow$ReportLine = [PSCustomObject]@{Timestamp = "N/A"Sender = "N/A"DLName = $DL.DisplayNameDLSMTP = $DL.PrimarySMTPAddressSubject = "No messages found"}$OutputReport.Add($ReportLine)}}$OutputReport | Sort-Object TimeStamp | Out-GridView$OutputReport | Export-CSV -NoTypeInformation $CSVFile -Encoding utf8$PercentOK = ($TotalDLOK/$DLs.Count).ToString("P")Write-Host ""Write-Host ("Total distribution lists checked: {0}" -f $DLs.count)Write-Host ("Active distribution lists: {0}" -f $TotalDLOK)Write-Host ("Percentage active distribution lists: {0}" -f $PercentOK)Write-Host ("Inactive distribution lists: {0}" -f ($DLs.count - $TotalDLOK))Write-Host ""Write-Host ("Report file available in: {0}" -f $CSVFile)
Attribution
Author
Office365itpros