๐๏ธ Recipe ยท Microsoft 365 Governance
Audit Entra ID Groups and Distribution Lists: Find Duplicates, Empty, and Inactive Groups
Identify redundant, empty, and stale groups/DLs across Entra ID and Exchange Online to improve governance and reduce clutter
Complexity
Intermediate
Impact
governance + cleanup + reporting + access-management
Context
Why This Matters
Why audit your groups and distribution lists?
Over time, Microsoft 365 tenants accumulate a large number of Entra ID security groups, Microsoft 365 groups, and Exchange distribution lists (DLs). Many of these are created for short-term projects, departing teams, or ad-hoc needs โ and then forgotten. The result is group sprawl: duplicate groups with confusing names, empty groups that still appear in address books, and inactive DLs that receive mail nobody reads.
Running a periodic audit helps you:
- Identify duplicates (same or near-identical display name) that confuse end users and cause wrong-group mailings.
- Find empty groups with zero members โ often leftover from failed provisioning or deleted teams.
- Flag inactive groups (no email, SharePoint, or Yammer activity in 60+ days) for archiving or deletion.
Run this audit quarterly, or before any tenant cleanup, license renegotiation, or migration project.
Expected Outcomes
What you'll have at the end
- A full inventory of all groups in the tenant (Entra ID security, Microsoft 365 Unified, and mail-enabled/DLs) with display name, primary SMTP, type, and ID.
- A list of duplicate display names (case-insensitive) with all matching group IDs.
- A list of empty groups (member count = 0).
- A list of inactive Microsoft 365 groups โ no recorded activity in the last 60 days.
- An exportable CSV report suitable for review with business owners before deletion.
Risks & Considerations
Warnings and gotchas
- Do not delete groups immediately. Always confirm with the business owner before removing โ some groups are used once a year (audit, compliance, year-end) and may appear inactive.
- Report anonymization: By default, the tenant report setting
displayConcealedNamesistrue, which masks group names in usage reports. Temporarily disabling it makes the audit usable โ but always restore it afterwards to comply with your privacy policy. - Distribution Lists (classic Exchange DLs) are not fully represented in the Microsoft 365 Groups activity report. For pure Exchange DLs, message-trace data (Exchange Online PowerShell) is required to confirm real inactivity.
- Security groups with no members may still be referenced in Conditional Access policies, SharePoint permissions, or app role assignments โ deletion can break access. Always check references before removing.
- Dynamic groups may temporarily have 0 members if the rule returns no match; do not treat these the same as static empty groups.
Required Permissions
| Permission | Why It's Needed |
|---|---|
| Group.Read.All | List all groups and enumerate members to detect empty groups |
| Directory.Read.All | Read group type, mail-enabled, and security-enabled properties across the directory |
| Reports.Read.All | Read Microsoft 365 Groups activity usage reports to detect inactivity |
| ReportSettings.ReadWrite.All | Temporarily disable report anonymization so groups appear by name in activity reports |
The fastest way to get this done: just ask Dex. Copy the prompt below and paste it into your Dex conversation.
For IT Admins
Paste into Dex Pro
Manual audit via admin portals
The manual workflow spans three portals. Expect this to take 45โ90 minutes for a tenant with a few hundred groups.
Step 1 โ Disable report anonymization (temporarily)
- Go to .
- Uncheck Display concealed user, group, and site names in all reports.
- Click Save. (Remember to re-enable this at the end.)
Step 2 โ Export the Groups list from Entra
- Go to .
- Click Download groups to export the full list as CSV.
- In Excel, sort by
displayNameand use conditional formatting (Highlight duplicate values) to spot duplicates.
Step 3 โ Find empty groups
- In the same Groups blade, click a group, then Members. Groups with 0 members are candidates.
- For a bulk view, open the CSV export and sort by
Memberscount column if available, or use the PowerShell approach below.
Step 4 โ Check Microsoft 365 Groups activity
- Go to .
- Set the period to 90 days.
- Click Export. In Excel, filter rows where
Last Activity Dateis blank or older than 60 days.
Step 5 โ Exchange Distribution Lists
- Go to .
- Filter by Distribution list.
- For each DL, review Members and last message activity (the latter requires a message trace or Exchange Online PowerShell).
Step 6 โ Restore anonymization
Return to and re-check Display concealed names. Click Save.
Graph API audit workflow
All endpoints are relative to https://graph.microsoft.com.
1. Check report anonymization setting
GET /v1.0/admin/reportSettings
// Response
{
"displayConcealedNames": true
}2. Temporarily disable anonymization
PATCH /v1.0/admin/reportSettings
Content-Type: application/json
{ "displayConcealedNames": false }
// 204 No Content3. List all groups (paginated)
GET /v1.0/groups?$select=id,displayName,mail,groupTypes,mailEnabled,securityEnabled&$top=999
// Follow @odata.nextLink until null4. Count members per group (to detect empty groups)
GET /v1.0/groups/{group-id}/members/$count
ConsistencyLevel: eventual
// Returns an integer. 0 = empty group.5. Pull 90-day groups activity report
GET /v1.0/reports/getOffice365GroupsActivityDetail(period='D90')
// Returns CSV by default. Columns include:
// Group Display Name, Last Activity Date, Group Type, Member Count,
// Exchange Received Email Count, SharePoint Active File Count, Group Id6. Detect duplicates (client-side)
Group the results of step 3 by lowercase displayName. Any key with more than one group ID is a duplicate.
7. Detect inactivity (client-side)
From the CSV in step 5, flag rows where Last Activity Date is empty or older than today โ 60 days.
8. Restore anonymization
PATCH /v1.0/admin/reportSettings
{ "displayConcealedNames": true }PowerShell approach using Microsoft Graph SDK
The provided script connects to Microsoft Graph, toggles anonymization, enumerates all groups, detects duplicates/empties/inactives, exports three CSVs, and restores the original anonymization setting.
Prerequisites
- PowerShell 7+
- Microsoft.Graph module:
Install-Module Microsoft.Graph -Scope CurrentUser - An admin account with the permissions listed above
Usage
# Run the audit with default inactivity threshold (60 days)
.\Audit-M365Groups.ps1 -OutputFolder C:\Reports
# Use a custom threshold and skip anonymization toggle
.\Audit-M365Groups.ps1 -OutputFolder C:\Reports -InactiveDays 90 -SkipAnonymizationToggleOutputs
Groups-Duplicates.csvโ groups that share a display nameGroups-Empty.csvโ groups with 0 membersGroups-Inactive.csvโ M365 groups with no activity in the threshold window
Review each CSV with business owners before any deletion, and restore displayConcealedNames = true immediately after.
Next steps