How to Export SharePoint Group Members to CSV Using PnP PowerShell

SharePoint groups are commonly used to manage access to sites, lists, libraries, folders, and documents. During migrations, permission reviews, security audits, or general administration, you may need to generate a report containing all users who belong to a specific SharePoint group.

In this article, we will use PnP PowerShell to retrieve the direct members of a SharePoint group and export the following information to a CSV file:

  • Display name
  • Email address
  • SharePoint login name or claims value

The resulting CSV can be opened in Microsoft Excel, imported into Power BI, or processed by another PowerShell, C#, or reporting solution.

Important distinction: SharePoint groups and Microsoft 365 groups

This article focuses on a SharePoint group, such as:

  • Site Owners
  • Site Members
  • Site Visitors
  • A custom SharePoint permission group

A SharePoint group belongs to a specific SharePoint site collection and is generally used to grant SharePoint permissions.

This is different from:

  • Microsoft 365 groups
  • Microsoft Entra ID security groups
  • Distribution lists
  • Microsoft Teams membership

The Get-PnPGroupMember cmdlet retrieves members of a SharePoint group in the currently connected site. It does not automatically behave as a recursive Microsoft Entra ID group-expansion tool.

Prerequisites

You need:

  • Access to the target SharePoint site
  • Permission to read the SharePoint group membership
  • PowerShell 7
  • PnP PowerShell
  • A Microsoft Entra ID application registration for interactive authentication

Current PnP PowerShell documentation requires PowerShell 7.4 or later for the latest supported module versions. PnP PowerShell is a cross-platform module that can be used on Windows, Linux, and macOS.

Step 1: Check whether PnP PowerShell is installed

Open PowerShell 7 and run:

Get-Module PnP.PowerShell -ListAvailable

If no module is returned, install PnP PowerShell:

Install-Module PnP.PowerShell -Scope CurrentUser

If the module is already installed, it can be updated with:

Update-Module PnP.PowerShell

You can confirm the installed version with:

Get-InstalledModule PnP.PowerShell

Step 2: Define the SharePoint site URL

Create a variable containing the URL of the site where the SharePoint group exists:

$SiteUrl = "https://contoso.sharepoint.com/sites/Finance"

The connection must target the correct site because SharePoint groups are associated with a specific site collection.

A group called Finance Members, for example, may exist in the Finance site but not in another site collection.

Step 3: Connect to SharePoint Online

Use Connect-PnPOnline with interactive authentication:

Connect-PnPOnline `
-Url $SiteUrl `
-Interactive `
-ClientId "YOUR-APPLICATION-CLIENT-ID"

Replace YOUR-APPLICATION-CLIENT-ID with the client ID of your Microsoft Entra ID application registration.

Interactive authentication supports modern authentication requirements such as multifactor authentication and Conditional Access. Current PnP PowerShell guidance uses an Entra ID application client ID for interactive connections.

After authentication, confirm that the connection is working:

Get-PnPWeb

This should return information about the current SharePoint web, including its title and server-relative URL.

Step 4: List the SharePoint groups

Before retrieving members, list the groups available in the current site:

Get-PnPGroup |
Select-Object Id, Title, LoginName |
Format-Table -AutoSize

The output should look similar to this:

Id Title LoginName
-- ----- ---------
3 Finance Owners Finance Owners
4 Finance Members Finance Members
5 Finance Visitors Finance Visitors

Copy the exact title of the group you want to export.

For example:

$GroupName = "Finance Members"

Using the exact title avoids errors caused by spelling differences, spaces, or unexpected group names.

Step 5: Retrieve the group members

Use Get-PnPGroupMember to retrieve the direct members of the selected SharePoint group:

$Members = Get-PnPGroupMember -Group $GroupName

The Get-PnPGroupMember cmdlet retrieves members from a SharePoint group in the current PnP connection.

To inspect the returned values before generating the CSV, run:

$Members |
Select-Object Title, Email, LoginName |
Format-Table -AutoSize

Example output:

Title Email LoginName
----- ----- ---------
John Smith john.smith@contoso.com i:0#.f|membership|john.smith@contoso.com
Maria Silva maria.silva@contoso.com i:0#.f|membership|maria.silva@contoso.com

The relevant SharePoint user properties are:

SharePoint propertyCSV columnDescription
TitleDisplayNameUser-friendly display name
EmailEmailEmail address stored in SharePoint
LoginNameClaimsSharePoint login or claims identity

Understanding the claims value

In SharePoint Online, a normal user login frequently appears in the following format:

i:0#.f|membership|john.smith@contoso.com

This is the SharePoint claims-encoded login name.

The value can be divided into two general parts:

i:0#.f|membership|

This is the claims provider prefix.

john.smith@contoso.com

This is the user principal name associated with the account.

Keeping the complete claims value in the report is useful when:

  • Comparing SharePoint permissions
  • Migrating users
  • Investigating unresolved accounts
  • Updating person fields
  • Creating permission reports
  • Processing users with PowerShell or C#

Step 6: Create the destination folder

Define the CSV path:

$CsvPath = "C:\Temp\SharePointGroupMembers.csv"

Make sure the destination folder exists:

$OutputFolder = Split-Path -Path $CsvPath -Parent
if (-not (Test-Path -Path $OutputFolder)) {
New-Item `
-Path $OutputFolder `
-ItemType Directory `
-Force | Out-Null
}

This prevents Export-Csv from failing because the destination directory does not exist.

Step 7: Prepare the report objects

Instead of exporting the original SharePoint objects directly, create new PowerShell objects containing only the required properties:

$Report = $Members | ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.Title
Email = $_.Email
Claims = $_.LoginName
}
}

This gives us full control over:

  • Column names
  • Column order
  • Which values are included
  • Future transformations
  • Additional validation

The resulting objects contain exactly three properties:

DisplayName
Email
Claims

Step 8: Export the report to CSV

Export the report with:

$Report |
Sort-Object DisplayName |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation `
-Encoding UTF8 `
-Delimiter ";"

Export-Csv converts PowerShell objects into CSV rows and saves them to a file. Each object becomes one row, and each selected object property becomes one column.

Why use -NoTypeInformation?

This prevents PowerShell type metadata from being written to the beginning of the CSV file.

Why use -Encoding UTF8?

UTF-8 helps preserve accented and international characters in names such as:

João Gonçalves
José María
François Dupont

Why use a semicolon delimiter?

In many regional versions of Microsoft Excel, including common Brazilian configurations, the semicolon is more reliably recognized as the column separator.

The output will look similar to this:

"DisplayName";"Email";"Claims"
"John Smith";"john.smith@contoso.com";"i:0#.f|membership|john.smith@contoso.com"
"Maria Silva";"maria.silva@contoso.com";"i:0#.f|membership|maria.silva@contoso.com"

If your environment expects a comma-separated file, remove the delimiter parameter or use:

-Delimiter ","

Complete PowerShell script

The following script combines all the previous steps:

# ============================================================
# Configuration
# ============================================================
$SiteUrl = "https://contoso.sharepoint.com/sites/Finance"
$GroupName = "Finance Members"
$ClientId = "YOUR-APPLICATION-CLIENT-ID"
$CsvPath = "C:\Temp\SharePointGroupMembers.csv"
# ============================================================
# Create the output folder
# ============================================================
$OutputFolder = Split-Path -Path $CsvPath -Parent
if (-not (Test-Path -Path $OutputFolder)) {
New-Item `
-Path $OutputFolder `
-ItemType Directory `
-Force | Out-Null
}
# ============================================================
# Connect to SharePoint Online
# ============================================================
Connect-PnPOnline `
-Url $SiteUrl `
-Interactive `
-ClientId $ClientId
# ============================================================
# Validate the SharePoint group
# ============================================================
$Group = Get-PnPGroup -Identity $GroupName
if ($null -eq $Group) {
throw "The SharePoint group '$GroupName' was not found."
}
Write-Host ""
Write-Host "Group found: $($Group.Title)" -ForegroundColor Cyan
# ============================================================
# Retrieve the direct group members
# ============================================================
$Members = @(
Get-PnPGroupMember -Group $Group
)
if ($Members.Count -eq 0) {
Write-Warning "The SharePoint group does not contain any direct members."
return
}
# ============================================================
# Prepare the report
# ============================================================
$Report = $Members | ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.Title
Email = $_.Email
Claims = $_.LoginName
}
}
# ============================================================
# Export the report
# ============================================================
$Report |
Sort-Object DisplayName |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation `
-Encoding UTF8 `
-Delimiter ";"
# ============================================================
# Display the result
# ============================================================
Write-Host ""
Write-Host "Export completed successfully." -ForegroundColor Green
Write-Host "Site: $SiteUrl"
Write-Host "Group: $($Group.Title)"
Write-Host "Members exported: $($Report.Count)"
Write-Host "CSV file: $CsvPath"
Invoke-Item $OutputFolder

Adding error handling

For production use, the main operations can be placed inside a try/catch block:

try {
Connect-PnPOnline `
-Url $SiteUrl `
-Interactive `
-ClientId $ClientId `
-ErrorAction Stop
$Group = Get-PnPGroup `
-Identity $GroupName `
-ErrorAction Stop
$Members = @(
Get-PnPGroupMember `
-Group $Group `
-ErrorAction Stop
)
$Report = $Members | ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.Title
Email = $_.Email
Claims = $_.LoginName
}
}
$Report |
Sort-Object DisplayName |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation `
-Encoding UTF8 `
-Delimiter ";" `
-ErrorAction Stop
Write-Host "Export completed: $CsvPath" -ForegroundColor Green
}
catch {
Write-Error "The export failed: $($_.Exception.Message)"
}
finally {
Disconnect-PnPOnline
}

The finally block disconnects the active PnP PowerShell session even when an error occurs.

Handling users without email addresses

Some returned SharePoint principals may have an empty Email property.

Possible examples include:

  • Legacy user accounts
  • Deleted or disabled users
  • SharePoint system accounts
  • Security groups
  • Microsoft Entra ID groups
  • Users whose information has not been fully synchronized
  • Special claims principals

To identify records without an email address, add a status column:

$Report = $Members | ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.Title
Email = $_.Email
Claims = $_.LoginName
EmailStatus = if ([string]::IsNullOrWhiteSpace($_.Email)) {
"Missing"
}
else {
"Available"
}
}
}

This produces:

DisplayName
Email
Claims
EmailStatus

You can also export only members with an email address:

$Members |
Where-Object {
-not [string]::IsNullOrWhiteSpace($_.Email)
} |
ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.Title
Email = $_.Email
Claims = $_.LoginName
}
} |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation `
-Encoding UTF8 `
-Delimiter ";"

However, excluding users without email addresses may hide important permission assignments. For audit reports, it is generally safer to keep them and identify the missing value.

Direct members versus nested group members

The script exports the principals added directly to the SharePoint group.

For example, consider the following membership:

Finance Members
├── John Smith
├── Maria Silva
└── Finance Department Security Group

The report may return three principals:

John Smith
Maria Silva
Finance Department Security Group

It does not necessarily return every user contained inside the Finance Department Security Group.

Expanding Microsoft Entra ID groups requires an additional process, usually involving Microsoft Graph permissions and group membership cmdlets. This is separate from retrieving direct SharePoint group membership.

SharePoint group members versus all site users

Get-PnPGroupMember should not be confused with Get-PnPUser.

Get-PnPGroupMember retrieves users or principals from a particular SharePoint group.

Get-PnPUser retrieves users stored in the site collection’s User Information List. That list can contain users who accessed the site in the past but no longer have permissions through the selected group.

Therefore, this command:

Get-PnPUser

is not a replacement for:

Get-PnPGroupMember -Group $GroupName

when the requirement is specifically to report the members of one SharePoint group.

Common errors

The group cannot be found

Example:

The SharePoint group 'Finance Members' was not found.

List the groups and copy the exact title:

Get-PnPGroup |
Select-Object Id, Title |
Format-Table -AutoSize

Also confirm that you connected to the correct site collection.

Access denied

The connected account may not have permission to read the group membership.

Test whether the account can access the site and retrieve groups:

Get-PnPWeb
Get-PnPGroup

The CSV contains formatting information

Do not pass output from Format-Table to Export-Csv.

Incorrect:

$Members |
Format-Table Title, Email |
Export-Csv -Path $CsvPath

Format-Table creates formatting instructions for console display rather than normal data objects.

Correct:

$Members |
Select-Object Title, Email |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation

The CSV opens in one Excel column

Use a delimiter compatible with the local Excel configuration:

-Delimiter ";"

PowerShell also supports the current culture’s list separator through:

-UseCulture

For example:

$Report |
Export-Csv `
-Path $CsvPath `
-NoTypeInformation `
-Encoding UTF8 `
-UseCulture

Conclusion

PnP PowerShell provides a straightforward way to retrieve direct members from a SharePoint group and export them into a structured CSV report.

The main process is:

  1. Connect to the correct SharePoint site.
  2. Locate the SharePoint group.
  3. Retrieve its direct members.
  4. Select the required user properties.
  5. Export the resulting objects to CSV.

The essential command is:

Get-PnPGroupMember -Group $GroupName

Combined with PSCustomObject and Export-Csv, it produces a clean report containing display names, email addresses, and SharePoint claims identities.

This report can support permission audits, migrations, access reviews, governance processes, troubleshooting, and integration with external reporting solutions.

Edvaldo Guimrães Filho Avatar

Published by