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 16 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#?

  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 !

  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?

Leave a Reply