🔧 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 Recently Deleted Teams Report Using Graph PowerShell

When a Team is deleted, it isn’t gone forever immediately. Microsoft 365 retains deleted Teams (as deleted groups) for 30 days, giving admins the ability to restore them if needed. But in a busy environment, multiple Teams might be deleted in a short time — whether by mistake, by intentional cleanup, or by user request. Without a clear view of what’s been deleted recently, administrators may struggle to track changes, validate compliance, or prepare for audits.

This Graph PowerShell script solves that problem. It scans your tenant for Teams deleted within the last 30 days, compiles vital details such as TeamId, DisplayName, Description, Visibility, CreatedDate, and DeletedDate, exports the report into a CSV, and then emails the results directly to the administrator.


i) Script

# ===== Recently Deleted Microsoft Teams (Last 30 Days) -> CSV -> Email =====
# Requires: Microsoft.Graph module
# Scopes: Directory.Read.All, Group.Read.All, Mail.Send

# --- Email variables ---
$FromUser  = "admin@contoso.com"       # Sender (must have mailbox)
$To        = "it-ops@contoso.com"      # Recipient
$Subject   = "Recently Deleted Microsoft Teams (Last 30 Days)"
$CsvOutDir = "$env:TEMP"

# --- Connect to Microsoft Graph ---
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "Directory.Read.All","Group.Read.All","Mail.Send"

# --- Time window (last 30 days) ---
$since = (Get-Date).AddDays(-30)

# --- Get deleted groups directly (not other objects) ---
$uri = "https://graph.microsoft.com/v1.0/directory/deletedItems/microsoft.graph.group"
$deletedGroups = @()

do {
    $response = Invoke-MgGraphRequest -Method GET -Uri $uri
    $deletedGroups += $response.value
    $uri = $response.'@odata.nextLink'
} while ($null -ne $uri)

    # --- Filter Teams and build report rows ---
    $rows = @()

    foreach ($grp in $deletedGroups) {
    $rpo = $grp.resourceProvisioningOptions
    $isTeam = $false
    if ($rpo) { $isTeam = ($rpo -contains "Team") -or ($rpo -match "Team") }

    if ($isTeam -and $grp.deletedDateTime) {
    $deletedDate = [datetime]$grp.deletedDateTime
    if ($deletedDate -ge $since) {
        $rows += [PSCustomObject]@{
            TeamId       = $grp.id
            TeamName     = $grp.displayName
            Description  = $grp.description
            Visibility   = $grp.visibility
            CreatedDate  = $grp.createdDateTime
            DeletedDate  = $deletedDate
        }
    }
    }
}

# --- 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 ("Recently_Deleted_Teams_{0}.csv" -f $ts)
$rows | Sort-Object DeletedDate -Descending | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

# --- Prepare HTML Body ---
$totalDeletedGroups = $deletedGroups.Count
$totalTeamsInWindow = $rows.Count
$summaryHtml = @"
                             
<html>
  <body style='font-family:Segoe UI,Arial,sans-serif'>
    <h3>Recently Deleted Microsoft Teams Report (Last 30 Days)</h3>
    <p>Total deleted groups scanned: <b>$totalDeletedGroups</b></p>
    <p>Teams deleted in last 30 days: <b>$totalTeamsInWindow</b></p>
    <p>The full list (TeamId, Name, Visibility, CreatedDate, DeletedDate) 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. Connects to Microsoft Graph
  2. Connects using the Graph PowerShell SDK with three required scopes:

    • Directory.Read.All – to read directory objects.
    • Group.Read.All – to fetch Microsoft 365 group details.
    • Mail.Send – to send the report by email.
  3. Gets deleted groups
  4. Instead of using Get-MgDirectoryDeletedItem (which doesn’t support -All), the script calls the REST endpoint directory/deletedItems/microsoft.graph.group via Invoke-MgGraphRequest. This ensures it retrieves deleted groups only, not deleted users or devices.

  5. Filters Teams
  6. Deleted groups with resourceProvisioningOptions containing "Team" are identified as Teams.

  7. Applies a 30-day window
  8. Only Teams whose deletedDateTime falls within the last 30 days are included in the report.

  9. Builds and sends the report
  10. Exports the details to CSV (TeamId, TeamName, Description, Visibility, CreatedDate, DeletedDate) and emails the file to the admin with a quick HTML summary.


iii) Further Enhancements

  • Include Deleted By: Combine with audit logs to find out who deleted the Team.
  • Automated Scheduling: Run weekly via Task Scheduler, Azure Automation, or Intune.
  • Owner Notification: Email former Team owners whenever their Team is deleted.
  • Lifecycle Reporting: Merge this with archived Teams data for a fuller governance picture.
  • Retention Insights: Highlight Teams nearing permanent purge after 30 days.

iv) Use Cases

  • Governance & Compliance: Track all recently deleted Teams for audit readiness.
  • Change Control: Validate which Teams were deleted in the last 30 days.
  • Security Reviews: Confirm that deleted Teams align with organizational policy.
  • Tenant Cleanup: Ensure deleted Teams aren’t mistakenly left out of oversight reports.

v) Possible Errors & Solutions

Error Cause Solution
Authorization_RequestDenied Missing Graph permissions or admin consent Reconnect with Directory.Read.All, Group.Read.All, Mail.Send and grant admin consent.
Invoke-MgGraphRequest not recognized Older Microsoft.Graph module version Update module with Update-Module Microsoft.Graph.
CSV file empty No Teams deleted in the last 30 days Verify deletions exist via Teams Admin Center.
Email not sent $FromUser not mailbox-enabled Use a licensed mailbox-enabled account as sender.
Script misses results Deleted items exceed 100 per page Handled by pagination loop using @odata.nextLink.

vi) Conclusion

Deleted Teams don’t vanish instantly — they linger for 30 days, giving admins the chance to restore them. But keeping track of these recently deleted Teams is crucial for compliance, security, and lifecycle management. This script automates the process: fetching all Teams deleted in the last 30 days, compiling their key details, and emailing the report. By scheduling this script, administrators gain continuous visibility into deletions, ensuring a healthier and more accountable Microsoft 365 environment.


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