Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

How to Get SharePoint Site Members Using PowerShell (PnP and SPO)

A manager asks who can open the SPGuides site, and the Site permissions panel shows three group names instead of people. Click into Members and you find a Microsoft 365 group, not a list of names.

In this tutorial, we’ll build the get SharePoint site members PowerShell scripts our team runs before a permissions review. Every PnP command below ran read-only on our SPGuides site; real names are covered in the screenshots.

Quick answer: Connect with Connect-PnPOnline, then run Get-PnPGroupMember -Group (Get-PnPGroup -AssociatedMemberGroup), or the Owner/Visitor switches. On a group-connected site those groups only hold the Microsoft 365 group, so run Get-PnPMicrosoft365GroupMember for the people. Get-PnPUser lists the User Information List, including people who lost access.

What You Need Before You Start

  • PnP PowerShell 3.x in PowerShell 7.4 or later; see our guide to install PnP PowerShell.
  • Your own Entra ID app registration, because Connect-PnPOnline now needs a -ClientId. Give it Graph Group.Read.All for the Microsoft 365 group cmdlets.
  • Site owner or site collection admin rights on the site.
Connect-PnPOnline -Url "https://spguides.sharepoint.com/sites/SPGuides" `
    -ClientId "00000000-0000-0000-0000-000000000000" -Interactive   # your Entra ID app's client ID

Step 1: List the SharePoint Groups on the Site

Get-PnPGroup lists the groups. We hide the system SharingLinks and Limited Access groups, then use the three switches that return the default groups the SharePoint site owner sees under Site permissions.

Get-PnPGroup |
    Where-Object { $_.Title -notlike "SharingLinks*" -and $_.Title -notlike "Limited Access*" } |
    Select-Object Id, Title, OwnerTitle | Format-Table -AutoSize

"Owners group:   " + (Get-PnPGroup -AssociatedOwnerGroup).Title
"Members group:  " + (Get-PnPGroup -AssociatedMemberGroup).Title
"Visitors group: " + (Get-PnPGroup -AssociatedVisitorGroup).Title
Get-PnPGroup listing SharePoint site groups and the associated owners, members and visitors groups

Our site returned 11 groups, 7 after the filter.

Step 2: Get Owners, Members and Visitors with Get-PnPGroupMember

$groups = [ordered]@{
    Owners   = Get-PnPGroup -AssociatedOwnerGroup
    Members  = Get-PnPGroup -AssociatedMemberGroup
    Visitors = Get-PnPGroup -AssociatedVisitorGroup
}
foreach ($role in $groups.Keys) {
    "--- $role ($($groups[$role].Title))"
    $people = Get-PnPGroupMember -Group $groups[$role]
    if (-not $people) { "    (no members)"; continue }
    $people | Select-Object Title, PrincipalType, LoginName | Format-Table -AutoSize | Out-String -Width 130
}
Get-PnPGroupMember output for the SharePoint Owners, Members and Visitors groups of a group-connected site

On our group-connected site, Members held one SecurityGroup entry whose login starts with c:0o.c|federateddirectoryclaimprovider| plus the Microsoft 365 group ID. Owners held the same claim with an _o suffix, plus System Account, and Visitors was empty. To check one person, add -User "emily.carter@spguides.onmicrosoft.com".

Step 3: Expand Microsoft 365 Group Members

On a group-connected site, the group’s owners are site owners and its members are site members. For more ways to read them, see how to get Microsoft 365 group members using PowerShell.

$site = Get-PnPSite -Includes GroupId
"Microsoft 365 group ID: $($site.GroupId)"

Get-PnPMicrosoft365GroupOwner -Identity $site.GroupId |
    Select-Object DisplayName, UserPrincipalName | Format-Table -AutoSize
Get-PnPMicrosoft365GroupMember -Identity $site.GroupId |
    Select-Object DisplayName, UserPrincipalName, UserType | Format-Table -AutoSize
Get-PnPMicrosoft365GroupOwner and Get-PnPMicrosoft365GroupMember listing the real owners and members of a SharePoint site

Our group had 4 owners and 5 members. Pull both lists; owners aren’t always members. Add Where-Object UserType -eq "Guest" to find external people.

Step 4: Compare Get-PnPUser with Real Site Access

Get-PnPUser doesn’t return site members. It reads the User Information List, which keeps everyone ever added, even after their access is removed. -WithRightsAssigned keeps only entries that still have rights:

$everyone   = Get-PnPUser                       # every entry in the User Information List
$withAccess = Get-PnPUser -WithRightsAssigned   # only entries that have access right now
"User Information List entries: $($everyone.Count)"
"Entries with access right now: $($withAccess.Count)"
$withAccess | Select-Object Id, Title, PrincipalType | Format-Table -AutoSize
Get-PnPUser compared with Get-PnPUser -WithRightsAssigned on a SharePoint Online site

Our site had 18 entries but only 5 with access: two users from other SharePoint groups, the two Microsoft 365 group claims and System Account. For a per-permission view, see checking site permissions in SharePoint Online.

Step 5: Export SharePoint Site Members to CSV

One row per person, with Microsoft 365 group claims expanded:

$groupId = (Get-PnPSite -Includes GroupId).GroupId
$roles = [ordered]@{
    Owner   = Get-PnPGroup -AssociatedOwnerGroup
    Member  = Get-PnPGroup -AssociatedMemberGroup
    Visitor = Get-PnPGroup -AssociatedVisitorGroup
}
$report = foreach ($role in $roles.Keys) {
    $group = $roles[$role]
    foreach ($m in Get-PnPGroupMember -Group $group) {
        if ($m.LoginName -like "*federateddirectoryclaimprovider*") {
            # Microsoft 365 group claim: expand it to the real people
            $people = if ($m.LoginName -like "*_o") { Get-PnPMicrosoft365GroupOwner -Identity $groupId }
                      else { Get-PnPMicrosoft365GroupMember -Identity $groupId }
            foreach ($p in $people) {
                [pscustomobject]@{ Role = $role; Group = $group.Title; Name = $p.DisplayName
                                   Email = $p.UserPrincipalName; Via = "Microsoft 365 group" }
            }
        }
        elseif ($m.PrincipalType -eq "User" -and $m.LoginName -ne "SHAREPOINT\system") {
            [pscustomobject]@{ Role = $role; Group = $group.Title; Name = $m.Title
                               Email = $m.Email; Via = "Direct" }
        }
    }
}
$report | Export-Csv -Path "C:\Reports\SPGuides-site-members.csv" -NoTypeInformation
"Rows exported: $($report.Count)"
Exporting SharePoint site owners and members to a CSV file with PnP PowerShell

Our run exported 9 rows: 4 owners and 5 members. If a group contains a plain Entra ID security group, expand it with Get-PnPEntraIDGroupMember -Identity <group> -Transitive.

Get-SPOUser and Get-SPOSiteGroup Equivalents

In the SharePoint Online Management Shell (shown as code, not run), you need SharePoint admin and site collection admin rights. Get-SPOUser stops at 500 accounts without -Limit All, and Get-SPOSiteGroup at 200 groups.

Connect-SPOService -Url "https://spguides-admin.sharepoint.com"
Get-SPOUser -Site "https://spguides.sharepoint.com/sites/SPGuides" -Group "SPGuides Members" -Limit All
Get-SPOSiteGroup -Site "https://spguides.sharepoint.com/sites/SPGuides" | Select-Object Title, Users, Roles
CmdletReturns
Get-PnPGroupMemberUsers and security groups in one SharePoint group
Get-PnPMicrosoft365GroupMember / OwnerPeople in the connected Microsoft 365 group
Get-PnPUserEvery User Information List entry, current or not
Get-SPOUser / Get-SPOSiteGroupUsers, or groups with users and roles (SPO shell)

Common Mistakes to Avoid

  • Reporting Get-PnPUser as the member list: 18 entries on our site, but 5 actual people.
  • Stopping at the group claim on group-connected sites instead of expanding it.
  • Group membership hidden from non-members; see the fix for permission to view group membership.

Can You Get Members of All SharePoint Sites at Once?

Yes. Connect to the admin center, list sites as in getting all SharePoint sites with PnP PowerShell, then connect to each site and run the Step 5 loop. Add SharePoint Online site details using PowerShell for storage data.

Next Steps

Once you know who’s on the site, check what they can open with SharePoint folder permissions using PowerShell, or grab more audit scripts from our PnP PowerShell scripts for SharePoint developers.

We also keep these guides handy when we clean up site access:

Frequently Asked Questions

How do I get all members of a SharePoint Online site using PowerShell?

Run Get-PnPGroupMember for the groups returned by Get-PnPGroup -AssociatedOwnerGroup, -AssociatedMemberGroup and -AssociatedVisitorGroup. On a group-connected site, also run Get-PnPMicrosoft365GroupMember and Get-PnPMicrosoft365GroupOwner.

What is the difference between Get-PnPUser and Get-PnPGroupMember?

Get-PnPUser reads the User Information List, which keeps people whose access was removed. Get-PnPGroupMember returns current members of one group. See the Get-PnPUser reference.

How do I use Get-SPOUser to list SharePoint group members?

After Connect-SPOService, run Get-SPOUser -Site -Group “”. Add -Limit All, because the default is 500. See the Get-SPOUser documentation.

Why does Get-PnPGroupMember return a group instead of people?

On a site connected to a Microsoft 365 group, the Owners and Members groups contain the group as one SecurityGroup entry. Expand it with the Microsoft 365 group cmdlets.