To find empty groups in Microsoft Entra ID, count each group's members and keep the groups where the count is 0. The count is of direct members, so a group whose only member is another group is not empty. An empty group can still be in use, named in a policy or assigned licences ready for people to join, so check before you delete it.
| Method | Best fit |
|---|---|
| PowerShell | A tenant-wide CSV of every group with no members, with its type. |
| Admin centre | Checking the members of a few groups you already suspect. |
| Altivane | Recommended for recurring clean-ups, with the share of empty groups rated against a threshold. |
Before you start
Use an account with a role that can read groups and their members, such as Directory Readers, Groups Administrator or User Administrator. No extra licence is needed: group management is part of Microsoft Entra ID Free, which comes with Microsoft 365. The PowerShell example requests GroupMember.; your organisation may require administrator consent.
Know what the count covers. It counts users, devices, contacts and other groups that are direct members. The count is an advanced query that uses an index, which might not be up to date with very recent changes. The examples cover every kind of group: Microsoft 365, security, mail-enabled security and distribution. They read and export; they don't delete or change any group.
Option 1
Find empty groups with PowerShell
Install the modules once, then connect, count each group's members and export the groups with none.
# Install once, if the modules are not already installed.
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Groups -Scope CurrentUser$ErrorActionPreference = 'Stop'
Connect-MgGraph -Scopes 'GroupMember.Read.All' -NoWelcome
try {
$properties = 'id', 'displayName', 'groupTypes', 'mailEnabled', 'securityEnabled',
'onPremisesSyncEnabled', 'resourceProvisioningOptions', 'createdDateTime'
$groups = @(Get-MgGroup -All -Property $properties)
$report = @(foreach ($group in ($groups | Sort-Object DisplayName)) {
# A member count is an advanced query: it needs ConsistencyLevel set to eventual.
$count = Get-MgGroupMemberCount -GroupId $group.Id -ConsistencyLevel eventual
if ($count -ne 0) { continue }
$type = 'Security'
if ($group.GroupTypes -contains 'Unified') { $type = 'Microsoft 365' }
elseif ($group.MailEnabled -and $group.SecurityEnabled) { $type = 'Mail-enabled security' }
elseif ($group.MailEnabled) { $type = 'Distribution' }
[pscustomobject]@{
Group = $group.DisplayName
Type = $type
Dynamic = $group.GroupTypes -contains 'DynamicMembership'
Team = $group.ResourceProvisioningOptions -contains 'Team'
Synced = $group.OnPremisesSyncEnabled -eq $true
Created = $group.CreatedDateTime
Id = $group.Id
}
})
if ($report.Count -eq 0) {
Write-Output "All $($groups.Count) groups have at least 1 member."
} else {
$report | Export-Csv -Path './empty-groups.csv' -NoTypeInformation -Encoding utf8
Write-Output "$($report.Count) of $($groups.Count) groups have no members."
}
} finally {
Disconnect-MgGraph | Out-Null
}The script makes 1 request for each group, so a large tenant takes a while. It marks dynamic groups, whose members come from a rule, groups behind a team and groups synced from an on-premises directory, whose membership is managed there. Keep only Microsoft 365 and Security rows to leave out the groups Exchange manages.
Check a single group
Reconnect with Connect-MgGraph, then count 1 group's members. The main export above disconnects when it finishes.
# Run while connected to Microsoft Graph. Replace the example group name.
$group = @(Get-MgGroup -Filter "displayName eq 'Contoso Sales'")
if ($group.Count -ne 1) { throw "Found $($group.Count) groups with that name; use its ID." }
$count = Get-MgGroupMemberCount -GroupId $group[0].Id -ConsistencyLevel eventual
Write-Output "$($group[0].DisplayName) has $count direct members."Option 2
Check group members in the admin centre
- Sign in to the Microsoft Entra admin centre for the correct tenant.
- Go to Entra ID > Groups > All groups.
- Search for a group and select it. Its Overview page shows how many members it has.
- Select Members to see who they are, then leave without adding or removing anyone.
This suits checking a few groups you already suspect. The admin centre shows member counts 1 group at a time, so to find every empty group in a tenant, use PowerShell or a report.
Option 3 ยท Recommended for recurring clean-ups
Report on empty groups with Altivane
- Open Altivane and choose the tenant.
- Build the tenant's documents with the Identity scorecard report in full, so the groups are listed as well as counted.
- Open the tenant's Excel workbook and find Empty Groups: each group with no members, its type, whether it is dynamic or behind a team, its owners and when it was created.
- Read Group Summary for the counts by type, and the scorecard for how the share of empty groups rates against your thresholds.
Altivane counts the members of every group, distribution, mail-enabled and synced groups included. A group whose count can't be read is never taken for empty: the figure says Not read and the run is marked partial. The scorecard rates the share of all groups that are empty, 10% for Warning and 30% for Critical to start. The table goes to the workbook only to start, with up to 100 rows when you put it in the Word report.
Contact usCommon questions
Is an empty group safe to delete?
Not always. An empty group can still be named in a Conditional Access policy, given access to an app or assigned licences, ready for people to be added. Microsoft says a group's licences must be removed before the group is deleted. Check what refers to the group first, and ask its owner.
Why is a dynamic group empty?
Its membership rule matches no one at the moment. The members of a dynamic group come from its rule, so read the rule and the properties it tests rather than adding people manually.
How do I find groups with no owner?
Empty and ownerless are separate checks: a group can be either, both or neither. See how to find groups without owners.
Microsoft references
Technical references checked on 6 November 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.