diff --git a/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/README.md b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/README.md new file mode 100644 index 000000000..142e2c59f --- /dev/null +++ b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/README.md @@ -0,0 +1,185 @@ +--- +plugin: add-to-gallery +--- + +# Get Azure AD (Entra ID) Guest Users with Elevated Permissions (M365) + + +## Summary + +This script audits and identifies Azure AD (Entra ID) guest (B2B) users who have been granted elevated permissions across Microsoft 365 services, including Entra ID directory roles, Microsoft Teams ownership, and SharePoint Online site collection administration. It generates a consolidated report to support security reviews, least-privilege enforcement, and external access governance. + +### Why It Matters + +- **Risk reduction:** Identifies external accounts with elevated access before they become a security issue. +- **Compliance support:** Provides evidence for audits and regulatory requirements. +- **Operational efficiency:** Consolidates checks across Entra ID, Teams, and SharePoint into one report. +- **Governance enforcement:** Helps enforce least‑privilege and external access policies consistently. + +# [PnP PowerShell](#tab/pnpps) + +```powershell + +# ------------------------------------------------------------ +# Configuration +# ------------------------------------------------------------ +$TenantAdminUrl = "https://contoso-admin.sharepoint.com" +$OutputPath = "C:\Temp\GuestUsersWithExcessivePermissions.csv" + +# ------------------------------------------------------------ +# Connect +# ------------------------------------------------------------ +if (-not $conn) { + $conn = Connect-PnPOnline -Url $TenantAdminUrl -Interactive -ReturnConnection +} + +# ------------------------------------------------------------ +# Helper: Get all guest users +# ------------------------------------------------------------ +function Get-GuestUsers { + param([Parameter(Mandatory)] $Connection) + + Invoke-PnPGraphMethod -Url "users?`$filter=userType eq 'Guest'&`$select=id,displayName,userPrincipalName,userType" ` + -Connection $Connection +} + +# ------------------------------------------------------------ +# Helper: Get Entra ID directory roles for guests +# ------------------------------------------------------------ +function Get-GuestDirectoryRoles { + param( + [Parameter(Mandatory)] $Connection, + [Parameter(Mandatory)] $Guests + ) + + $results = @() + + $roles = Invoke-PnPGraphMethod -Url "directoryRoles" -Connection $Connection + + foreach ($role in $roles.value) { + $members = Invoke-PnPGraphMethod -Url "directoryRoles/$($role.id)/members" -Connection $Connection + + foreach ($member in $members.value) { + if ($member.userType -eq "Guest") { + $results += [PSCustomObject]@{ + DisplayName = $member.displayName + UserPrincipalName = $member.userPrincipalName + PermissionType = "AAD Role" + AssignedRole = $role.displayName + Resource = "Tenant" + } + } + } + } + + return $results +} + +# ------------------------------------------------------------ +# Helper: Get Teams owned by guests +# ------------------------------------------------------------ +function Get-GuestTeamOwners { + param([Parameter(Mandatory)] $Connection) + + $results = @() + $groups = Invoke-PnPGraphMethod -Url "groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')" ` + -Connection $Connection + + foreach ($group in $groups.value) { + $owners = Invoke-PnPGraphMethod -Url "groups/$($group.id)/owners" -Connection $Connection + + foreach ($owner in $owners.value) { + if ($owner.userType -eq "Guest") { + $results += [PSCustomObject]@{ + DisplayName = $owner.displayName + UserPrincipalName = $owner.userPrincipalName + PermissionType = "Teams" + AssignedRole = "Owner" + Resource = $group.displayName + } + } + } + } + + return $results +} + +# ------------------------------------------------------------ +# Helper: Get SharePoint Site Collection Admins (guests only) +# ------------------------------------------------------------ +function Get-GuestSPOAdmins { + param([Parameter(Mandatory)] $Connection) + + $results = @() + $sites = Get-PnPTenantSite -Connection $Connection + + foreach ($site in $sites) { + try { + Connect-PnPOnline -Url $site.Url -Connection $Connection + $admins = Get-PnPSiteCollectionAdmin + + foreach ($admin in $admins) { + if ($admin.PrincipalType -eq "User" -and $admin.LoginName -like "*#EXT#*") { + $results += [PSCustomObject]@{ + DisplayName = $admin.Title + UserPrincipalName = $admin.Email + PermissionType = "SharePoint" + AssignedRole = "Site Collection Administrator" + Resource = $site.Url + } + } + } + } + catch { + Write-Warning "Failed to process site $($site.Url)" + } + } + + return $results +} + +# ------------------------------------------------------------ +# Main Execution +# ------------------------------------------------------------ +Write-Host "Retrieving guest users..." -ForegroundColor Cyan +$guestUsers = Get-GuestUsers -Connection $conn + +Write-Host "Checking Entra ID directory roles..." -ForegroundColor Cyan +$aadRoles = Get-GuestDirectoryRoles -Connection $conn -Guests $guestUsers.value + +Write-Host "Checking Microsoft Teams ownership..." -ForegroundColor Cyan +$teamsOwners = Get-GuestTeamOwners -Connection $conn + +Write-Host "Checking SharePoint site collection administrators..." -ForegroundColor Cyan +$spoAdmins = Get-GuestSPOAdmins -Connection $conn + +# ------------------------------------------------------------ +# Consolidate Results +# ------------------------------------------------------------ +$finalResults = @() +$finalResults += $aadRoles +$finalResults += $teamsOwners +$finalResults += $spoAdmins + +# ------------------------------------------------------------ +# Export +# ------------------------------------------------------------ +$finalResults | + Sort-Object UserPrincipalName, PermissionType | + Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 + +Write-Host "Completed. Results exported to $OutputPath" -ForegroundColor Green + +``` +[!INCLUDE [More about PnP PowerShell](../../docfx/includes/MORE-PNPPS.md)] +*** + + +## Contributors + +| Author(s) | +|-----------| +| Josiah Opiyo | + +[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)] + diff --git a/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/example.png b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/example.png new file mode 100644 index 000000000..9feb18d96 Binary files /dev/null and b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/example.png differ diff --git a/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/get-azure-ad-(entra-ID)-guest-users-with-elevated-permissions-(M365) README.mdREADME.md b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/get-azure-ad-(entra-ID)-guest-users-with-elevated-permissions-(M365) README.mdREADME.md new file mode 100644 index 000000000..f33520073 --- /dev/null +++ b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/get-azure-ad-(entra-ID)-guest-users-with-elevated-permissions-(M365) README.mdREADME.md @@ -0,0 +1,169 @@ +

Get Azure AD (Entra ID) Guest Users with Elevated Permissions (M365)

+

Summary

+

+ This script audits and identifies Azure AD (Entra ID) guest (B2B) users who have been granted elevated permissions across Microsoft 365 services, including Entra ID directory roles, Microsoft Teams ownership, and SharePoint Online site collection administration. It generates a consolidated report to support security reviews, least-privilege enforcement, and external access governance. +

+

Why It Matters

+

+

+

+

PnP Powershell

+## Script + +```powershell +# ------------------------------------------------------------ +# Configuration +# ------------------------------------------------------------ +$TenantAdminUrl = "https://contoso-admin.sharepoint.com" +$OutputPath = "C:\Temp\GuestUsersWithExcessivePermissions.csv" + +# ------------------------------------------------------------ +# Connect +# ------------------------------------------------------------ +if (-not $conn) { + $conn = Connect-PnPOnline -Url $TenantAdminUrl -Interactive -ReturnConnection +} + +# ------------------------------------------------------------ +# Helper: Get all guest users +# ------------------------------------------------------------ +function Get-GuestUsers { + param([Parameter(Mandatory)] $Connection) + + Invoke-PnPGraphMethod -Url "users?`$filter=userType eq 'Guest'&`$select=id,displayName,userPrincipalName,userType" ` + -Connection $Connection +} + +# ------------------------------------------------------------ +# Helper: Get Entra ID directory roles for guests +# ------------------------------------------------------------ +function Get-GuestDirectoryRoles { + param( + [Parameter(Mandatory)] $Connection, + [Parameter(Mandatory)] $Guests + ) + + $results = @() + + $roles = Invoke-PnPGraphMethod -Url "directoryRoles" -Connection $Connection + + foreach ($role in $roles.value) { + $members = Invoke-PnPGraphMethod -Url "directoryRoles/$($role.id)/members" -Connection $Connection + + foreach ($member in $members.value) { + if ($member.userType -eq "Guest") { + $results += [PSCustomObject]@{ + DisplayName = $member.displayName + UserPrincipalName = $member.userPrincipalName + PermissionType = "AAD Role" + AssignedRole = $role.displayName + Resource = "Tenant" + } + } + } + } + + return $results +} + +# ------------------------------------------------------------ +# Helper: Get Teams owned by guests +# ------------------------------------------------------------ +function Get-GuestTeamOwners { + param([Parameter(Mandatory)] $Connection) + + $results = @() + $groups = Invoke-PnPGraphMethod -Url "groups?`$filter=resourceProvisioningOptions/Any(x:x eq 'Team')" ` + -Connection $Connection + + foreach ($group in $groups.value) { + $owners = Invoke-PnPGraphMethod -Url "groups/$($group.id)/owners" -Connection $Connection + + foreach ($owner in $owners.value) { + if ($owner.userType -eq "Guest") { + $results += [PSCustomObject]@{ + DisplayName = $owner.displayName + UserPrincipalName = $owner.userPrincipalName + PermissionType = "Teams" + AssignedRole = "Owner" + Resource = $group.displayName + } + } + } + } + + return $results +} + +# ------------------------------------------------------------ +# Helper: Get SharePoint Site Collection Admins (guests only) +# ------------------------------------------------------------ +function Get-GuestSPOAdmins { + param([Parameter(Mandatory)] $Connection) + + $results = @() + $sites = Get-PnPTenantSite -Connection $Connection + + foreach ($site in $sites) { + try { + Connect-PnPOnline -Url $site.Url -Connection $Connection + $admins = Get-PnPSiteCollectionAdmin + + foreach ($admin in $admins) { + if ($admin.PrincipalType -eq "User" -and $admin.LoginName -like "*#EXT#*") { + $results += [PSCustomObject]@{ + DisplayName = $admin.Title + UserPrincipalName = $admin.Email + PermissionType = "SharePoint" + AssignedRole = "Site Collection Administrator" + Resource = $site.Url + } + } + } + } + catch { + Write-Warning "Failed to process site $($site.Url)" + } + } + + return $results +} + +# ------------------------------------------------------------ +# Main Execution +# ------------------------------------------------------------ +Write-Host "Retrieving guest users..." -ForegroundColor Cyan +$guestUsers = Get-GuestUsers -Connection $conn + +Write-Host "Checking Entra ID directory roles..." -ForegroundColor Cyan +$aadRoles = Get-GuestDirectoryRoles -Connection $conn -Guests $guestUsers.value + +Write-Host "Checking Microsoft Teams ownership..." -ForegroundColor Cyan +$teamsOwners = Get-GuestTeamOwners -Connection $conn + +Write-Host "Checking SharePoint site collection administrators..." -ForegroundColor Cyan +$spoAdmins = Get-GuestSPOAdmins -Connection $conn + +# ------------------------------------------------------------ +# Consolidate Results +# ------------------------------------------------------------ +$finalResults = @() +$finalResults += $aadRoles +$finalResults += $teamsOwners +$finalResults += $spoAdmins + +# ------------------------------------------------------------ +# Export +# ------------------------------------------------------------ +$finalResults | + Sort-Object UserPrincipalName, PermissionType | + Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 + +Write-Host "Completed. Results exported to $OutputPath" -ForegroundColor Green + + diff --git a/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/preview.png b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/preview.png new file mode 100644 index 000000000..72a9255df Binary files /dev/null and b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/preview.png differ diff --git a/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/sample.json b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/sample.json new file mode 100644 index 000000000..ce9c1f3bb --- /dev/null +++ b/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/sample.json @@ -0,0 +1,53 @@ +[ + { + "name": "spo-get-entra-id-guest-users-with-elevated-permissions-m365", + "source": "pnp", + "title": "Get Azure AD (Entra ID) Guest Users with Elevated Permissions (M365)", + "shortDescription": "Identify guest users within a Microsoft 365 tenant who have elevated permissions across various services such as Entra ID, Microsoft Teams, and SharePoint Online.", + "url": "https://pnp.github.io/script-samples/spo-get-entra-id-guest-users-with-elevated-permissions-m365/README.html", + "longDescription": [ + "" + ], + "creationDateTime": "2025-12-18", + "updateDateTime": "2025-12-18", + "products": [ + "SharePoint", + "Entra ID" + ], + "metadata": [ + { + "key": "PNP-POWERSHELL", + "value": "1.11.0" + } + ], + "categories": [ + "Report" + ], + "tags": [ + "" + ], + "thumbnails": [ + { + "type": "image", + "order": 100, + "url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/spo-get-entra-id-guest-users-with-elevated-permissions-m365/assets/preview.png", + "alt": "Preview of the sample Get Azure AD (Entra ID) Guest Users with Elevated Permissions (M365)" + } + ], + "authors": [ + { + "gitHubAccount": "ojopiyo", + "company": "", + "pictureUrl": "https://github.com/ojopiyo.png", + "name": "Josiah Opiyo" + } + ], + "references": [ + { + "name": "Want to learn more about PnP PowerShell and the cmdlets", + "description": "Check out the PnP PowerShell site to get started and for the reference to the cmdlets.", + "url": "https://aka.ms/pnp/powershell" + } + ] + } +]