M365Buddy
SharePoint Online · PnP PowerShell

Export All SharePoint Online Sites Using PnP PowerShell

Generate a SharePoint Online site inventory and storage usage report using PnP PowerShell, Microsoft Entra ID certificate authentication, SharePoint tenant commands and CSV export.

PowerShell 7+PnP PowerShellEntra IDCSVSharePoint Online
SharePoint Site Inventory PowerShell Script

View the actual source first, copy it, edit a working copy, or download the .PS1 directly to your PC.

How to use the script
Use View Script to inspect the complete source inside M365Buddy. From there you can copy it or open it in Script Editor. Use Download .PS1 when you want the file on your PC.

What does this script do?

This script creates a consolidated SharePoint Online inventory containing site information, storage usage, site type, web creation date, site collection administrators, SharePoint owners and Microsoft 365 Group visibility. It connects to the SharePoint Admin Center, retrieves sites, processes each site and exports the final report to CSV.

Prerequisites

1. PowerShell 7 or later

PowerShell 7 or later is required because the script uses parallel processing.

$PSVersionTable

2. PnP PowerShell

Install-Module PnP.PowerShell -Scope CurrentUser
Get-Module PnP.PowerShell -ListAvailable
Import-Module PnP.PowerShell

3. Microsoft Entra ID application

You need an Entra application, client ID, tenant information, public certificate, private certificate, PFX password, the permissions required by the commands below, and admin consent where required.

Related setup article:
Register an Entra ID App for PnP PowerShell Certificate Authentication
This linked article explains application registration, certificate creation, certificate upload, API permissions, admin consent, and authentication testing.

Required access for this report

ResourceRequired accessWhy it is needed
SharePoint OnlineAccess to the SharePoint Tenant Administration site, using an app-only permission and tenant access configuration supported by your PnP.PowerShell version.Get-PnPTenantSite and Get-PnPTenant retrieve tenant-wide site inventory and storage allocation.
Microsoft GraphRead access to Microsoft 365 Groups. Group.Read.All (Application) is a common candidate for the group lookup; verify the requirement for your installed PnP.PowerShell version before granting it.Get-PnPMicrosoft365Group retrieves the Public/Private visibility for group-connected sites.
Individual SharePoint sitesThe app must be authorized to read site/web information, users, and SharePoint groups for the sites being inventoried.Get-PnPWeb, Get-PnPUser, Get-PnPGroup, and Get-PnPGroupMember populate creation date, site collection administrators, and SharePoint Owners.
Least-privilege reminder: Do not grant Sites.FullControl.All, Directory.Read.All, or other broad permissions simply because a generic setup guide lists them. Check the requirements of the exact commands and your installed module, grant only what the report needs, and provide admin consent where required. In a PowerShell 7 session with PnP.PowerShell installed, inspect command permission metadata with:
Get-PnPCommandPermission -CommandName Get-PnPTenantSite
Get-PnPCommandPermission -CommandName Get-PnPTenant
Get-PnPCommandPermission -CommandName Get-PnPMicrosoft365Group
Get-PnPCommandPermission -CommandName Get-PnPWeb
Get-PnPCommandPermission -CommandName Get-PnPUser
Get-PnPCommandPermission -CommandName Get-PnPGroup
Get-PnPCommandPermission -CommandName Get-PnPGroupMember

Use the returned application-permission requirements alongside the PnP documentation for your installed version. The SharePoint tenant-administration access requirement is separate from Microsoft Graph permissions.

4. Certificate file

Update the PFX path in the script. The certificate must include the private key and match the certificate registered on the Entra application.

5. SMTP configuration — only if you want email

The standard version emails the CSV through the configured SMTP server. If you do not need email, you can use the same script as a CSV-only report — see Do not want email? Make it CSV-only.

If you want email but do not have an SMTP server or relay, do not add a fake SMTP value. See What if you do not have an SMTP server? for the recommended Microsoft 365 alternatives.

6. Output folder

The account running the script needs permission to write to the configured output folder.

Configuration values

Review the configuration section before execution and use placeholders when publishing the script publicly.

$Tenant = "contoso.onmicrosoft.com"
$ClientId = "YOUR-APPLICATION-CLIENT-ID"
$PfxPath = "C:\Certificates\SharePointAutomationCert.pfx"
$AdminCenterUrl = "https://contoso-admin.sharepoint.com"
$TenantRootUrl = "https://contoso.sharepoint.com"
$OutputDirectory = "C:\Reports\SharePointSiteInventory"
Security: Never publish PFX files, private keys, passwords, SMTP credentials or other secrets in a public GitHub repository.

How to execute the script

Step 1 — View or download

Click View Script to inspect the code. Click Open in Script Editor to modify a copy in the browser. Click Download .PS1 to download the PowerShell file to your PC.

Step 2 — Open PowerShell 7

Open PowerShell 7 using the account that will run the report. Administrator elevation is not inherently required for the script's cloud operations. Use an elevated session only when your setup requires it—for example, installing modules for all users or changing machine-level settings. The account still needs access to the PFX/private key and write access to the output folder.

cd "C:\Scripts\SharePoint"

Step 3 — Run the script

.\SharePoint_Site_Inventory.ps1

Step 4 — Check and resolve PowerShell execution policy

If PowerShell shows an error such as running scripts is disabled on this system, first check the effective policy:

Get-ExecutionPolicy
Get-ExecutionPolicy -List

For a normal user workstation, the preferred approach is to allow locally created scripts for your user account:

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

If the downloaded .ps1 file is being treated as a file from the Internet, unblock just this script:

Unblock-File -Path "C:\Scripts\SharePoint\SharePoint_Site_Inventory.ps1"

Then run the script again:

cd "C:\Scripts\SharePoint"
.\SharePoint_Site_Inventory.ps1

Use PowerShell 7 as Administrator only if the setup action requires elevation, such as installing modules for all users or changing a machine-level setting. The CurrentUser execution-policy change itself does not require administrator rights.

If the policy is controlled by your organization: if Get-ExecutionPolicy -List shows MachinePolicy or UserPolicy set by Group Policy/Intune, do not try to bypass it. Contact your Microsoft 365/endpoint administrator and follow the organization's approved policy.
Windows PowerShell 7 — Administrator— □ ×
PS C:\Scripts\SharePoint> Get-ExecutionPolicy -List
 
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser RemoteSigned
LocalMachine RemoteSigned
 
PS C:\Scripts\SharePoint> Unblock-File -Path ".\SharePoint_Site_Inventory.ps1"
PS C:\Scripts\SharePoint> .\SharePoint_Site_Inventory.ps1
Script execution started...
Illustrative example of the execution-policy check and script execution. Your actual policy values may differ.

What results will be generated?

ColumnDescription
SiteNameSharePoint site title.
SiteUrlSharePoint site URL.
SiteTypeClassified SharePoint site type.
TemplateSharePoint site template.
WebCreatedDateRoot web creation date.
Office365VisibilityMicrosoft 365 Group visibility.
StorageGBCurrent site storage usage.
SiteCollectionAdminsSite collection administrators.
SPOwnersSharePoint Owner Group members.
How to interpret blank fields: The script intentionally keeps the report clean and does not add per-site error or status columns. If a site connection or a detail lookup cannot be completed, the affected field may be blank while the site can still appear in the CSV. A blank Office365Visibility value does not prove that a group is public or private. Also, SPOwners lists members of the associated SharePoint Owner group; it is not a list of Microsoft 365 Group owners.

Illustrative CSV row

The values below are fictional examples. This table shows how one row from the exported CSV maps to the report columns.

SiteNameSiteUrlSiteTypeTemplateWebCreatedDateOffice365VisibilityStorageGBSiteCollectionAdminsSPOwners
Project Hubhttps://contoso.sharepoint.com/sites/ProjectHubMicrosoft 365 Group / Teams SiteGROUP#02025-04-18Private12.45admin@contoso.comowner1@contoso.com; owner2@contoso.com

On smaller screens, scroll the table horizontally to see all nine columns.

Storage and over-utilization

For each site, the script converts StorageUsageCurrent from MB to GB using StorageUsageCurrent / 1024. It sums those site values, reads the tenant's StorageQuota, and calculates over-utilization as used storage minus allocated storage. The percentage is over-utilized storage divided by allocated storage, multiplied by 100. Negative over-utilization is displayed as zero.

Interpretation: These figures are calculated from the values returned by the script. They should not be described as an exact reconciliation with every storage figure shown in the Microsoft 365 admin center without separately validating the tenant's storage definitions and returned values.

Email report behavior

The standard version creates the CSV first, builds an HTML email summary, attaches the CSV and sends it through Send-MailMessage. The temporary CSV is deleted only after the send command completes successfully. If email delivery fails, the CSV is retained for recovery. The current filename includes the date (not the time), so rerunning the script on the same day uses the same CSV path and can overwrite an earlier run's file.

The email's Report Date and Report Generated labels currently both display the date only; they do not show a time.

Do not want email? Make it CSV-only

Keep the same inventory script—just remove email delivery.

The script exports the CSV before the email section. Follow the steps below to keep the report on your computer without sending email. Your inventory logic and CSV columns do not need to change.

Go to the step-by-step changes ↓  ·  Need email without SMTP? See alternatives →

Make these four changes

Work on a copy of the script first. Keep the existing site inventory, calculations, validation and Export-Csv sections unchanged.

1Remove the email configuration

Near the top of the script, remove or comment out these email settings:

$From = "SharePointReport@contoso.com"

$To = @(
    "admin@contoso.com"
)

$SmtpServer = "smtp.contoso.com"
2Remove the email-sending section

Find the section marked # EMAIL. Remove the email subject/body creation and the complete try { ... } catch { ... } block used to send the email and delete the temporary CSV.

The sending command looks like this:

Send-MailMessage `
    -From $From `
    -To $To `
    -Subject $Subject `
    -Body $Body `
    -BodyAsHtml `
    -Attachments $OutputCsv `
    -SmtpServer $SmtpServer `
    -ErrorAction Stop
3Keep the CSV export intact

Do not remove the earlier Export-Csv block or the configuration for $OutputDirectory. These are what create and save the report.

4Run the script and check the output folder

Run your modified copy in PowerShell 7. When it completes, open the folder specified by $OutputDirectory and confirm that the CSV was created.

Expected result
Run script Collect sites Export CSV CSV stays in output folder

What if you do not have an SMTP server?

You have two practical choices depending on whether email is actually required:

Option 1 — You only need the report

Use the CSV-only version. Keep the inventory and Export-Csv logic and remove the SMTP/email block. The report stays in $OutputDirectory.

Option 2 — You need the report emailed

If SMTP is unavailable, use an approved Microsoft 365-based delivery method instead of trying to configure Send-MailMessage against an unknown server.

  • Microsoft Graph sendMail: the PowerShell process authenticates with an Entra ID application and sends the message through Exchange Online. Read the Microsoft Graph email article →
  • Power Automate: the script places the CSV in an approved SharePoint or OneDrive location and a flow sends the email using the Microsoft 365 Outlook connector. Read the Power Automate email article →
  • Azure Automation / Logic Apps: useful for scheduled or unattended reporting where report generation and email delivery are separated. A dedicated article can cover this pattern when the automation workflow is needed.

These approaches are separate from the SMTP version of this script. Do not simply replace the SMTP server with an Exchange Online hostname and expect Send-MailMessage to provide the same Microsoft 365 authentication model.

Important: Do not place an SMTP password, client secret, PFX password or private key directly in a public script. Use approved authentication and secret-management practices for unattended email delivery.

Troubleshooting

Certificate file not found

Test-Path "C:\Certificates\SharePointAutomationCert.pfx"

PnP PowerShell module not found

Get-Module PnP.PowerShell -ListAvailable
Install-Module PnP.PowerShell -Scope CurrentUser

Access denied or blank report fields

Check admin consent, application permissions, certificate matching, tenant/client ID and SharePoint access. This script intentionally suppresses per-site lookup errors to keep the console and CSV clean. If a field is blank, validate the relevant app permission and access to that site separately; do not interpret a blank value as a confirmed negative result.

SMTP email failed

Check SMTP server, sender, recipients, relay permissions, firewall/port and authentication. If email is not required, remove the email block and use CSV-only mode.

Security recommendations

  • Never publish PFX files or private keys.
  • Never publish certificate passwords, client secrets or SMTP passwords.
  • Replace internal tenant names, IDs, SMTP servers, email addresses and local paths with placeholders.
  • Use approved secret management for unattended automation.

Use the script

SharePoint Site Inventory

View it first, then copy, edit or download.

M365Buddy · SharePoint Online · PowerShell · PnP PowerShell