PowerShell Guide: Granting Sites.Selected Permissions to a SharePoint Site (PnP.PowerShell)
Sites.Selected is a two-step permission model:
- You grant the app the SharePoint application permission
Sites.Selectedin Entra ID (admin consent). - You explicitly grant that app access to specific site(s) (Read/Write/Manage/FullControl). Until step 2 happens, the app has no access to any site. (Microsoft Learn)
This article focuses on Step 2, using PnP.PowerShell.
What you will use (the 4 core cmdlets)
Microsoft Learn explicitly lists these PnP cmdlets for managing Sites.Selected site grants: (Microsoft Learn)
Grant-PnPAzureADAppSitePermission(create a site grant) (PNP GitHub)Get-PnPAzureADAppSitePermission(list/inspect grants) (PNP GitHub)Set-PnPAzureADAppSitePermission(change a grant) (PNP GitHub)Revoke-PnPAzureADAppSitePermission(remove a grant) (PNP GitHub)
Important change since Sep 9, 2024: you must use your own Entra app for PnP.PowerShell
PnP’s shared “PnP Management Shell” multi-tenant app was deleted/retired, and registering your own Entra app became mandatory. (PNP GitHub)
So you typically have two apps in this story:
- Workload App (your unattended C# app): has SharePoint Application permission
Sites.Selected - Operator App (used to run PnP.PowerShell): used only to grant / manage site permissions
Permissions & roles required to run the grant commands
Who can grant site permissions for Sites.Selected?
Microsoft Learn states that to grant explicit permissions to selected sites, you need a tenant global admin OR an application with Sites.FullControl.All application permission. (Microsoft Learn)
What does PnP.PowerShell require?
PnP’s Grant-* cmdlet documents its requirement as Microsoft Graph delegated Sites.FullControl.All. (PNP GitHub)
Similarly, Get-*, Set-*, and Revoke-* list Microsoft Graph Sites.FullControl.All as required. (PNP GitHub)
Also note: Get-PnPAzureADAppSitePermission explicitly says you need the SharePoint Administrator role to use it (for some scenarios). (PNP GitHub)
Step-by-step: Grant Sites.Selected access to a single site
Step 0 — Install PnP.PowerShell
Install-Module PnP.PowerShell -Scope CurrentUser
Import-Module PnP.PowerShell
Step 1 — Create/register your Operator App for PnP.PowerShell (mandatory)
PnP provides an official guide and cmdlets to register your own Entra app for PnP usage. (PNP GitHub)
Option A (simplest): create an app for Interactive login
Register-PnPEntraIDAppForInteractiveLogin `
-ApplicationName "PnP.PowerShell.Operator" `
-Tenant "contoso.onmicrosoft.com"
PnP explains this is the easiest automatic path for interactive login. (PNP GitHub)
After creating the app, you must ensure it has the permissions you need for the cmdlets you’ll run (next step).
Step 2 — Ensure the Operator App has Graph delegated Sites.FullControl.All
Because Grant-PnPAzureADAppSitePermission requires Microsoft Graph delegated Sites.FullControl.All. (PNP GitHub)
In Entra ID (for Operator App):
- API permissions → Microsoft Graph → Delegated permissions → add:
Sites.FullControl.All
- Grant admin consent
(You only do this once.)
Step 3 — Connect to the target SharePoint site with the Operator App
Use Connect-PnPOnline with your Operator App’s ClientId. (The exact connect parameters vary by how you set up the app; interactive is typical for admin operations.)
Example (interactive):
Connect-PnPOnline `
-Url "https://contoso.sharepoint.com/sites/DevSite" `
-ClientId "<OPERATOR-APP-CLIENT-ID>" `
-Tenant "contoso.onmicrosoft.com" `
-Interactive
Step 4 — Grant the Workload App permission to this site (the key step)
This creates the actual site-scoped access entry for your Workload App, and is explicitly “used in conjunction with … Sites.Selected”. (PNP GitHub)
Grant-PnPAzureADAppSitePermission `
-AppId "<WORKLOAD-APP-CLIENT-ID>" `
-DisplayName "Contoso Workload App (Unattended)" `
-Site "https://contoso.sharepoint.com/sites/DevSite" `
-Permissions Write
Accepted values:
Read,Write,Manage,FullControl(PNP GitHub)
Tip: For “create item in list”, Write is the normal minimum.
Step 5 — Verify the grant and capture the PermissionId
Get-PnPAzureADAppSitePermission -Site "https://contoso.sharepoint.com/sites/DevSite"
Get-PnPAzureADAppSitePermission returns the app permissions for the site, and it also supports filtering by -AppIdentity or -PermissionId. (PNP GitHub)
You will need the PermissionId if you want to update or revoke.
Example (filter by app):
Get-PnPAzureADAppSitePermission `
-Site "https://contoso.sharepoint.com/sites/DevSite" `
-AppIdentity "<WORKLOAD-APP-CLIENT-ID>"
Step 6 — Update an existing grant (Read → Write, Write → FullControl, etc.)
Set-PnPAzureADAppSitePermission `
-Site "https://contoso.sharepoint.com/sites/DevSite" `
-PermissionId "<PERMISSION-ID>" `
-Permissions FullControl
Set-* is explicitly for updating permissions and supports the same role values. (PNP GitHub)
Step 7 — Revoke the grant (remove app access from the site)
Revoke-PnPAzureADAppSitePermission `
-Site "https://contoso.sharepoint.com/sites/DevSite" `
-PermissionId "<PERMISSION-ID>"
Common issues (and what they usually mean)
“403 Forbidden” in your C# app after token success
Almost always means:
- The Workload App has
Sites.Selected, but you did not grant the site permission (Step 4), or - You granted Read but your code is performing Write
This is exactly how Sites.Selected is designed: no site access until explicit grant exists. (Microsoft Learn)
“Need admin approval” or permission prompts in PnP
Usually means your Operator App is missing the required Graph delegated permission (Sites.FullControl.All) or admin consent wasn’t granted. The cmdlets list this requirement. (PNP GitHub)
Quick reference cheat-sheet
# 1) Connect as Operator (interactive)
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/DevSite" -ClientId "<OPERATOR-APP-ID>" -Tenant "contoso.onmicrosoft.com" -Interactive
# 2) Grant Workload app access
Grant-PnPAzureADAppSitePermission -AppId "<WORKLOAD-APP-ID>" -DisplayName "Workload App" -Site "https://contoso.sharepoint.com/sites/DevSite" -Permissions Write
# 3) Verify
Get-PnPAzureADAppSitePermission -Site "https://contoso.sharepoint.com/sites/DevSite"
# 4) Update
Set-PnPAzureADAppSitePermission -PermissionId "<PERMISSION-ID>" -Site "https://contoso.sharepoint.com/sites/DevSite" -Permissions Read
# 5) Revoke
Revoke-PnPAzureADAppSitePermission -PermissionId "<PERMISSION-ID>" -Site "https://contoso.sharepoint.com/sites/DevSite"
