Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitIn large Microsoft Teams environments, administrators often archive inactive private teams to maintain workspace hygiene and reduce clutter. However, keeping track of which private teams are archived can quickly become a challenge. This Graph PowerShell script automates the process — fetching all private archived Teams, generating a detailed report, and emailing it directly to the administrator.
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "Group.Read.All","Team.ReadBasic.All","Mail.Send"
$FromUser = "admin@contoso.com"
$ToList = "it-ops@contoso.com;teams-admin@contoso.com"
$Subject = "Private Archived Teams Report"
$CsvOutDir = "$env:TEMP"
$teamsGroups = Get-MgGroup -All -Property Id,DisplayName,Visibility,CreatedDateTime,ResourceProvisioningOptions `
-Filter "resourceProvisioningOptions/Any(x:x eq 'Team')"
$privateArchived = @()
foreach ($g in $teamsGroups) {
if ($g.Visibility -ne "Private") { continue }
$team = Get-MgTeam -TeamId $g.Id -ErrorAction SilentlyContinue
if ($null -ne $team -and $team.IsArchived -eq $true) {
$ownerObjs = Get-MgGroupOwner -GroupId $g.Id -All -ErrorAction SilentlyContinue
$ownerNames = @()
$ownerMails = @()
foreach ($o in ($ownerObjs | Where-Object { $_ })) {
$name = $o.AdditionalProperties['displayName']
$mail = $o.AdditionalProperties['mail']
if ($name) { $ownerNames += $name }
if ($mail) { $ownerMails += $mail }
}
$privateArchived += [PSCustomObject]@{
TeamId = $g.Id
DisplayName = $g.DisplayName
Visibility = $g.Visibility
IsArchived = $team.IsArchived
CreatedDate = $g.CreatedDateTime
Owners = ($ownerNames -join "; ")
OwnerEmails = ($ownerMails -join "; ")
}
}
}
if (-not (Test-Path -Path $CsvOutDir)) { New-Item -ItemType Directory -Path $CsvOutDir | Out-Null }
$ts = Get-Date -Format "yyyyMMdd_HHmmss"
$csvPath = Join-Path $CsvOutDir ("Private_Archived_Teams_{0}.csv" -f $ts)
$privateArchived | Sort-Object DisplayName | Export-Csv -NoTypeInformation -Encoding UTF8 -Path $csvPath
$total = $privateArchived.Count
$summaryHtml = @"
<html>
<body style='font-family:Segoe UI,Arial,sans-serif'>
<h3>Private Archived Teams Report</h3>
<p>Total private archived teams: <b>$total</b></p>
<p>Attached CSV includes: TeamId, DisplayName, Visibility, IsArchived, CreatedDate, Owners, OwnerEmails.</p>
</body>
</html>
"@
$fileBytes = [System.IO.File]::ReadAllBytes($csvPath)
$base64Content = [System.Convert]::ToBase64String($fileBytes)
$attachment = @{
"@odata.type" = "#microsoft.graph.fileAttachment"
name = [System.IO.Path]::GetFileName($csvPath)
contentBytes = $base64Content
contentType = "text/csv"
}
$recipients = @()
$ToList.Split(@(';', ','), [System.StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object {
$addr = $_.Trim()
if ($addr) { $recipients += @{ emailAddress = @{ address = $addr } } }
}
$mail = @{
message = @{
subject = "$Subject"
body = @{ contentType = "HTML"; content = $summaryHtml }
toRecipients = $recipients
attachments = @($attachment)
}
saveToSentItems = $true
}
Send-MgUserMail -UserId $FromUser -BodyParameter $mail
Write-Host "Done. CSV saved at: $csvPath" -ForegroundColor Green
The script connects to Microsoft Graph using the required scopes — Group.Read.All, Team.ReadBasic.All, and Mail.Send — to access Teams data and send mail.
It retrieves all Microsoft 365 groups with Teams provisioned using the resourceProvisioningOptions filter.
The script checks for Teams with Visibility set to Private and IsArchived equal to True.
For each archived team, it fetches owner details, including their display names and email addresses.
A CSV file is created in the temp directory containing the details. The script then composes a brief HTML email with the CSV attached and sends it to the administrators listed in $ToList.
| Error | Cause | Solution |
|---|---|---|
| Access Denied | Insufficient Graph permissions | Use Connect-MgGraph -Scopes "Group.Read.All, Team.ReadBasic.All, Mail.Send" |
| Empty CSV File | No private archived teams found | Verify if any private teams have been archived in your tenant. |
| Request_UnsupportedQuery | Query syntax issue | Ensure the resourceProvisioningOptions filter syntax matches the Graph schema. |
| Send-MgUserMail fails | Sender lacks mailbox | Make sure $FromUser is a licensed mailbox-enabled account. |
| Owners missing | Owners not assigned | Encourage ownership assignment before archiving to avoid orphaned Teams. |
This Graph PowerShell script offers administrators a streamlined approach to monitor and report archived private Teams in Microsoft 365. Automating this task helps maintain governance, ensures compliance readiness, and simplifies lifecycle management. By emailing the report directly to stakeholders, it keeps IT teams informed and proactive about workspace hygiene.
© m365corner.com. All Rights Reserved. Design by HTML Codex