Simplify user tasks like bulk creation, updates, password resets, deletions, license checks & more — all from one place.
🚀 Launch ToolkitAs Microsoft Teams becomes the backbone of collaboration, knowing who owns which Team is critical. Owners hold the keys—they manage membership, settings, and governance. Yet, in many organizations, the ownership landscape drifts out of sync with reality: Teams without owners, Teams with too many owners, or Teams owned by accounts that no longer exist. This makes governance difficult and increases security risks.
To regain visibility, administrators need a simple way to list all Teams and their owners in one consolidated report. The following Graph PowerShell script does exactly that—it fetches all Teams, gathers their owners, and emails the results as a CSV to the administrator.
# ===== Teams Owners -> CSV -> Email to Admin =====
# Requires: Microsoft.Graph module
# Scopes: Group.Read.All, User.Read.All, Mail.Send
# --- Email variables ---
$FromUser = "admin@contoso.com" # Sender (must have mailbox)
$To = "it-ops@contoso.com" # Recipient
$Subject = "Microsoft Teams Owners report"
$CsvOutDir = "$env:TEMP"
# --- Connect to Microsoft Graph ---
Import-Module Microsoft.Graph -ErrorAction Stop
Connect-MgGraph -Scopes "Group.Read.All","User.Read.All","Mail.Send"
# --- Get Teams-enabled groups ---
$teams = Get-MgGroup -All -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" `
-Property "id,displayName,description,visibility,createdDateTime,mailNickname"
# --- Build owner rows (one row per owner per Team) ---
$rows = @()
foreach ($t in $teams) {
$ownerObjs = Get-MgGroupOwner -GroupId $t.Id -All -ErrorAction SilentlyContinue
foreach ($o in $ownerObjs) {
$ownerDisplayName = $null
$ownerUpn = $null
try {
# Resolve to user for clean DisplayName/UPN
$u = Get-MgUser -UserId $o.Id -Property DisplayName,UserPrincipalName -ErrorAction Stop
$ownerDisplayName = $u.DisplayName
$ownerUpn = $u.UserPrincipalName
} catch {
# Fallback for non-user owners or missing fields
$ownerDisplayName = $o.AdditionalProperties['displayName']
$ownerUpn = $o.AdditionalProperties['userPrincipalName']
}
$rows += [PSCustomObject]@{
TeamId = $t.Id
TeamName = $t.DisplayName
TeamVisibility = $t.Visibility
OwnerDisplayName= $ownerDisplayName
OwnerUPN = $ownerUpn
}
}
}
# --- 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 ("Teams_Owners_{0}.csv" -f $ts)
$rows | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8
# --- Prepare HTML Body ---
$totalTeams = $teams.Count
$totalOwners = $rows.Count
$summaryHtml = @"
<html>
<body style='font-family:Segoe UI,Arial,sans-serif'>
<h3>Microsoft Teams Owners Report</h3>
<p>Total Teams scanned: <b>$totalTeams</b></p>
<p>Total owner entries: <b>$totalOwners</b></p>
<p>The full owner list is attached as a CSV (one row per owner per Team)</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
Uses the Graph PowerShell SDK with scopes:
Filters groups that have resourceProvisioningOptions set to "Team".
For each Team, Get-MgGroupOwner retrieves owners. Owners are resolved to users with clean DisplayName and UserPrincipalName.
The script generates one row per Team-owner pair (so Teams with multiple owners have multiple rows). Data includes TeamId, TeamName, Visibility, OwnerDisplayName, and OwnerUPN.
The CSV is attached to an HTML email and sent to the administrator, summarizing the number of Teams scanned and owner entries found.
Error | Cause | Solution |
---|---|---|
Authorization_RequestDenied | Missing required Graph scopes or admin consent | Connect with Group.Read.All, User.Read.All, Mail.Send and ensure admin consent is granted. |
Get-MgGroup not recognized | Graph module not installed | Run Install-Module Microsoft.Graph -Scope CurrentUser. |
Missing owners in CSV | Some owners are service principals or external objects | Script includes a fallback to capture displayName and userPrincipalName when available. |
Email not sent | $FromUser not mailbox-enabled | Use a licensed mailbox-enabled account. |
CSV is empty | No Teams found or insufficient Graph permissions | Verify Teams exist and permissions are correctly granted. |
Ownership visibility is key to Teams governance and security. This Graph PowerShell script provides administrators with a straightforward, automated way to inventory all Teams and their owners. The emailed CSV makes it easy to track ownership patterns, flag issues, and support audits. By scheduling and enhancing the script, IT teams can strengthen oversight and keep Microsoft Teams clean, compliant, and well-governed.
© m365corner.com. All Rights Reserved. Design by HTML Codex