đź”§ New: User Management Graph PowerShell Toolkit

Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.

🚀 Launch Toolkit

Email Empty Teams List to Microsoft 365 Administrator Using Graph PowerShell

It’s not unusual for Microsoft Teams environments to accumulate empty Teams over time. Maybe a Team was created for a project that never kicked off, or its members shifted elsewhere. These empty Teams clutter your environment, confuse administrators, and increase the attack surface if left unmanaged.

This Graph PowerShell script addresses that by scanning your tenant, finding Teams with no members, and emailing the results (as a CSV file) to the administrator. The report also includes useful details like TeamId and CreatedDate for easier governance decisions.


i) Script

# ===== Empty Microsoft Teams -> CSV -> Email to Admin =====
# Requires: Microsoft.Graph module
# Scopes: Team.ReadBasic.All, Group.Read.All, Mail.Send
                                
# --- Email variables ---
$FromUser  = "admin@contoso.com"       # Sender (must have mailbox)
$To        = "it-ops@contoso.com"      # Recipient
$Subject   = "Empty Microsoft Teams report"
$CsvOutDir = "$env:TEMP"
                                
# --- Connect to Microsoft Graph with the necessary scopes ---
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "Team.ReadBasic.All","Group.Read.All","Mail.Send"
                                
# --- Fetch all teams ---
$teams = Get-MgGroup -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" `
         -Property Id, DisplayName, Description, Visibility, CreatedDateTime -All
                                
# --- Prepare an array to store the data of empty teams ---
$emptyTeamsData = @()
                                
foreach ($team in $teams) {
    # Get the member count for each team
    $memberCount = (Get-MgGroupMember -GroupId $team.Id -All).Count
                                
    # If the team is empty (no members), add its information to the array
    if ($memberCount -eq 0) {
       # Determine the team type based on visibility
       $teamType = if ($team.Visibility -eq "Public") { "Public" } else { "Private" }
                                
       # Add the team's information to the array
       $emptyTeamsData += [pscustomobject]@{
             TeamId      = $team.Id
             TeamName    = $team.DisplayName
             Description = $team.Description
             TeamType    = $teamType
             CreatedDate = $team.CreatedDateTime
        }
    }
}
                                
# --- Output the data in a table format (console) ---
$emptyTeamsData | Format-Table -AutoSize
                                
# --- Export to CSV ---
if (-not (Test-Path -Path $CsvOutDir)) { New-Item -ItemType Directory -Path $CsvOutDir | Out-Null }
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$csvPath = Join-Path $CsvOutDir ("Empty_Teams_{0}.csv" -f $ts)
$emptyTeamsData | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
                                
# --- Prepare HTML Body ---
$summaryHtml = @"
<html>
  <body style='font-family:Segoe UI,Arial,sans-serif'>
    <h3>Empty Microsoft Teams Report</h3>
    <p>Total empty Teams: <b>$($emptyTeamsData.Count)</b></p>
    <p>The full list (with TeamId and CreatedDate) is attached as a CSV.</p>
  </body>
</html>
                                
"@

# --- Prepare Attachment ---
$fileBytes     = [System.IO.File]::ReadAllBytes($csvPath)
$base64Content = [System.Convert]::ToBase64String($fileBytes)
$csvFileName   = [System.IO.Path]::GetFileName($csvPath)
$attachment = @{
    "@odata.type" = "#microsoft.graph.fileAttachment"
    name          = $csvFileName
    contentBytes  = $base64Content
    contentType   = "text/csv"
}

# --- Prepare and Send Email ---
$mail = @{
  message = @{
    subject = "${Subject}"
    body    = @{
      contentType = "HTML"
      content     = $summaryHtml
    }
    toRecipients = @(@{ emailAddress = @{ address = $To } })
    attachments  = @($attachment)
  }
  saveToSentItems = $true
}

Send-MgUserMail -UserId $FromUser -BodyParameter $mail

Write-Host "Done. CSV saved at: $csvPath" -ForegroundColor Green

                            

ii) How the Script Works

  1. Connect to Graph
  2. Uses the Microsoft Graph PowerShell SDK with the scopes Team.ReadBasic.All, Group.Read.All, and Mail.Send.

  3. Retrieve Teams-enabled groups
  4. Filters groups that have resourceProvisioningOptions containing "Team", ensuring only Teams are included.

  5. Check membership
  6. Iterates through each Team and counts members using Get-MgGroupMember. If count = 0, the Team is flagged as empty.

  7. Collect details
  8. Captures TeamId, TeamName, Description, Visibility (converted to Public/Private), and CreatedDate.

  9. Export and email
  10. The results are exported to a timestamped CSV and emailed to the administrator with a quick HTML summary.


iii) Further Enhancements

  • Owner Details: Include Team owner information to quickly assign accountability.
  • Member Count Column: Export all Teams with member counts, not just empty ones.
  • Lifecycle Checks: Flag empty Teams older than 6 months for deletion/archival.
  • Scheduled Runs: Automate weekly/monthly reporting via Task Scheduler or Azure Automation.
  • Send to Governance Team: Mail results to a distribution list or compliance mailbox.

iv) Use Cases

  • Governance Hygiene: Quickly identify abandoned Teams that should be cleaned up.
  • Security Oversight: Ensure no empty Teams exist that could later be misused.
  • Tenant Management: Provide IT admins with visibility into unused Teams.
  • Compliance Reporting: Generate auditable reports of Teams without members.

v) Possible Errors & Solutions

Error Cause Solution
Authorization_RequestDenied Scopes missing or consent not granted Connect with Team.ReadBasic.All, Group.Read.All, and Mail.Send; ensure admin consent.
Get-MgGroup not recognized Microsoft Graph module missing Install via Install-Module Microsoft.Graph -Scope CurrentUser.
Empty results though Teams exist No empty Teams in tenant Confirm Teams exist; remove membership filter to validate.
Email not sent $FromUser not mailbox-enabled Use a licensed mailbox-enabled account for sending.
CSV file missing Invalid path for $CsvOutDir Change $CsvOutDir to a valid directory path.

vi) Conclusion

Public Teams are fantastic for open collaboration—but they need oversight. This script gives you a dependable single-source report of every public Team plus its owners, delivered as a CSV to your inbox. Schedule it, share it with governance, and expand it over time (owner counts, settings, activity) to keep your Microsoft 365 environment tidy and safe.


Graph PowerShell Explorer Widget

20 Graph PowerShell cmdlets with easily accessible "working" examples.


Permission Required

Example:


                


                


                

© m365corner.com. All Rights Reserved. Design by HTML Codex