30-second answer

Sites.Read.All, the application role published by Office 365 SharePoint Online, was enough for every read below. Nine operations were tested against a live tenant with an application identity that held that role and nothing else. Two tenant-level reads were refused, and their minimum has not been established.

What this proves

The identity was registered with no permissions at all, then given Sites.Read.All (d13f72ca-a275-4b96-b789-48ebcc4da984) and nothing else. Each operation ran in its own session against a real tenant.

OperationPermission testedResultVerified
Get-PnPWebSites.Read.Allsucceeded18 Aug 2026
Get-PnPSiteSites.Read.Allsucceeded18 Aug 2026
Get-PnPListSites.Read.Allsucceeded18 Aug 2026
Get-PnPListItemSites.Read.Allsucceeded18 Aug 2026
Get-PnPSiteCollectionAdminSites.Read.Allsucceeded18 Aug 2026
Get-PnPFeature -Scope WebSites.Read.Allsucceeded18 Aug 2026
Get-PnPPageSites.Read.Allsucceeded18 Aug 2026
Get-PnPAppSites.Read.Allsucceeded18 Aug 2026
Get-PnPCopilotAgentSites.Read.Allsucceeded18 Aug 2026
Get-PnPTenantSiteSites.Read.Allrefused18 Aug 2026
Get-PnPTenantSites.Read.Allrefused18 Aug 2026

Nine site-scoped reads need that one role and nothing more. That is a floor somebody measured, not a ceiling somebody copied out of documentation.

What it does not prove

It does not establish the minimum for the two refusals. The error is an authorisation error, it is identical on the tenant administration host, and connecting to https://<tenant>-admin.sharepoint.com does not change it. Which role those two operations need has not been established, and this article does not guess at one.

It also does not say that Sites.Read.All is narrow. It is broad: it grants read access to every site collection in the organisation. That is uncomfortable for a script that only needs one site, and it is what the model offers. The SharePoint application roles are Sites.Read.All, Sites.Manage.All, Sites.ReadWrite.All and Sites.FullControl.All, and none of them is narrower than the first while still reading a site. It is the minimum available, not the minimum imaginable.

There is a narrower model, Sites.Selected, where an administrator grants an application access to named sites individually. It is a different mechanism rather than a smaller version of the same one, and it was not part of this test. If your script reads a fixed, known set of sites, it is the direction worth investigating before accepting tenant-wide read.

PowerShell

PowerShell
Connect-PnPOnline -Url "https://<tenant>.sharepoint.com" `
  -ClientId $appId -Tenant $tenantId -CertificatePath ./app.pfx

Get-PnPWeb          # succeeds with Sites.Read.All alone
Get-PnPTenantSite   # refused with Sites.Read.All alone

Example output

Text
Attempted to perform an unauthorized operation.

Explanation

Permissions read off documentation describe what an API is documented to require. They do not establish what your version of a module, against your tenant, actually needs. Starting from an identity with nothing and adding one role produces a different kind of answer.

It also surfaces the thing documentation never mentions. Connect-PnPOnline succeeded here before any permission existed at all, which is covered separately in Why Connect-PnPOnline succeeds when the next command is refused.

Production considerations

  • This is an application permission, consented once by an administrator, and it applies with no user present. It is not the same as the delegated permission of the same name, which is bounded by what the signed-in user can already see.
  • Application-only through Entra ID needs a certificate on this path, not a client secret. See Certificate or client secret for PnP app-only.
  • Grant it to a purpose-built identity. An application that reads every site in the organisation is worth naming, dating and reviewing.

None directly. This establishes what an identity can read, which is a precondition for collecting evidence rather than a claim about a tenant.

References

Found something wrong? Suggest a correction. The article source is not public; the engine it cites is.

What this answer underwrites

The engine reads these Microsoft operations to collect evidence, so what is established here is what those collectors rest on.

  • activitywhen a person last changed something on one site
  • agentsthe Copilot agents in one site, and the sources each declares
  • classificationwhat a site records about the kind of content it holds
  • modernityhow one site is built: template, branding, publishing
  • ownerswho administers one site
  • permissionsevery visible list on a site, and its inheritance
  • sharingwhat one site permits, and its default link
  • sitesevery site this identity can enumerate
  • spfxa site's app catalog: which solutions lag their version