Identifying and managing public M365 groups: a PowerShell script for governance
A PowerShell script to identify and monitor public Microsoft 365 groups, reducing oversharing risks and improving tenant governance.
Introduction
One of the most underestimated problems in managing a Microsoft 365 tenant is the presence of public groups created unintentionally by users. These groups pose a significant risk to data security, especially since the introduction of Microsoft 365 Copilot, which has access to all content visible to the user, including content from public groups the user may not even be aware of.
In this article I’ll show you a PowerShell script I use regularly with my clients to identify every public M365 group in the tenant, extracting detailed information about each one. This script is an essential starting point for any effective governance strategy.
The problem with public groups
Before looking at the script, it’s important to understand why public groups are a problem.
When a user creates an M365 group (through Teams, Outlook, Planner or other workloads), they can choose between two visibility levels:
- Private: only members can see the group’s content
- Public: every user in the organisation can see and access the content
The problem arises when users:
- Don’t understand the difference between public and private
- Select “public” thinking it simply means “internal to the company”
- Don’t realise they are making potentially sensitive documents accessible to the whole organisation
With the arrival of Copilot, this problem is amplified even further: even if a user has never searched for or discovered a public group, Copilot knows the user has access to that content and will use it in its answers.
The export script
The script presented in this article lets you extract a complete list of all public groups in the tenant, including detailed information such as:
- Group name and description
- Creation date
- Group owners (essential for identifying who is responsible)
- Group members
- URL of the associated SharePoint site (if any)
The full script is shown below. Save it as a .ps1 file (for example Export-PublicGroups.ps1) so you can run it.
Connect-MgGraph
Connect-ExchangeOnline
# Retrieve all public groups
$groups = Get-MgGroup -All | ?{$_.Visibility -eq "Public"}
$date = Get-Date -Format dd-MM-yyyy
$csvFilePath = "C:\temp\M365_Groups_Public_$date.csv"
$results = @()
foreach ($group in $groups) {
$groupInfo = [PSCustomObject]@{
'Group Name' = $group.DisplayName
'Group ID' = $group.Id
'MailNickName' = $group.mailnickname
'Description' = $group.Description
'CreatedDateTime' = $group.CreatedDateTime
'Visibility' = $group.Visibility
'Group Type' = $group.GroupTypes -join ', '
}
# Retrieve group members
$members = Get-MgGroupMember -GroupId $group.Id
$memberNames = @()
foreach ($member in $members) {
$memberDetails = Get-MgUser -UserId $member.Id
$memberNames += $memberDetails.DisplayName
}
$groupInfo | Add-Member -MemberType NoteProperty -Name 'Members' -Value ($memberNames -join ', ')
# Retrieve group owners
$owners = Get-MgGroupOwner -GroupId $group.Id
$ownerNames = @()
foreach ($owner in $owners) {
$ownerDetails = Get-MgUser -UserId $owner.Id
$ownerNames += $ownerDetails.DisplayName
}
$groupInfo | Add-Member -MemberType NoteProperty -Name 'Owners' -Value ($ownerNames -join ', ')
#Get information about the Microsoft 365 Group
$groupSPcheck = Get-UnifiedGroup -Identity $group.id
$SharePointSiteURL = @()
#Check if the group has a SharePoint site associated with it
if ($groupSPcheck.SharePointSiteUrl) {
$SharePointSiteURL = $($groupSPcheck.SharePointSiteUrl) -join "`n"
} else {
$SharePointSiteURL = "No SharePoint Site"
}
$groupInfo | Add-Member -MemberType NoteProperty -Name 'SharePointSiteURL' -Value ($SharePointSiteURL -join ', ')
$results += $groupInfo
}
# Export results to CSV
$results | Export-Csv -Path $csvFilePath -NoTypeInformation -Delimiter ";"
Write-Host "Export complete. Results saved to: $csvFilePath"
Prerequisites
Before running the script, make sure you meet the following prerequisites.
PowerShell modules installed
- Microsoft Graph PowerShell SDK
- Exchange Online Management Module
Required permissions
- Read access to groups and users in Azure AD
- Read access to Exchange Online configuration
How to use the script
1. Analysing the results
Open the generated CSV file in Excel. The file will contain one row per public group, with all the extracted information.
What to look at
Groups with no owner or with inactive owners
These groups should be considered “orphaned” and represent the highest risk. They need to be reassigned or deleted.
Groups with associated SharePoint sites
It’s advisable to manually review the content of these sites to assess how sensitive the exposed data is.
Recently created groups
These help you spot trends: an increase in the creation of public groups may point to a need for training or preventive measures.
Groups with few or no members
These may be obsolete groups and candidates for deletion.
2. Remediation actions
Once the problematic groups have been identified, you can take the following actions.
Contact the owners
Explain the risks of public groups and decide together whether to convert the group to private or whether public visibility is genuinely needed.
Convert groups to private
For groups that don’t need to be public, use the appropriate PowerShell commands to change their visibility.
Remove obsolete groups
For groups that are no longer used or have no active owners, consider deleting them after archiving any relevant content.
Document your decisions
Keep a record of the analysis carried out, the actions taken and the reasons justifying the continued existence of legitimate public groups.
3. Ongoing monitoring
The real value of this script comes from running it periodically.
Recommended good practices
Schedule weekly runs
Use Task Scheduler or Azure Automation to automate execution.
Compare results over time
Because the file name includes the date, you can easily compare exports and identify:
- new public groups created
- groups fixed after remediation actions
- trends of improvement or deterioration
Create governance metrics
Track over time:
- total number of public groups
- percentage of public groups out of the total
- average remediation time
- number of new public groups created per week or month
4. Integrating with your governance strategy
This script is only the first step.
For truly effective governance, it’s advisable to pair it with:
Prevention
As described in my article ➡️ Controlling M365 groups with Sensitivity Labels, using sensitivity labels lets you block the creation of new public groups, allowing only private ones.
Training
The data collected helps identify departments or users who frequently create public groups, so you can plan targeted training sessions.
Policy and attestation
Integrate this check with periodic attestation processes in which owners confirm that the group’s visibility is still appropriate.
Reporting
Produce monthly reports for management showing how the situation is evolving and how effective the actions taken have been.
Conclusions
Public M365 groups are one of those problems that stay invisible until they become critical. With the introduction of Copilot, what used to be a theoretical risk becomes a concrete and immediate one.
This script gives you the visibility you need to identify and fix the problem quickly. My advice is simple: run it today, analyse the results and start working with the owners to sort out any incorrect configurations.
Data governance is not a one-off activity but an ongoing process. This script is one of the essential tools to have in your toolbox to stay in control of your Microsoft 365 environment.