Report Microsoft 365 Groups with Guest Users Using Graph PowerShell

Guest access in Microsoft 365 enables seamless collaboration with external partners, vendors, and customers. However, from a governance and security standpoint, groups that contain guest users require closer scrutiny—especially when those groups are broadly used for conversations, file sharing, and Teams collaboration.

A common challenge for administrators is answering questions like:

  • Which Microsoft 365 groups currently contain guest users?
  • How many guests exist in each group?
  • Do these externally shared groups have owners accountable for access?

This Graph PowerShell script helps address these concerns by identifying Microsoft 365 (Unified) groups that contain guest users, generating a structured report, and automatically emailing it to administrators—without relying on 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","Directory.Read.All","Mail.Send"

$Groups = Get-MgGroup -All -Property Id,DisplayName,GroupTypes,Mail,CreatedDateTime |
Where-Object { $_.GroupTypes -contains "Unified" }

$Report = @()

foreach ($group in $Groups) {

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

    $GuestMembers = $Members | Where-Object {
        $_.AdditionalProperties.'@odata.type' -eq "#microsoft.graph.user" -and
        $_.AdditionalProperties.userType -eq "Guest"
    }

    if ($GuestMembers.Count -gt 0) {

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

$ReportPath = "$env:TEMP\Groups_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 Microsoft 365 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 = "Microsoft 365 Groups with Guest Users — $(Get-Date -Format 'yyyy-MM-dd')"

$Body = @"
Hello Team,<br><br>
Attached is the <b>Microsoft 365 Groups with Guest Users</b> report.<br>
This report highlights groups that contain external users along with guest and owner counts.<br><br>
Total groups 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          = "Groups_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 sender
    The script dynamically sets the sender using the current Graph session, ensuring delegated Mail.Send works without access issues.
  2. Retrieves Microsoft 365 (Unified) groups only
    Groups are filtered by the presence of "Unified" in the GroupTypes property, excluding security and distribution groups.
  3. Enumerates group members and owners
    For each group:
    • Members are retrieved using Get-MgGroupMember
    • Owners are retrieved using Get-MgGroupOwner
  4. Identifies guest users
    Guest users are detected based on:
    • @odata.type = microsoft.graph.user
    • userType = Guest
  5. Builds a governance-focused report
    Only groups containing guest users are included, along with:
    • guest count
    • owner count
    • ownership status
  6. Exports an Excel-safe CSV
    The script ensures UTF-8 BOM encoding so column headers remain visible in Excel even when no data exists.
  7. Emails the report using Graph PowerShell
    The CSV is attached and mailed via Send-MgUserMail, eliminating the need for SMTP configuration.

iii) Further Enhancements

  • Highlight high-risk groups
    • Groups with guest users and zero owners
    • Groups with unusually high guest counts
  • Include guest user details
    Add guest UPNs or domains for deeper auditing.
  • Filter by external domains
    Flag groups containing guests from specific domains.
  • Automated review notifications
    Email group owners requesting confirmation of continued guest access.
  • Teams-specific reporting
    Extend the logic to report Teams with external users enabled.

iv) Possible Errors & Solutions

Error Cause Solution
403 Access Denied while sending email Sender UPN does not match the signed-in account or missing Mail.Send delegated permission Ensure the sender is set using (Get-MgContext).Account and admin consent is granted
Empty CSV file No Microsoft 365 groups currently contain guest users Expected behavior; the script still generates a readable CSV
Group member enumeration failures Temporary Graph throttling or permission gaps Script safely skips affected groups and continues processing
Excel shows corrupted CSV Missing UTF-8 BOM Already handled by explicitly rewriting the file with BOM


v) Conclusion

External collaboration is powerful—but without visibility, it can quickly become a governance blind spot. This Graph PowerShell script provides administrators with a clear, automated way to identify Microsoft 365 groups that include guest users, assess ownership accountability, and maintain oversight of external access.

By combining accurate group membership analysis with SMTP-less email delivery through Microsoft Graph, this solution fits seamlessly into modern Microsoft 365 administration and security review 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