Over the past year Microsoft have released Sites.Selected permissions for both Microsoft Graph & SharePoint which can be given to an Azure AD App (App Registration). When created the app by default with Sites.Selected permissions does not have access to any SharePoint sites and has to be explicitly added added using Microsoft Graph or PnP PowerShell (Grant-PnPAzureADAppSitePermission) to the site(s) to be administered. Both processes only give the access Read or Write permissions to the site and not Full Control.

See my earlier blogs on Sites.Selected for Microsoft Graph & SharePoint:

I did some testing of the permissions and the Write permission is unfortunately more like a standard Contribute permission in a SharePoint site i.e. you can add content to libraries but not create any new libraries or make changes to the structure of existing libraries. What I wanted was a permission object for the application on the site to have FullControl and be able to do everything in the site i.e. create, read, update and delete.

Adding a New Document Library with Sites.Selected Write permission – see error

When a app has Sites.Selected Full Control permission it is possible to add a new list etc and have permissions the same as SharePoint Full Control permission level.

Guide: How to add an Azure AD app with SharePoint Sites.Selected permission to sites with Full Control

I will now show you below with a script how you can assign an existing Azure AD App (to create a new Azure AD App use Register-PnPAzureADApp) with SharePoint Sites.Selected permissions to a Site with Full Control using PnP PowerShell. The same approach could be used for Microsoft Graph Sites.Selected permissions also except line 22 (New-PnPList) will not work as most PnP PowerShell cmdlets use CSOM behind the scenes and not Microsoft Graph. So you will then need to use Microsoft Graph calls for administering SharePoint rather then PnP cmdlets.

The trick with the script below is that in Microsoft Graph it is not currently possible to create a permission object in a site with Full Control permissions – only Read & Write. So you first have to create a permission object to a Site with Read/Write Permissions using Grant-PnPAzureADAppSitePermission (line 12). Next get the Permission ID of the permission object just created by using Get-PnPAzureADAppSitePermission (line 15). Then use Set-PnPAzureADAppSitePermission to upgrade the permission object from Read/Write permissions to FullControl (line 18).

Finally connect using your SharePoint Sites.Selected app (line 21) and test that full control permissions have been applied by creating a new list in the site (line 24).


I hope this is helpful for some people – I’m not sure if this is by design (don’t think so) or a bug in MS Graph that needs to be fixed to allow permission objects to be created with Full Control rather than just Read/Write. Hopefully it will be sorted soon – in the meantime the script above will help me with the workaround and hopefully others with Sites.Selected permissions for Full Control.

Let me know if you have any thoughts or comments if you found this helpful?

This Post Has 43 Comments

  1. Aurora

    Thank you! This really helped me out. It’s very weird that you can’t grant the permission directly, but I’m glad there’s a workaround. Considering that this feature has been available for a while I guess they don’t consider it a bug.

    1. Leon Armston


      Glad to help out! Agree it seems weird you can’t grant the permissions directly so through a bit of trial and error I was able to figure out this workaround – so had to document it so I dont forget ha!

  2. Krishnan

    Is it possible to achieve the above in C#?

    1. Juan

      Hello Krishnan,
      Were you able to achieve the above in C#? I am able to read/write using PowerShell successfully with Microsoft new feature Sites.Selected using two AAD registered apps (Admin and Client).
      I would like to do the same in a C# program but unfortunately get access denied. I am using the demo program provided by this short youtube video with an example of the new Sites.Selected:
      “Episode #200 – Sites.Selected Application permission for Graph and SharePoint APIs”
      Perhaps you or someone else can figure this out. The demo C# program provides two flavors with the Sites.Selected Microsoft feature: MS Graph (no certificates) and SharePoint CSOM (with certificates). Thanks, Juan

      1. Juan

        Hello Leon,
        After using your trick with your script, I was able to successfully run demo C# program of “Episode #200 – Sites.Selected Application permission for Graph and SharePoint APIs”. Thank you very much for sharing your recipe!

        Respectfully, Juan

        1. Chinthaka.

          Hi All,
          I’m getting following error when I’m trying to Grant-PnPAzureADAppSitePermission with Azure Managed Identity Graph & SharePoint sites.fullcontrol.all.

          Following is the exception we get.

          ERROR: {“error”:{“code”:”accessDenied”,”message”:”Access denied”,”innerError”:{“date”:”2023-08-15T07:12:36″,”request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”,”client-request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”}}}
          Exception : Type : System.Management.Automation.PSInvalidOperationException ErrorRecord :
          Exception : Type : System.Management.Automation.ParentContainsErrorRecordException Message : {“error”:{“code”:”accessDenied”,”message”:”Access denied”,”innerError”:{“date”:”2023-08-15T07:12:36″,”request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”,”client-request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”}}} HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException FullyQualifiedErrorId : InvalidOperation TargetSite : Name : ProcessRecord DeclaringType : PnP.PowerShell.Commands.Base.PnPConnectedCmdlet MemberType : Method Module : PnP.PowerShell.dll

          Message : {“error”:{“code”:”accessDenied”,”message”:”Access denied”,”innerError”:{“date”:”2023-08-15T07:12:36″,”request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”,”client-request-id”:”0fea12dd-b38b-4d59-8f50-f9ea21561aaa”}}} Source : PnP.PowerShell HResult : -2146233079 StackTrace : at PnP.PowerShell.Commands.Base.PnPConnectedCmdlet.ProcessRecord() in c:\build\src\Commands\Base\PnPConnectedCmdlet.cs:line 101 at System.Management.Automation.Cmdlet.DoProcessRecord() at System.Management.Automation.CommandProcessor.ProcessRecord() CategoryInfo : InvalidOperation: (:) [Grant-PnPAzureADAppSitePermission], PSInvalidOperationException FullyQualifiedErrorId : InvalidOperation,PnP.PowerShell.Commands.Apps.GrantPnPAzureADAppSitePermission InvocationInfo : MyCommand : Grant-PnPAzureADAppSitePermission ScriptLineNumber : 90 OffsetInLine : 1 HistoryId : 1 ScriptName : C:\home\site\wwwroot\ManagedID_TEST\run.ps1 Line : Grant-PnPAzureADAppSitePermission -Permissions “Write” -Site $siteUrl -AppId $clientid -DisplayName $displayName -Connection $Connection PositionMessage : At C:\home\site\wwwroot\ManagedID_TEST\run.ps1:90 char:1 + Grant-PnPAzureADAppSitePermission -Permissions “Write” -Site $siteUrl … +

          Anyone knows why we getting this error?

  3. Jes Kirkup

    Great post, very useful. One thing would be good to know is where can you go to find which sites have “Full Control” Sites.Selected permissions vs Read, Write etc. (or none)

  4. Jes Kirkup

    What I mean is whether there is a way to view them in any of the portals or is the only option to enumerate them via Get-PnPAzureADAppSitePermission -Site XYZ

    1. Leon Armston

      Hi Jes

      Thanks for your comment

      I believe the only way is to go through everysite and run Get-PnPAzureADAppSitePermission. There is nothing in the UI yet to show these app permissions. Hopefully soon!

  5. Krishnan

    Hi Leon,
    I am getting below error after connecting to connect-pnponline using certificate.
    get-pnpweb : The remote server returned an error: (401) Unauthorized.

    My question is it mandatory to have both Sites.Selected in both Graph API and SharePoint API?

    At the moment, i have only Sites.Selected Graph API. Thanks in advance

    1. Leon Armston

      Hi Krishan

      PnP PowerShell cmdlets for SharePoint i.e. Get-PnPWeb behind the scenes uses CSOM so will not work with Graph Permissions. You need SharePoint Sites.Selected permissions to run PnP PowerShell which does allow CSOM to be executed.


  6. Jes

    Hi Leon,

    After doing some further investigations I am using PnP.Framework in a .NET Framework 4.8 console app to download a file from a site that I have configured “Read” access via Sites.Selected.

    Authenticating via AuthenticationManager as follows (using a cerificate uploaded to the Azure portal)

    AuthenticationManager auth = new AuthenticationManager(clientId, certPath, certPassword, tenantId);

    This works and I can obtain a client context

    using (var clientContext = auth.GetContext(siteUrl))

    from there able to e.g. enumerate properties

    e.g. clientContext.Web.Title

    However I get a 401 when trying to use OpenBinaryDirect via the client context.

    I had assumed “Read” access with “Sites.Selected ” would allow me to read any file in the Documents library.

    Uri fileUrl = new Uri($@”{ConfigurationManager.AppSettings[“SiteUrl”]}/Documents/{fileName}”);
    Microsoft.SharePoint.Client.FileInformation spFile = Microsoft.SharePoint.Client.File.OpenBinaryDirect(clientContext, fileUrl.AbsolutePath);

    Any idea what might be the issue here?

    Many thanks


  7. Jes

    One more thing to mention here is that using PnP.PowerShell with the same certificate credentials passed in Connect-PnPOnline and downloading the file using Get-PnPFile works fine. It is the CSOM approach that is failing.

    1. Leon Armston

      Hi Jes

      Thanks for your comments

      I’m not 100% sure if it’s working in PnP PowerShell but not via your console app using PnP.Framework – maybe one to post on the discussions of the PnP Framework GitHUb repo https://github.com/pnp/pnpframework/discussions is my suggestion.

      Sorry I cannot help you on this one – PowerShell is probably the extent of my coding skils.

  8. Jes

    Hi Leon, thanks very much for your reply and rest assured that you have been a great help already :-).

    In case anyone else hits the same problem I have a solution. I needed to replace some CSOM code that was downloading a file using OpenBinaryDirect which was working with Basic Authentication. After disabling authentication (i.e. Basic) I needed to switch to certificate based and setting up the Sites.Selected to give minimal permissions to the site collection (“read”) to allow this as per your article. So I found that I could connect to the target site collection via CSOM but attempting to use the client context and call OpenBinaryDirect then resulted in a 401 error.

    As I mentioned, I was able to use PnP.PowerShell Get-PnPFile to download the file using the certificate based authentication, so I checked with fiddler to see what HTTP traffic was going on behind the scenes. It was enumerating the file properties to obtain the UniqueId of the file and then call the _layouts/15/download.aspx passing the id as a parameter and streaming the file to the client.

    Using the PnP.Framework nuget package auth methods you can obtain the bearer token that you need to pass and then you can use the httpClient to call the download.aspx page (after obtaining the Unique ID from a call to the REST /_api/web/getFileByServerRelativeUrl which takes a relative document library path).

    Here is the working code:

    // Method uses the physical certificate and password to obtain access token
    AuthenticationManager auth = new AuthenticationManager(clientId, localCertificatePath, certPassword, tenantId);

    // Get the Unique ID via REST
    string accessToken = auth.GetAccessToken(siteUrl);
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Add(“Authorization”, “Bearer ” + accessToken);
    client.DefaultRequestHeaders.Add(“Accept”, “application/json;odata=verbose”);

    var taskGetFileInfo = Task.Run(() => client.GetAsync(siteUrl + “/_api/web/getFileByServerRelativeUrl(” + “‘” + doclibRelativePath + “‘” + “)”));
    var json = taskGetFileInfo.Result.Content.ReadAsStringAsync();
    var SPFileRestResponse = JsonConvert.DeserializeObject(json.Result);

    // Download the file via REST
    var uri = new Uri(siteUrl + “/_layouts/15/download.aspx?UniqueId=” + SPFileRestResponse.d.UniqueId);
    var taskDownloadFile = Task.Run(() => client.GetByteArrayAsync(uri));
    var response = taskDownloadFile.Result;
    System.IO.File.WriteAllBytes(@”c:\temp\targetfile.docx”, response);

    Hopefully the above is legible and may be helpful to anyone else who encounters the issue.

    Seems like REST is more reliable than CSOM at present 🙂

    Thanks again for your blog, it definitely has saved me some time and put me on the right track.

    Kind regards


    1. Leon Armston

      Excellent Jes

      Glad you were able to get it working with REST and thanks for posting your solution to help any others!

  9. Bhasker

    Hi….can someone please confirm if sites.selected is ready for business use (i.e, is in full production) or is still in beta/early release?

    Thanks !

    1. Robin Meure

      It’s out of preview and GA, so yes, this is ready for business use

      1. Leon Armston

        Correct. This is ready for business use. Just annoying you have to first create the permission object with read or write before you can change it to full control.

  10. Chris

    Hi Leon,

    My output from script:

    Id : xxx
    Roles : {fullcontrol}
    Apps : {, xxx}

    New-PnPList : The remote server returned an error: (401) Unauthorized.
    At script.ps1:24 char:1
    + New-PnPList -Title “Full Control List” -Template DocumentLibrary
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : WriteError: (:) [New-PnPList], WebException
    + FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.Lists.NewList

    Running Get-PnPAzureADAppSitePermission -site xxx returns:

    Get-PnPAzureADAppSitePermission -site $siteUrl

    Id : xxx
    Roles :
    Apps : {SitesResourceSpecific, xxx}

    What version of pnp.powershell did you use when creating the article?

    1. Leon Armston

      Hi Chris

      Looks like you dont have permission (Roles) to create a new list i.e. you do not have full control permissions. When you ran Get-PnPAzureADAppSitePermission in the output Roles was empty when it should say Read, Write or Full Control.

      1. Chris

        Thanks Leon, I found my problem with it not working.
        I had assigned the API permission sites.selected under Graph, only when I switched it to sites.selected under SharePoint did it work.

  11. vinay

    Hi Leon. Great post. Is there a way to find to which sites an Azure AD app has access? For example using Sites.Selected permissions if i have given full control permissions to couple of sites for the Azure AD App, how can i get the list of those sites that an app has access to?

    1. Leon Armston

      Hi Vinay

      As far as I’m aware the only way would be create a loop to connect to every site and run Get-PnPAzureADAppSitePermission and then add this all to an array. Then filter the array of all the site permission object with the app id.



      1. vinay

        Thanks a lot Leon. Seems to be a long running script if the sites are close to 50 grand atleast there is a workaround. Will try and let you know how it goes.. Appreciated..

        1. Adrian Corston

          Sadly this doesn’t work. When the app only has Sites.Selected permission the Graph API endpoint GET /v1.0/sites doesn’t return a list of any sites at all. The app can access the sites that it has read/write/fullcontrol permissions to, but only if it already knows the Site ID.

          That’s two head-scratching days of my life that I’ll never get back… so hopefully this helps someone else!

  12. Mallikarjun

    Hi Leon, Very informative post. Couple of questions.
    1. Is basic authentication for SharePoint online specifically getting deprecated by some date? Like exchange online https://docs.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/deprecation-of-basic-authentication-exchange-online mentioned in this link.
    2. Lets say i am executing CAML query using CSOM with user provided credentials from console, which will return only those items on which user has access to. Now If i have to use this Modern Authentication and Application permission then all items are returned in the result. How to pass user context so that items are filtered based on provided user credentials?

    1. Leon Armston

      Hi Mallikarjun

      Thanks for your comment – much appreciated.

      1. You should be really using Modern Authentication or even better via a security principal (app registration/enterprise application) to authenticate to SharePoint. I’ve not used basic authentication for years for SharePoint – so not 100% sure if you can use it anymore?
      2. Sites.Selected permissions can only be done at the application level i.e. the application has access to everything on the site. There is no delegated level (user) for Sites.Selected. I THINK for your scenario you would need to use Modern Auth to show only items the user can see.


  13. Franck

    Hi Leon,
    First thank you for the article as it really helped.
    Since Sites.Selected has now “full control” on a given site, I expected that the app would be able to create local term sets (New-PnPTermSet) and terms (New-PnPTerm) in the provisioned site collection term group but it always returns “The current user has insufficient permissions to perform this operation.”
    Is this a bug in PnP.Powershell or “by design”?

    1. Franck

      Hi Leon,
      Forget my previous message as it’s working now. I don’t why it didn’t work at first shot but I retried on several SPO sites and, once the site coll local term group is created, the app can create new local term sets with only the “Sites.Selected” app permission with “full control”.

      1. Franck

        Sorry again but my previous message was not correct (unfortunately I cannot delete my previous messages).
        To be able to write to the site coll local term group in “app only” mode using “Sites.Selected”, you must also grant your app with the “TermStore.ReadWrite.All” app permission. Although it might look as too permissive (all term groups of all site coll), it will be limited to the local term group of the SPO site(s) configured with “Sites.Selected”.

        1. Leon Armston

          Hi Franck

          Sorry for the late reply but loving your research and testing! This is handy to know!

          Thanks for testing


  14. Westley

    This permission is being considered for the future, but the name of the permission may change to ‘Owner’, since it is not officially supported or documented yet.

    1. Leon Armston

      Hi Westley thanks for your comment.

      This is great to get some direction from Microsoft re the permission. Owner would make sense as FullControl has the same permissions as the owners group in SharePoint. Write could also possibly be changed to contribute to reflect the SharePoint permission role it closely matches.



  15. Joseph

    Hi Leon, I have a question for you. In your script I see the field from certthumbprint. This app was created by a user in the org and does not, to my knowledge, have an SSL/TLS cert connected to it. I tried to go to Azure to find the app in App registrations, but it is not in “All Applications” . I also went to the Azure Portal and tried to use App Services, but we do not seem to have permissions or a license ( I am a global admin and it still tells me to buy a license).

    Is the certthumbprint required, or do I only need to put it on there if I have associated an SSL/TLS cert for the application?

  16. Jon

    Do you know if Sites.Selected works for allowing searching within the SPO Site?

  17. Bilal Khan

    I am trying to make a connection using client ID and client secret. I can make a connection to SPO site however when i run get-pnpsite it gives below error

    Get-PnPSite : The remote server returned an error: (401) Unauthorized.

    If i use client ID and thumbprint for same application it works. Not sure why it is not working with client ID and app secret.

  18. Markus

    Hi Leon,
    thanks for this post. What I understand is, that sites.selected is only available for Application Permissions. Is that right? If yes, do you know is this will come for Delegated Permissions as well?

  19. Tomas Olejnik


    I have discovered that now it’s suddenly possible to add FullControl directly without the procedure described above, just like this:
    Grant-PnPAzureADAppSitePermission -AppId ’14d7fd0e-a5e0-4c8b-a463-338d5d4b927c’ -DisplayName ‘SitesSelectedTest’ -Site ‘https://crm123456.sharepoint.com/sites/site1’ -Permissions FullControl

    Does anyone have some information or link to official documentation when this has been introduced?

    Thank you

  20. Sebastian

    Even when your app has fullcontrol permission on site, it’s not unauthorized to use “Get-PnPApp” 🙁

    1. Sebastian

      sorry, I made mistake 😀 It works 😉

Leave a Reply