Live Developer Training: next batch coming soon. Be the first to know.Live training: next batch soonJoin the waitlist →Join →

How to Get All SharePoint Online Sites Using PnP PowerShell

Every SharePoint cleanup I’ve run starts with the same question: how many sites do we actually have? The admin center shows them, but you can’t easily filter by template, join the list with storage numbers, or hand a CSV to the person who has to chase site owners.

PnP PowerShell answers that in a few lines. I tested everything below on my own Microsoft 365 tenant in PowerShell 7 with PnP.PowerShell 3.3: listing every site collection, filtering by template and URL, adding OneDrive sites, finding subsites and exporting the result.

Quick answer: Connect to the SharePoint admin center, not a site: Connect-PnPOnline -Url https://contoso-admin.sharepoint.com -ClientId <your-app-id> -Interactive. Then run Get-PnPTenantSite to return every site collection (OneDrive excluded). Add -Template, -Filter "Url -like 'sales'" or -IncludeOneDriveSites to narrow or widen the list, and pipe it to Export-Csv for a report.

What you need before you start

Get-PnPTenantSite reads tenant-level data, so a regular site member can’t run it. Check these three things first. If PnP isn’t on your machine yet, follow my guide on installing PnP PowerShell for SharePoint Online.

  • PowerShell 7.4 or later with the PnP.PowerShell module. Current PnP versions don’t load in Windows PowerShell 5.1.
  • The SharePoint Administrator or Global Administrator role. The cmdlet runs against the tenant administration site.
  • Your own Entra ID app registration for the -ClientId parameter. The shared PnP app stopped working on September 9, 2024, which is why older scripts without a client ID fail.

If you landed here from an older tutorial and saw an error about Register-PnPManagementShellAccess, that’s the same retired app; my post on the Register-PnPManagementShellAccess error explains the switch.

Get all site collections with Get-PnPTenantSite

Connect to the admin center URL, which is your tenant name followed by -admin, then call the cmdlet with no parameters. I store the result in a variable so I can count, group and filter it without asking SharePoint again:

$adminUrl = "https://contoso-admin.sharepoint.com"
$clientId = "00000000-0000-0000-0000-000000000000"   # your Entra ID app's client ID

Connect-PnPOnline -Url $adminUrl -ClientId $clientId -Interactive

$sites = Get-PnPTenantSite
"Site collections found: $($sites.Count)"

Here’s the real run on my tenant. Grouping by template is the fastest way to see what kind of sites you have before you filter anything:

Get-PnPTenantSite returning all SharePoint Online site collections grouped by template in PowerShell 7

Each object includes Title, Url, Template, Owner, StorageUsageCurrent (in MB) and LastContentModifiedDate. Some properties come back with default values unless you add -Detailed, which fills in every attribute but runs noticeably slower on a large tenant.

For one site, pass its URL: Get-PnPTenantSite -Identity https://contoso.sharepoint.com/sites/Marketing -Detailed. Modern sites can also be looked up by their site ID.

Filter sites by template: team sites, communication sites and more

The Template value tells you what kind of site each one is. These are the codes I saw in my tenant, and they’re the ones you’ll meet most often. If the difference between the first two is fuzzy, read my comparison of team sites and communication sites.

TemplateWhat it is
GROUP#0Team site connected to a Microsoft 365 group (including sites created by Microsoft Teams)
SITEPAGEPUBLISHING#0Communication site
STS#3Modern team site without a Microsoft 365 group
STS#0Classic team site (often the tenant root site on older tenants)
TEAMCHANNEL#0 / #1Site for a Teams private or shared channel
RedirectSite#0Redirect left behind when a site URL was changed
SRCHCEN#0, APPCATALOG#0, SPSMSITEHOST#0System sites: search center, app catalog, OneDrive host

Pass a template to -Template and SharePoint filters on the server, which is faster than pulling everything and filtering locally:

# Communication sites
Get-PnPTenantSite -Template SITEPAGEPUBLISHING#0

# Team sites connected to a Microsoft 365 group (includes Teams team sites)
Get-PnPTenantSite -Template GROUP#0

# Any site with a Microsoft 365 group behind it
Get-PnPTenantSite -GroupIdDefined $true

# Classic-style team sites without a group
Get-PnPTenantSite -Template STS#3

In my tests the template value wasn’t case-sensitive: SITEPAGEPUBLISHING#0 returned the root site even though it reports SitePagePublishing#0.

Filter sites by URL or other properties

-Filter takes a server-side expression. With -like, the value matches anywhere in the URL, so