PowerShell Exchange Online Distribution Group Audit

Distribution groups accumulate silently over years: project lists whose projects ended, team aliases where the team disbanded, external-sender-enabled groups whose owners have left the company. Without active governance, each one is a potential email security risk or a compliance gap. A PowerShell audit of your Exchange Online distribution groups surfaces every group’s membership count, ownership status, and external sender configuration in a single exportable report.
Quick Answer
Connect with Connect-ExchangeOnline, enumerate groups with Get-DistributionGroup -ResultSize Unlimited, pull members with Get-DistributionGroupMember, and flag groups where ManagedBy is empty or RequireSenderAuthenticationEnabled is $false for immediate review.
Connecting to Exchange Online and Getting All Groups
The Exchange Online PowerShell module (ExchangeOnlineManagement) is required. Install it once from PSGallery if not already present.
# Install if needed
# Install-Module ExchangeOnlineManagement -Scope CurrentUser
Import-Module ExchangeOnlineManagement
# Connect — opens browser for MFA if required
Connect-ExchangeOnline -UserPrincipalName "[email protected]" -ShowProgress $true
# Get all distribution groups (not Microsoft 365 Groups)
$groups = Get-DistributionGroup -ResultSize Unlimited |
Where-Object { $_.RecipientTypeDetails -eq "MailUniversalDistributionGroup" }
Write-Host "Found $($groups.Count) distribution groups."
Note the RecipientTypeDetails filter. Get-DistributionGroup also returns mail-enabled security groups. Adjust the filter if you want to include those in the audit. Microsoft 365 Groups require Get-UnifiedGroup — they are a different cmdlet family entirely.
Enumerating Members with Get-DistributionGroupMember
Member counts and the list of members are the first governance data points. Large groups — especially those with external members — deserve extra scrutiny.
$groupData = foreach ($group in $groups) {
$members = Get-DistributionGroupMember -Identity $group.Identity `
-ResultSize Unlimited `
-ErrorAction SilentlyContinue
$externalMembers = $members |
Where-Object { $_.RecipientTypeDetails -eq "MailContact" -or
$_.ExternalEmailAddress -ne $null }
[PSCustomObject]@{
DisplayName = $group.DisplayName
Alias = $group.Alias
MemberCount = $members.Count
ExternalMembers = $externalMembers.Count
PrimarySmtp = $group.PrimarySmtpAddress
ManagedBy = ($group.ManagedBy -join "; ")
HasOwner = ($group.ManagedBy.Count -gt 0)
RequireAuth = $group.RequireSenderAuthenticationEnabled
HiddenFromGAL = $group.HiddenFromAddressListsEnabled
}
}
Write-Host "Processed $($groupData.Count) groups."
Identifying Groups with No Assigned Owner
Ownerless groups are a governance red flag: no one is responsible for reviewing membership or approving sender requests.
$noOwner = $groupData | Where-Object { -not $_.HasOwner }
Write-Host "Groups with no owner: $($noOwner.Count)"
$noOwner | Select-Object DisplayName, Alias, MemberCount, PrimarySmtp |
Format-Table -AutoSize
Assign an interim owner or deprecate ownerless groups. You can set an owner with Set-DistributionGroup -Identity "alias" -ManagedBy "[email protected]" directly from the audit script.
Checking RequireSenderAuthenticationEnabled Setting
When RequireSenderAuthenticationEnabled is $false, anyone on the internet can send to the group — a frequent spam and phishing vector. Audit how many groups have this open.
$openToExternal = $groupData | Where-Object { $_.RequireAuth -eq $false }
Write-Host "Groups open to external senders: $($openToExternal.Count)"
foreach ($g in $openToExternal) {
Write-Host " OPEN: $($g.DisplayName) ($($g.Alias)) — $($g.MemberCount) members" `
-ForegroundColor Yellow
}
# Remediate: lock down a specific group
# Set-DistributionGroup -Identity "dl-allstaff" `
# -RequireSenderAuthenticationEnabled $true
Reporting Last Delivery Date from Message Trace
Groups that have not received email in months are candidates for decommissioning. Get-MessageTrace provides delivery history but has a 10-day lookback limit for standard queries.
$endDate = Get-Date
$startDate = $endDate.AddDays(-10)
$groupData = $groupData | ForEach-Object {
$smtpAddr = $_.PrimarySmtp
$trace = Get-MessageTrace -RecipientAddress $smtpAddr `
-StartDate $startDate `
-EndDate $endDate `
-ErrorAction SilentlyContinue |
Select-Object -First 1
$_ | Add-Member -MemberType NoteProperty `
-Name LastDelivery `
-Value ($trace.Received ?? "No mail in 10 days") `
-Force
$_
}
# For older data use Start-HistoricalSearch (async, results via email)
# Start-HistoricalSearch -ReportTitle "DL Audit" -StartDate (Get-Date).AddDays(-30) ...
Exporting the Full Group Audit to CSV
Combine all collected data into a final export for review meetings and compliance records.
$reportPath = "DLAudit-$(Get-Date -Format yyyyMMdd).csv"
$groupData | Select-Object DisplayName, Alias, PrimarySmtp,
MemberCount, ExternalMembers,
HasOwner, ManagedBy,
RequireAuth, HiddenFromGAL, LastDelivery |
Sort-Object HasOwner, DisplayName |
Export-Csv $reportPath -NoTypeInformation -Encoding UTF8
Write-Host "Audit exported: $reportPath ($($groupData.Count) groups)"
# Disconnect cleanly
Disconnect-ExchangeOnline -Confirm:$false
Common Errors
- Get-MessageTrace maximum lookback of 10 days: The cmdlet hard-limits results to the last 10 days. For historical delivery data beyond that window, use
Start-HistoricalSearch, which runs asynchronously and delivers results to an admin mailbox — or use the Microsoft 365 Compliance center Message Trace for up to 90 days. - Mixing Distribution Groups and Microsoft 365 Groups:
Get-DistributionGroupdoes not return Microsoft 365 Groups (formerly Office 365 Groups). Those requireGet-UnifiedGroup. If your audit needs to cover both, run both cmdlets and combine the results — the ownership, membership, and external sender properties use different names between the two cmdlet families.
Related Cmdlets / See Also
Wrapping Up
A quarterly distribution group audit takes minutes with this script and surfaces ownerless groups, open external relays, and dormant lists before they become compliance findings. Schedule it, export to CSV, and route flagged groups to their business owners for disposition decisions.


