Table of Contents

How to determine which permissions you need

Note

As of September 9th, 2024, it is required to use your own Entra ID Application Registration to use PnP PowerShell. This introduces the complexity of trying to determine the minimum set of permissions you will need to be able to execute your script. This article aims to help you in determining the permissions you need to set on your Entra ID Application Registration.

Creating an Entra ID Application Registration

In case you're starting from the beginning and you do not have your own Entra ID Application Registration yet to use with PnP Powershell, which is mandatory, you can follow these steps to create your Entra ID Application Registration.

Asking PnP PowerShell which permissions a cmdlet needs

Before working out permissions by trial and error, ask the module directly. Get-PnPCommandPermission returns the permissions a cmdlet needs. It reads the metadata that ships inside the module, so it works offline and does not require you to be connected to a tenant:

Get-PnPCommandPermission -CommandName Get-PnPTeamsTeam
CommandName            : Get-PnPTeamsTeam
PermissionSource       : Declared
DelegatedPermissions   : Graph: Group.Read.All OR Graph: Group.ReadWrite.All
ApplicationPermissions : Graph: Group.Read.All OR Graph: Group.ReadWrite.All
ResourceTypes          : Graph
DelegatedAvailable     : True
ApplicationAvailable   : True
MinimumSharePointRole  : NotApplicable

Read the result as follows:

  • Permissions within one set are all required together, they are combined with AND. Multiple sets are alternatives, combined with OR. In the example above, granting only Group.Read.All is enough.
  • The alternatives are listed in the order they are declared on the cmdlet, which by convention starts with the least privileged one. Treat that as a strong hint rather than a guarantee and check the scopes before granting them.
  • MinimumSharePointRole follows the SharePoint permission levels and tells you which rights are needed on the SharePoint site itself, on top of the API permission. Note that some operations need a higher level than their verb suggests: reading permissions needs the Enumerate Permissions right, which only Full Control holds, and customizing pages or applying a theme needs Design rather than Edit. This applies when connecting delegated, where the signed in user is bound by their own rights, and when connecting app only using Sites.Selected, where the application has to be granted access per site. A tenant wide application permission such as Sites.ReadWrite.All already covers every site, so in that case nothing has to be assigned on the site.
  • DelegatedAvailable and ApplicationAvailable tell you whether the cmdlet can be used at all in that scenario. A cmdlet with ApplicationAvailable : False cannot be run app only, no matter which permissions you grant. They are empty when there are no permissions to base that on, i.e. for a ResourceDependent or NotApplicable result.

How reliable is the answer

Always check PermissionSource, it states how authoritative the answer is:

PermissionSource What it means
Declared The permissions are declared on the cmdlet itself. These are accurate.
DeclaredAndInferred Part declared, part derived. The declared part is accurate, the SharePoint part is an estimate. Either the cmdlet uses SharePoint next to the declared API, in which case the SharePoint permission is needed on top, or it uses SharePoint instead of it depending on how you call it, in which case it is an alternative. Read Guidance before granting anything.
Inferred Derived from what the cmdlet does. A least privilege estimate that may need to be raised, and it covers the SharePoint API only.
ResourceDependent The permission follows from what you point the cmdlet at, i.e. New-PnPGraphSubscription needs read permissions on the resource you subscribe to. The Guidance property links to the relevant documentation.
NotApplicable The cmdlet needs no permissions on the application registration you connect with. That covers cmdlets which perform no request at all, i.e. Get-PnPContext, cmdlets which call an API that needs no permissions, i.e. Get-PnPChangeLog which reads the release notes from GitHub, and cmdlets which authenticate separately and can still require rights of their own such as a directory role, i.e. Register-PnPEntraIDApp. Read Guidance to see which applies.
Unknown The permissions could not be determined. Fall back to the approach described further down this article.

Anything other than Declared is guidance rather than a guarantee, so verify it against your own scenario.

Testing the permissions of your current connection

After connecting, use Test-PnPConnectionPermission to compare the scp or roles claims in the current connection's access tokens with the permissions reported for a cmdlet:

Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/project -Interactive -ClientId $clientId
Test-PnPConnectionPermission -CommandName Set-PnPList

The cmdlet returns $true when the connection holds one complete permission set and $false when a required permission is missing, naming the missing AND and OR alternatives on the error stream. A more privileged scope satisfies a lesser one, so a connection holding AllSites.FullControl passes a check for AllSites.Read.

Because the error is non-terminating, the same cmdlet serves both a conditional and a preflight style:

# Branch on the result
if (Test-PnPConnectionPermission -CommandName Set-PnPList -ErrorAction SilentlyContinue) { ... }

# Or fail the script before anything is changed
Test-PnPConnectionPermission -CommandName Set-PnPList -ErrorAction Stop

The test covers API permission claims only. It cannot verify the signed-in user's SharePoint permission level, a Sites.Selected grant on the target site, directory roles or the other requirements listed in MinimumSharePointRole and AdditionalRoles. Where Get-PnPCommandPermission reports unknown, conditional or resource-dependent permissions, or where no token could be acquired for a resource, the cmdlet returns nothing and reports the requirement as indeterminate rather than claiming a permission is missing. Inferred permissions remain estimates even when the current token contains them. Read the error and the Guidance from Get-PnPCommandPermission for those cases.

Working out the permissions for an entire script

The most useful application is composing the permission set for a script before you run it. Extract the PnP cmdlets it uses and ask for their permissions in one go:

# Matched case insensitively, as PowerShell command names are. Get-PnPCommandPermission resolves
# any casing, and any alias, back to the canonical cmdlet name.
$cmdlets = [regex]::Matches((Get-Content ./myscript.ps1 -Raw), '\b[a-z]+-pnp[a-z0-9]+\b', 'IgnoreCase') |
    ForEach-Object { $_.Value } | Sort-Object -Unique

$cmdlets | Get-PnPCommandPermission |
    Format-Table CommandName, PermissionSource,
        @{ n = 'Application'; e = { ($_.ApplicationPermissions | ForEach-Object { $_.ToString() }) -join ' OR ' } }
CommandName       PermissionSource Application
-----------       ---------------- -----------
Add-PnPListItem           Inferred SharePoint: Sites.ReadWrite.All
Connect-PnPOnline    NotApplicable
Get-PnPList               Declared SharePoint: Sites.Selected OR SharePoint: Sites.Read.All OR …
Get-PnPTeamsTeam          Declared Graph: Group.Read.All OR Graph: Group.ReadWrite.All
Set-PnPWeb                Inferred SharePoint: Sites.FullControl.All

To get a starting list to grant, take the first alternative of each cmdlet, which by convention is the least privileged one, and group it per API. Always review the result before granting it, as that convention is not enforced:

$cmdlets | Get-PnPCommandPermission |
    Where-Object { $_.ApplicationPermissions.Count -gt 0 } |
    ForEach-Object { $_.ApplicationPermissions[0].Permissions } |
    Group-Object ResourceType |
    ForEach-Object { "{0}: {1}" -f $_.Name, (($_.Group.Scope | Sort-Object -Unique) -join ', ') }
Graph: Group.Read.All
SharePoint: Sites.FullControl.All, Sites.ReadWrite.All, Sites.Selected

Where several SharePoint scopes appear, the most privileged one covers the others, so in this example Sites.FullControl.All is what the script ends up needing. That immediately shows you which single cmdlet is driving the permission up, in this case Set-PnPWeb. If you can avoid that cmdlet, the whole script drops to Sites.ReadWrite.All.

Use -Source to review only the answers that are estimates and therefore worth verifying:

$cmdlets | Get-PnPCommandPermission | Where-Object PermissionSource -ne 'Declared'

Other useful queries

# Every cmdlet that touches Microsoft Graph, with authoritative metadata only
Get-PnPCommandPermission -ResourceTypeName Graph -Source Declared

# Every Teams related cmdlet
Get-PnPCommandPermission -CommandName *Teams*

# Which cmdlets require SharePoint administrator rights
Get-PnPCommandPermission | Where-Object MinimumSharePointRole -eq 'SharePointAdministrator'

# Which cmdlets cannot be used app only
Get-PnPCommandPermission | Where-Object ApplicationAvailable -eq $false

The rest of this article describes how to apply these permissions to your application registration, starting from the smallest possible set.

Starting with minimal permissions

It is highly recommended to keep the permissions on your Entra ID Application Registration to a minimum to avoid risks when access through your application registration would somehow fall in the wrong hands. As PnP PowerShell always starts with connecting to SharePoint Online, you will at least need permissions to access SharePoint Online, regardless of whatever else you plan on doing with PnP PowerShell.

When using a delegate login

A delegate login means you will be interactively logging in to your tenant using PnP PowerShell by providing your credentials. There are several ways of connecting in this way. Always remember that in this scenario, regardless of which permissions you assign to your Entra ID Application Registration, the user logging in through PnP PowerShell must also have the permissions for whatever the user is trying to do. I.e. if you would assign AllSites.FullControl permissions on your application registration, the user still can only access those SharePoint Online sites to which the user has been granted permissions directly to that site as well and will only have those permissions assigned to it (i.e. read or write).

The lowest permission you can set on a delegate login will be AllSites.Read on the delegate scope of SharePoint:

image

When using an app only context

An app only context is being used when your intend is to run a script that does not require any user intervention to connect and authenticate to your tenant. There are several ways of connecting in this way. In this scenario, exactly those permissions you assign to your Entra ID Application Registration are the ones the script that runs will have. Therefore, be extra careful in this scenario not to set too high permissions.

The lowest permission you can set on an app only scenario will be Sites.Selected on the application scope of SharePoint:

image

When using the Sites.Selected permission, you still must assign permissions to one or more sites where the script will have access to. Ensure you will assign at least Read permissions on the site of which you will use the URL in your Connect-PnPOnline <url> statement in your script. You can easily do so by utilizing the PnP PowerShell cmdlet Grant-PnPAzureADAppSitePermission as shown here:

Grant-PnPAzureADAppSitePermission -AppId "<Client ID of your Entra ID applicarion registration>" -DisplayName "PnP PowerShell" -Permissions Read -Site <url of the SharePoint Online site to which you will connect>

In order to be able to run this cmdlet, you will need to connect to PnP PowerShell using preferably another Entra ID application registration which will have the AllSites.FullControl permission on the delegate scope on SharePoint set to it and being logged on with a Global Administrator or SharePoint Administrator privileged account.

Adding additional permissions as needed

Once you've se the minimum permissions as described above, you can go ahead and test using your Entra ID application registration to connect to SharePoint Online using one of the available connect options. You can add -ValidateConnection -Verbose to your Connect-PnPOnline cmdlet to instruct it to test the connection once established.

Now you likely want to perform more than just a simple read using PnP PowerShell. Read on below to find out what you can do to find out about the additional permissions your application registration might need to operate correctly.

When using a delegate login

When planning to use your app registration for delegates, you could opt for simply starting to use it. Once you execute a cmdlet that requires more permissions, a dialog will pop up asking you to consent to these additional permissions being assigned to the application registration. An example of this is shown on the following screenshot:

image

What technically happens here when you provide consent through this dialog is that in the Entra ID Enterprise Application connected to your Entra ID application registration, the permission gets added. This means that on subsequent requests of this cmdlet using this client id, even in new sessions, these permissions will then be granted already. You can visualize this by going to Entra ID > Identity > Applications > Enterprise applications and looking for the registration with the same name as your Entra ID application registration. Once found, open it, in the menu click on Permissions, go to the User consent tab and look at the assigned permissions:

image

When using an app only context

For an app only scenario, you will have to follow a different approach, as there is no way for it to interactively request for more permissions. If you try to execute a cmdlet for which the Entra ID application registration does not have permissions, it will return you an access denied notice.

The quickest way to find out what to add is to run Get-PnPCommandPermission against the cmdlet that failed, as described earlier in this article. It requires no connection, so you can run it before ever hitting the access denied.

Alternatively you can add -Verbose to your cmdlet. For many, but unfortunately not all, cmdlets, this will reveal which permissions it receives through the application registration and which permissions it actually needs to be able to execute properly. See the following example:

image

In this scenario, you now know you need to add Application.Read.All on the applications scope of Microsoft Graph in your application registration in order to give it sufficient rights to execute this cmdlet.

Help, I can't figure out which permissions I need

First run Get-PnPCommandPermission against the cmdlet, as described earlier in this article. It has an answer for nearly every cmdlet in the module.

If it returns a PermissionSource of Unknown, or an Inferred answer that turns out not to be sufficient, use the table below for guidance on a minimum permissions approach.

What are you trying to do Permission type Permission(s) likely needed from least to most privileged
Interact with SharePoint Delegate AllSites.Read / AllSites.Write / AllSites.Manage / AllSites.FullControl
Interact with SharePoint App Only Sites.Selected / Sites.Read.All / Sites.ReadWrite.All / Sites.Manage.All / Sites.FullControl .All
Interact with Microsoft Graph Delegate \ App Only Use Get-PnPCommandPermission -CommandName <cmdlet>, -Verbose, or look at the documentation to find the permissions needed
Interact with Power Platform Delegate Azure Service Management\user_impersonation AND Dynamics CRM\user_impersonation AND PowerApps Service\User (the last one you can find on the second tab: APIs that my organization uses)