An integration needs to read or write one SharePoint site. The application registration arrives with a request for Sites.Read.All, or Sites.FullControl.All, and somebody has been asked, reasonably, not to approve a permission that covers every site collection in the tenant for the sake of one.

Microsoft Graph has a permission for this case, Sites.Selected, and it behaves differently from every other Sites.* permission. Consenting to Sites.Selected grants access to nothing: access exists only when the consent, a per-site grant created by a separate and highly privileged identity, and a token carrying the scope are all present. The rest of this article is the detail of those three parts, taken from the documentation as read on 12 November 2026. The calls and cmdlets below are taken from that documentation; we did not run them against a tenant for this article.

Scope: SharePoint Online and Microsoft Graph

Everything here concerns SharePoint Online reached through Microsoft Graph. The permissions reference describes the application permission as "Allow the application to access a subset of site collections without a signed in user. The specific site collections and the permissions granted will be configured in SharePoint Online."

None of the pages read for this article describes Sites.Selected for SharePoint Server Subscription Edition. We therefore make no statement about on-premises farms: the article covers the cloud service only.

The overview of Selected permissions is explicit. For other scopes, "as soon as the scope is consented, the application can access the resources it represents". Selected scopes "require an explicit assignment action", and an application consented for one "would initially have no access".

The same page lists three steps, written for lists but stated to apply to every Selected scope: the application is consented in Entra ID, the application is granted a role on the resource through the permissions endpoint, and the application acquires a token that contains the scope. "If any of the three steps are missed, the application doesn't have access."

That gives an administrator two independent controls, also named in the overview: delete the grant on the resource, or revoke the consent in Entra ID. Either one removes access.

Who can create the per-site grant

The application that holds Sites.Selected cannot grant itself a site. The overview's table of management requirements lists a single entry for a site, Sites.FullControl.All, with the note: "Because you can grant full control permissions to a site collection by using Sites.Selected, this requirement is necessarily high."

The Create permission reference agrees: Sites.FullControl.All is the least privileged permission for both delegated and application calls. For delegated calls it adds: "the user must have an administrator role, such as SharePoint Administrator or higher. In addition, the delegated user running the API must be a member of the target SharePoint site."

In practice this means two identities. One, used by an administrator and kept away from the integration, holds Sites.FullControl.All and creates grants. The other is the integration, which holds only Sites.Selected.

Creating, listing, changing and removing the grant

The grant is addressed by site ID. If only the URL is known, Get a site resource documents the path form, and the id property of the response is the value to use:

Text
GET https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com:/sites/projects

Create the grant with the administrative identity. The id under application is the client ID of the application that holds Sites.Selected:

Text
POST https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
Content-Type: application/json
JSON
{
  "roles": ["read"],
  "grantedToIdentities": [
    {
      "application": {
        "id": "00000000-0000-0000-0000-000000000000",
        "displayName": "Contoso Projects Reader"
      }
    }
  ]
}

The reference states that a successful call returns 201 Created and a permission object. Its id is what the later calls need. The page also notes that the method creates application permissions only: "you can't use it to create a new user site permission".

The remaining operations are documented on List permissions, Update permission and Delete permission:

Text
GET    https://graph.microsoft.com/v1.0/sites/{site-id}/permissions
PATCH  https://graph.microsoft.com/v1.0/sites/{site-id}/permissions/{permission-id}
DELETE https://graph.microsoft.com/v1.0/sites/{site-id}/permissions/{permission-id}

The PATCH body carries only the new role, for example { "roles": ["write"] }, and returns 200 OK. DELETE returns 204 No Content. All three require Sites.FullControl.All. The Update and Delete pages mark delegated access as "Not supported", so by those tables they are application-only calls, while Create and List accept both.

The roles, and where the documentation disagrees

The role names are not listed identically in the sources we read, which matters when a call is rejected for an unknown role.

SourceRoles listed
Overview of Selected permissionsread, write, owner, fullcontrol
Resource Specific Consent articleread, write, manage, fullcontrol
PnP PowerShell -PermissionsRead, Write, Manage, FullControl

read is described as "Read the metadata and contents of the resource", write as "Read and modify the metadata and contents of the resource". The overview describes owner only as "Represents the owner role", and the Resource Specific Consent article describes manage as adding the ability "to manage the site". We could not establish from documentation whether owner and manage are the same role under two names, so read, write and fullcontrol are the three values every source agrees on.

The same operation with PnP PowerShell

The current PnP PowerShell documentation confirms four cmdlets. The older Grant-PnPAzureADAppSitePermission name is documented as an alias of the first.

PowerShell
# Connect as an administrator. The client ID is the app registration
# PnP PowerShell signs in with, not the application receiving access.
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/projects" -Interactive -ClientId "11111111-2222-3333-4444-555555555555"

# Create the grant for the application that holds Sites.Selected.
Grant-PnPEntraIDAppSitePermission -AppId "00000000-0000-0000-0000-000000000000" -DisplayName "Contoso Projects Reader" -Permissions Read -Site "https://contoso.sharepoint.com/sites/projects"

# List the grants on the site, then read one in full.
Get-PnPEntraIDAppSitePermission -Site "https://contoso.sharepoint.com/sites/projects"
Get-PnPEntraIDAppSitePermission -PermissionId "<permission-id>"

# Change the role, or remove the grant.
Set-PnPEntraIDAppSitePermission -PermissionId "<permission-id>" -Permissions Write
Revoke-PnPEntraIDAppSitePermission -PermissionId "<permission-id>"

Three details from those pages are worth carrying. Grant-PnPEntraIDAppSitePermission requires Sites.FullControl.All and states that it "only works with delegated permissions, not with app permissions". Its -DisplayName is "Only for visual reference purposes, does not need to match the name of the application in Entra ID". And Get-PnPEntraIDAppSitePermission warns that without -PermissionId "the Roles property is not populated. This is a current API limitation", so an empty role in a listing is not evidence of a broken grant. The same page says that naming a site with -Site requires the SharePoint Administrator role.

The PnP pages for Set and Revoke list Sites.FullControl.All without saying which token type they need, while the Graph reference marks update and delete as application-only. The sources do not settle this between them, and we did not run it.

Narrower than a site: lists, items and files

The overview lists three further scopes: Lists.SelectedOperations.Selected for a specific list, ListItems.SelectedOperations.Selected for list items, files or folders, and Files.SelectedOperations.Selected for files or library folders. It adds that there is "no functional difference" between that naming format and Sites.Selected.

The mechanism is the same three steps against a deeper permissions endpoint, for example POST /sites/{siteid}/lists/{listid}/permissions. Create permission on a list shows the body using grantedToV2 with a single application object rather than grantedToIdentities.

Two documented consequences apply only below site level. Granting on a list, item, folder or file "breaks inheritance on the assigned resource", so the service limit on unique permissions applies, whereas a site-level grant does not break inheritance. And management can be delegated downwards: the overview's table allows an application holding Sites.Selected with the fullcontrol or owner role on a site to manage list permissions inside it.

Application and delegated behaviour

The overview states that "all Selected scopes now support delegated and application modes". For an application token, access is granted when a record for the application exists on the resource or a parent and the role allows the operation. For a delegated token, "both the application and user permissions are calculated and then intersected", so the application cannot exceed the user and the user cannot exceed the application's grant. The same page recommends delegated where possible.

The permissions reference marks admin consent as required for the application permission and not required for the delegated one.

Configurations that end in access denied

The documentation gives the outcome for a site without a grant: the Resource Specific Consent article says the code "will fail and you'll see an "Access denied" exception". From the pages above, the documented conditions under which the application has no access, or the grant cannot be created in the first place, are:

  • consent given, no grant created on that site;
  • grant created, but the token does not carry the Selected scope ("without the proper scopes appearing in the token, the application doesn't have access");
  • the grant created with an identity lacking Sites.FullControl.All, or, when delegated, by a user who is not an administrator and a member of the target site;
  • consent revoked in Entra ID, after which "Application access is lost as soon as a scope is revoked";
  • a list grant deleted, which removes access to the list "and all items within that list, regardless of any explicit permissions set on those list items".

Limitations

  • Tenant-wide endpoints. The permission tables for List sites and for the search query API name Sites.Read.All and broader permissions for application access. Sites.Selected is not listed on either, so an application holding only that scope should be given site IDs rather than be expected to discover them.
  • Subsites. The List permissions page states that retrieving subsite permissions with that method "isn't supported at this time".
  • Lower scopes do not reach upwards. "Lower scopes can never provide access to higher-level resources."
  • SharePoint REST is a separate consent. The Resource Specific Consent article shows a distinct Sites.Selected application permission under SharePoint Online for applications that call SharePoint REST instead of Graph.
  • Per-API permission tables do not mention it. The table on Get a site resource lists only Sites.Read.All and Sites.ReadWrite.All. The overview is the page that defines how Selected scopes are evaluated.

Finally, Sites.Selected narrows which sites an application reaches, and the role decides what it may do there. A fullcontrol grant on one site is still full control of that site.

Tags#sharepoint#graph#security#identity#powershell#havecode

Support independent engineering knowledge

Knowledge, Analysis, the Field Guide and the tools are free to read and use, and they stay that way. If this article saved you time, you can support the research and writing behind it. Support is voluntary and unlocks nothing.

Buy me a coffee (opens in a new tab)João Lívio's page on buymeacoffee.com

Comments