Report Microsoft Teams with Guest Users Using Graph PowerShell

Microsoft Teams makes collaboration with external users easy—but that convenience also introduces governance and security challenges. Guest users can access conversations, files, and shared resources, and over time administrators may lose visibility into which Teams include guests and whether those Teams are properly owned.

A common gap in many tenants is the lack of a consolidated view answering:

  • Which Teams currently contain guest users?
  • How many guests are present in each Team?
  • Are those Teams actively owned and governed?

This Graph PowerShell script focuses exclusively on Teams-backed Microsoft 365 groups, identifies Teams that contain guest users, generates a governance-ready report, and automatically emails it to administrators—without requiring any SMTP configuration.

🚀 Community Edition Released!

Try the M365Corner Microsoft 365 Reporting Tool — your DIY pack with 20+ out-of-the-box M365 reports for Users, Groups, and Teams.


i) The Script

$SenderUPN = (Get-MgContext).Account

$Recipients = @(
    "admin@yourtenant.onmicrosoft.com",
    "securityteam@yourtenant.onmicrosoft.com"
)

Connect-MgGraph -Scopes "Group.Read.All","User.Read.All","Directory.Read.All","Mail.Send"

$TeamsEnabledGroups = Get-MgGroup -All `
    -Property Id,DisplayName,Mail,GroupTypes,ResourceProvisioningOptions,CreatedDateTime |
Where-Object {
    $_.GroupTypes -contains "Unified" -and
    $_.ResourceProvisioningOptions -contains "Team"
}

$Report = @()

foreach ($group in $TeamsEnabledGroups) {

    try {
        $Members = Get-MgGroupMember -GroupId $group.Id -All
        $Owners  = Get-MgGroupOwner -GroupId $group.Id -All
    } catch {
        continue
    }

    $GuestCount = 0

    foreach ($member in $Members) {

        if ($member.AdditionalProperties.'@odata.type' -ne "#microsoft.graph.user") {
            continue
        }

        try {
            $User = Get-MgUser -UserId $member.Id -Property UserType
        } catch {
            continue
        }

        if ($User.UserType -eq "Guest") {
            $GuestCount++
        }
    }

    if ($GuestCount -gt 0) {
        $Report += [PSCustomObject]@{
            "Team Name"    = $group.DisplayName
            "Team Email"   = $group.Mail
            "Team Id"      = $group.Id
            "Created Date" = $group.CreatedDateTime
            "Guest Count"  = $GuestCount
            "Owner Count"  = $Owners.Count
            "Has Owners"   = if ($Owners.Count -gt 0) { "Yes" } else { "No" }
        }
    }
}

$ReportPath = "$env:TEMP\Teams_With_Guest_Users_Report.csv"

if ($Report.Count -gt 0) {
    $Report | Sort-Object "Guest Count" -Descending |
        Export-Csv -Path $ReportPath -NoTypeInformation -Encoding utf8
} else {
    "No Teams (Teams-enabled groups) with guest users were found." |
        Set-Content -Path $ReportPath -Encoding utf8
}

$Bytes = [System.IO.File]::ReadAllBytes($ReportPath)
$Utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText(
    $ReportPath,
    [System.Text.Encoding]::UTF8.GetString($Bytes),
    $Utf8Bom
)

$Count = $Report.Count
$Subject = "Teams with Guest Users — $(Get-Date -Format 'yyyy-MM-dd')"

$Body = @"
Hello Team,<br><br>
Attached is the <b>Teams with Guest Users</b> report.<br>
This report includes only <b>Teams-enabled Microsoft 365 Groups</b> that contain guest users, along with guest and owner counts.<br><br>
Total Teams with guests found: <b>$Count</b><br><br>
Regards,<br>
Graph PowerShell Automation
"@

$AttachmentContent = [System.Convert]::ToBase64String(
    [System.IO.File]::ReadAllBytes($ReportPath)
)

$Attachments = @(
    @{
        "@odata.type" = "#microsoft.graph.fileAttachment"
        Name          = "Teams_With_Guest_Users_Report.csv"
        ContentBytes  = $AttachmentContent
    }
)

$ToRecipients = $Recipients | ForEach-Object {
    @{ EmailAddress = @{ Address = $_ } }
}

$Message = @{
    Message = @{
        Subject = $Subject
        Body    = @{
            ContentType = "HTML"
            Content     = $Body
        }
        ToRecipients = $ToRecipients
        Attachments  = $Attachments
    }
    SaveToSentItems = "true"
}

Send-MgUserMail -UserId $SenderUPN -BodyParameter $Message
                                

ii) How the Script Works

  1. Uses the signed-in admin as the mail sender
    The script dynamically uses the current Graph session account, ensuring delegated Mail.Send works reliably.
  2. Targets only Teams-backed Microsoft 365 groups
    Teams are identified by filtering groups that:
    • contain "Unified" in GroupTypes
    • contain "Team" in ResourceProvisioningOptions
  3. Enumerates group members and owners
    For each group:
    • Members are retrieved using Get-MgGroupMember
    • Owners are retrieved using Get-MgGroupOwner
  4. Enumerates members and owners per Team
    Each Team’s members and owners are retrieved using Graph group endpoints.
  5. Accurately detects guest users
    Since group membership metadata does not reliably expose userType, the script resolves each user using Get-MgUser and checks the UserType property directly.
  6. Builds a governance-focused report
    Only Teams containing guest users are included, along with:
    • guest count
    • owner count
    • ownership status
  7. Exports an Excel-safe CSV
    UTF-8 BOM encoding ensures column headers are visible even if no Teams match the criteria.
  8. Emails the report using Graph PowerShell
    The CSV is attached and sent via Send-MgUserMail, eliminating the need for SMTP configuration.

iii) Further Enhancements

  • Highlight high-risk Teams
    • Teams with guest users and zero owners
    • Teams with unusually high guest counts
  • Include guest details
    Add guest UPNs or external domains for deeper auditing.
  • Combine with Teams external access settings
    Correlate guest presence with tenant or Team-level policies.
  • Notify Team owners automatically
    Send review requests to owners for Teams with guests.
  • Schedule periodic audits
    Run monthly or quarterly to maintain external collaboration hygiene.

iv) Possible Errors & Solutions

Error Cause Solution
Report shows no Teams despite known guest access Guest detection using group member metadata alone Script correctly resolves full user objects to read UserType
403 Access Denied when sending mail Sender does not match signed-in account or missing Mail.Send Use (Get-MgContext).Account and grant delegated consent
Performance impact in large tenants Per-user lookups for accurate guest detection Run off-hours or schedule periodically for governance checks
CSV opens without headers in Excel Missing UTF-8 BOM Explicit BOM handling already implemented in the script


v) Conclusion

Teams with guest users represent a critical intersection of collaboration and security. Without visibility, external access can persist unchecked and unmanaged. This Graph PowerShell solution provides administrators with a clear, automated way to identify Teams containing guest users, verify ownership accountability, and maintain control over external collaboration.

By combining accurate guest detection with automated reporting and SMTP-less email delivery, this script fits seamlessly into modern Microsoft 365 governance workflows.


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