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:
- Microsoft Graph: Testing out the new Microsoft Graph SharePoint (specific site collection) app permissions with PnP PowerShell
- SharePoint: PnP PowerShell/CSOM Now Works With SharePoint Sites.Selected Permission using Azure AD App
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.

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).
Summary
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?
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.
Hi
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!
Is it possible to achieve the above in C#?
Hi Krishnan
Yes it is, it is just making a request via Microsoft Graph to a endpoint (see link for C# instructions)
https://docs.microsoft.com/en-gb/graph/api/site-update-permission?view=graph-rest-1.0&tabs=csharp
Replace “read” with “fullcontrol”
Thanks
Leon
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
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
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?
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)
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
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!
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
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.
Thanks
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
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.
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.
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 + “‘” + “)”));
taskGetFileInfo.Wait();
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));
taskDownloadFile.Wait();
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
Jes
Excellent Jes
Glad you were able to get it working with REST and thanks for posting your solution to help any others!
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 !
It’s out of preview and GA, so yes, this is ready for business use
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.
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?
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.
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.
Good to hear you resolved the issue!
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?
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.
Thanks
Leon
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..
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!
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?
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.
Thanks
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”?
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”.
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”.
Hi Franck
Sorry for the late reply but loving your research and testing! This is handy to know!
Thanks for testing
Leon
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.
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.
Regards
Leon
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?
Do you know if Sites.Selected works for allowing searching within the SPO Site?
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.
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?
Hi,
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
Tomas