Here: https://github.com/VladilenK/Manage-m365-with-PowerShell
Tag Archives: Sites.Selected
Token – SharePoint API compatibility matrix
If I get token with (Graph, MSAL, PnP) and use this token for (Graph API, SharePoint CSOM API, SharePoint REST API) matrix.
An App used in this tests has Sites.FullControl.All MS Graph API and SharePoint API permissions, as well as FullControl ACS based permissions to SharePoint (AppInv.aspx).
Token/API | MS Graph /v1.0/sites | SharePoint CSOM PnP.PowerShell Get-PnPSite Get-PnPTenantSite | SharePoint REST API PnP.PowerShell Invoke-PnPSPRestMethod Invoke-RestMethod |
MS Graph /oauth2/v2.0/token secret | OK | (401) Unauthorized | AudienceUriValidationFailedException |
MSAL.PS Get-MsalToken with secret | OK | (401) Unauthorized | AudienceUriValidationFailedException |
MSAL.PS Get-MsalToken with certificate | OK | (401) Unauthorized | AudienceUriValidationFailedException |
PnP.PowerShell Get-PnPAccessToken with Certificate | OK | OK OK | OK AudienceUriValidationFailedException |
PnP.PowerShell Get-PnPGraphAccessToken with Certificate | OK | OK OK | OK AudienceUriValidationFailedException |
PnP.PowerShell Get-PnPAppAuthAccessToken with Certificate or secret | InvalidAuthenticationToken | OK OK | OK OK |
PnP.PowerShell Request-PnPAccessToken with Certificate | InvalidAuthenticationToken | OK OK | OK AudienceUriValidationFailedException |
PnP.PowerShell Request-PnPAccessToken with Secret | InvalidAuthenticationToken | OK OK | OK OK |
Testing Sites.Selected SharePoint and MS Graph API
Sites.Selected MS Graph API permissions were introduced by Microsoft in March 2021. One year later, in 2022 they added SharePoint Sites.Selected API permissions.

Why is this so important? Because MS Graph API for SharePoint is still limited and cannot cover all possible needs. I’d estimate: 90% of applications use SharePoint CSOM, so developers have to use AppInv.aspx to provide permissions for their applications to SharePoint API.
But from this moment – having SharePoint API permissions in MS Graph – in theory – we can fully rely on permissions provided in Azure and – in theory – this should allow us disable SharePoint-Apps only principal:
Set-SPOTenant -DisableCustomAppAuthentication $true
My math professor taught me: “before trying to find a solution – ensure the solution exists.” So let us test:
Are we really able to work with a specific SharePoint site using MS Graph and SharePoint API Sites.Selected permissions provided via Microsoft Azure?
What will happen with our new/legacy applications if we disable SharePoint app-only SPNs (DisableCustomAppAuthentication)?
I’m getting controversial test results… maybe PnP.PowerShell 1.10 is not fully support SharePoint Sites.Selected API.
Tech Wizard (Sukhija Vikas) on March 20, 2022 in the article “SharePoint and Graph API APP only permissions for Selected Sites” suggests using pre-release (AllowPrerelease).
So please ignore the following for a while.
Meantime I’ll test providing SharePoint Sites.Selected API permissions via Graph API call.
(wip) Test set #1: Certificate vs Secret
DisableCustomAppAuthentication: $false (SP-app-only spns are enabled).
All applications have “write” access provided to a specific site only.
Connecting with Connect-PnPOnline and then test access with Get-PnPSite
App / Get-PnPSite | Secret | Certificate |
ACS based (Azure+AppInv) | OK | The remote server returned an error: (401) Unauthorized. |
MS Graph API Sites.Selected | The remote server returned an error: (403) Forbidden. | The remote server returned an error: (401) Unauthorized. |
SharePoint API Sites.Selected | OK | OK |
MS Graph API + SharePoint API Sites.Selected | Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) | OK |
App with no permissions | The remote server returned an error: (403) Forbidden | The remote server returned an error: (401) Unauthorized |
(wip) Test set #2: Sites.Selected SharePoint vs MS Graph (secret)
- DisableCustomAppAuthentication = $false
(SP-app-only spns are enabled). - All applications have “write” access provided to a specific site only.
- Using Client Secret (not a certificate)
- Using PnP.PowerShell
Action/Via | SharePoint + MS Graph Sites.Selected “secret” | SharePoint Sites.Selected “secret” | MS Graph Sites.Selected “secret” |
Connect-PnPOnline | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. |
Get-PnPSite | OK | OK | The remote server returned an error: (403) Forbidden. |
Get-PnPList | OK | OK | |
Get-PnPListItem | OK | OK | |
Set-PnPSite | Attempted to perform an unauthorized operation. | ||
Set-PnPList | Attempted to perform an unauthorized operation. | ||
Set-PnPListItem | OK | OK | |
New-PnPList | Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) | ||
Add-PnPListItem | OK |
(wip) Test set #3: Read vs Write vs FullControl
DisableCustomAppAuthentication = $false
(SP-app-only spns are enabled).
All applications have Sites.Selected SharePoint and MS Graph API permissions.
Using Client Secret (not a certificate)
Using PnP.PowerShell
Read | Write | FullControl | |
Connect-PnPOnline | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. | WARNING: Connecting with Client Secret uses legacy authentication and provides limited functionality. We can for instance not execute requests towards the Microsoft Graph, which limits cmdlets related to Microsoft Teams, Microsoft Planner, Microsoft Flow and Microsoft 365 Groups. |
Get-PnPSite | Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) | ||
Get-PnPList | Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)) | ||
Get-PnPListItem | |||
Set-PnPSite | |||
Set-PnPList | |||
Set-PnPListItem | |||
New-PnPList | |||
Add-PnPListItem |
(wip) Test set #5: Certificate vs Secret
C#, SharePoint CSOM, PnP.Framework
Findings
PnP.PowerShell Get-, Grant-, Set- and Revoke-PnPAzureADAppSitePermission cmdlets require Azure App with MS Graph Sites.FullControl.All app permissions (otherwise it says “Access denied”) and authentication via certificate (otherwise it says “This cmdlet does not work with a ACS based connection towards SharePoint.”)
The same actions – managing permissions for the client app to the specific site collections – could be done via Microsoft Graph Sites Permissions API using just secret-based authentication.
If an azure app does not have Sites.Selected API permissions configured – “Grant-PnPAzureADAppSitePermission” works as expected – no error messages – the output is normal – as if Sites.Selected API permissions were configured in the app. The same for Get-, -Set and Revoke-. Permissions provided for the app to the site are not effective though: Connect-PnPOnline works well, but all other commands – starting from Get-PnPSite – returns “The remote server returned an error: (403) Forbidden.”
If an app have no permissions to SharePoint – “Connect-PnPOnline” works ok, but “Get-PnPSite” return an error: “The remote server returned an error: (403) Forbidden.”
Set-PnPAzureADAppSitePermission gives an error message “code”:”generalException”,”message”:”General exception while processing”
if the site is not specified.
AppInv is not working?
Error: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
References
- Track SharePoint App-only Service Principals in Microsoft 365
- Microsoft Graph Sites Permissions API
- “SharePoint and Graph API APP only permissions for Selected Sites”
Testing environment
- Microsoft 365 E5 Dev environment
- PowerShell 7.2.2
- PnP.PowerShell 1.10
- “write” permissions to the specific sites for client apps were assigned via PnP.PowerShell
Providing Permissions to a Site for Sites.Selected App
How to provide permissions for an Azure registered application with MS Graph SharePoint Sites.Selected API permissions to a specific site via calling Microsoft Graph API from PowerShell.
We need an “admin” application – Azure registered application with with Sites.FullControl.All MS Graph API permissions. This method can use secret, so we need Client Id and Client Secret for this “admin” app.
We also need a Client Id and Application Display Name for an Azure application with Sites.Selected MS Graph and/or SharePoint API permissions provided.
And we need our “target” site Url.
With PowerShell scripts you can:
- Get Microsoft Graph Access Token with an “admin” app
- Get client (target) site Id
- Get current app permissions provided to client site
- Add read or write permissions for the client app to the client site
- Revoke one specific permission from site
- Revoke all app permissions provided to site
– please refer to the GitHub Repo Sites.Selected
References
Sites.Selected SharePoint API
Sites.Selected MS Graph API permissions were introduced by Microsoft in March 2021. It was a good move towards site-level access for non-interactive (daemon) applications, but still developers were limited with only what MS Graph API provides for SharePoint. SharePoint CSOM and REST API still provides much more than MS Graph API.
So developers had to use AppInv.aspx at site level to provide ACS-based permissions to their apps to be able to use SharePoint CSOM and REST APIs. The bad news is ACS-based permissions have some downsides so some SharePoint/m365/security engineers consider them legacy and deprecated. But if we decide to disable SharePoint App-only service principals – all apps with ACS-based permissions provided via AppInv.aspx will stop working.
2021: Microsoft Graph Sites.Selected API

Recently Microsoft introduced Sites.Selected SharePoint API permissions for registered Azure Apps! So from now developers should be fully happy with API permissions provided in Azure (without SharePoint ACS-based permissions).
2022: SharePoint Sites.Selected API

Why is this so important? Because this should allow us to be able to switch from ACS based permissions provided in SharePoint via AppInv.aspx to Azure-provided permissions and as a consequence – disable SharePoint-Apps only principal (‘set-spotenant -DisableCustomAppAuthentication $true’).
Why we are eager to disable Custom App Authentication in SharePoint? Simply say, SharePoint App-only service principals are not trackable (they all appeared as a “spo_service@support.onmicrosoft.com” id in all logs) and hard to manage (there is no way to get list of existing/registered SP app-only service principals, sites and their owners) – see more in this article.
So, SharePoint Sites.Selected application API permissions provided in Azure is a significient step to make Microsoft 365 SharePoint environment more secure and manageble.
More on the Sites.Selected:
- How to provide permissions to a specific site for Sites.Selected app
- Testing Sites.Selected SharePoint and MS-Graph API Apps
References
Connecting to SharePoint Online programmatically: Secret vs Certificate
Update: Sites.Selected API MS Graph permissions was introduced by Microsoft in 2021. It was a good move towards site-level development, but still developers were limited with only what MS Graph API provides for SharePoint dev.
So devs had to use AppInv.aspx at site level to provide ACS permissions to their apps to be able to use SharePoint CSOM and REST APIs.
Recently Microsoft introduced Sites.Selected SharePoint API permissions for registered Azure Apps! So now devs should be fully happy without ACS-based permissions.
Scenario
You have an application that needs access to Microsoft 365 SharePoint Online site/list/documents. Application is running without interaction with users – e.g. unattended, as daemon job.
There are two options you can authenticate to Microsoft 365 – with the secret or with the certificate. Authenticating with certificate is considered more secure.
Questions
- What happens if SharePoint-Apps only principal is disabled
(i.e. ‘set-spotenant -DisableCustomAppAuthentication $true’ )? - Why I’m getting 401 error when authenticating to SPO?
- Why I’m getting 403 error when authenticating to SPO with secret?
- What permissions to I need to work with SPO?
Findings
Note: we will use PowerShell 7.2 and PnP.PowerShell 1.9 to illustrate it.
Disabled SharePoint-Apps only principal
If SharePoint-Apps only principal is disabled in your tenant
(i.e. ‘Get-PnPTenant | select DisableCustomAppAuthentication’ returns $true ), then the only way you work with SPO from code is:
- an App registered in Azure
- API permissions provided via Azure (MS Graph, SharePoint)
- Certificate is used
In all other cases (even your Connect-PnPOnline command complete successfully) – you will be getting error 401 (unauthorized) when trying Get-PnPTenant or Get-PnPTenantSite or Get-PnPSite
Enabled SharePoint-Apps only principal
If SharePoint-Apps only principals are enabled in your tenant
(i.e. ‘Get-PnPTenant | select DisableCustomAppAuthentication’ returns $false ), then you have two options to work with SPO from code:
- Azure App with a secret (Client Id + Client Secret) and permissions to SharePoint provided via SharePoint ( AppInv.aspx )
- Azure App with a certificate (Client Id + Certificate) and permissions provided via Azure (Microsoft Graph and/or SharePoint)
Error 401 while accessing SharePoint Online with PnP
(Get-PnPTenant, Get-PnPTenantSite)
Track SharePoint App-only Service Principals in Microsoft 365
Update (May 2023):
You can use Get-PnPAzureACSPrincipal to returns the lists of all Azure ACS principals installed in your Tenant including subsites.
Scenario
Developers in the organization can use both – Azure Apps and SharePoint Apps to work with SharePoint sites in their “daemon” applications.
It is recommended to use Azure apps so, you want to know – what are SharePoint Apps registered and their owners, who registered SharePoint Apps. Eventually you would disable SharePoint Apps-only principal but before that you’d move Devs from SP-App-only to Azure App (see Disable Custom App Authentication).
(SharePoint App-only service principals aka SP-App-Only are SPN or App registered from within SharePoint using AppRegNew.aspx system page).
One of the approaches is to track Apps/Owners with Unified Audit Log
Use Unified Audit Logs
The following PowerShell code:
$operations = 'Add service principal.'
$recordType = 'AzureActiveDirectory'
Search-UnifiedAuditLog -StartDate $start -EndDate $end -ResultSize $resultSize -Formatted -Operations $operations -RecordType $recordType
returns events with operation = ‘Add service principal.’ Nice, but…
if an app was registered in Azure – event contains an UPN under UserIds property:

Unfortunately, in case with registering app in SharePoint, an audit log event will be like:

i.e. UserId registerd is “spo_service@support.onmicrosoft.com”, so we do not know who registered a SharePoint-only app
In theory – we could use events recorded immediately before and after “Add service principal” event to track a user and site who has registered a SharePoint-only app… But for me it seems like too complicated for automation.

Instead we can do simple search through audit log for events “AppRegNew.aspx page visited”. This gives us a good approximation of who registered SP-App-only principal. Worst scenario – we reach more people than we really need (including those who started registering sp-app-only but did not complete) but all of them would be definitely our target auditory.
Consider the following code:
$freeText = "appregnew" $operations = 'PageViewed' $recordType = 'SharePoint' $results = Search-UnifiedAuditLog -StartDate $start -EndDate $end -ResultSize $resultSize -FreeText $freeText -Operations $operations -RecordType $recordType
this would give you all users who loaded “/_layouts/15/appregnew.aspx” page
Update: Sites.Selected API MS Graph permissions was introduced by Microsoft in 2021. It was a good move towards site-level development, but still developers were limited with only what MS Graph API provides for SharePoint dev.
So devs had to use AppInv.aspx at site level to provide ACS permissions to their apps to be able to use SharePoint CSOM and REST APIs.
Recently Microsoft introduced Sites.Selected SharePoint API permissions for registered Azure Apps! So now devs should be fully happy without ACS-based permissions.
References
- Export all SharePoint App Principals using the Microsoft Graph PowerShell Module
- Use a PowerShell script to search the audit log
- PowerShell samples to get Apps registrations from Unified Audit Log
- Azure App, ACS, SP-App only and Disable Custom App Authentication
- Accessing SharePoint using an application context, also known as app-only
Access SPO Site Programmatically via MS Graph API and SharePoint API
Scenario
You are a software developer. Your company uses Microsoft Office 365 (SharePoint, Teams etc.). The need is to work with a specific site collection programmatically (from code – Python, C#, Java, PowerShell, JavaScript etc.) – e.g. upload/download documents, update list items, search etc.
The code must run without user interaction (unattended, aka daemon app). Sometimes this is also called “SharePoint Automation”.
The solution is based on a new Graph API feature – Sites.Selected and a classic SP-Only app.
Solution
- Register an Azure App and configure it as usual.
Select API Permissions blade and add two permissions:
– Microsoft Graph -> Applications Permissions -> “sites.selected”
– SharePoint -> Applications Permissions -> “sites.selected“ - Request “Grant admin consent” from a tenant/global admin
- Request SharePoint admin to run PowerShell code (e.g. this one) to assign proper permissions to your azure app for a specific site collection (consider site owner consent)
- (optionally) Provide SharePoint API permissions:
(require Site Collection Owner/Admin account) – use
https://YourTenant.sharepoint.com/teams/YourSite/_layouts/15/appinv.aspx
to add SharePoint API permissions to your app. E.g. full control permissions to site collection would be
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection"
Right="FullControl" />
</AppPermissionRequests>
Consider minimal permissions (e.g. as per Sumit)
Problem Solved
- you get access to one and only one site collection (“least privilege” principal)
- you get both – SharePoint API and Microsoft Graph API permissions to SharePoint
- you can use app secret or certificate to authenticate – depending on what are your security requirements
Note: if your scenario require authenticated user present – the solution would be a little different: Connect-PnPOnline Interactive with Client App Id
Update:
Sites.Selected API MS Graph permissions was introduced by Microsoft in 2021. It was a huge step forward, but still devs were limited with MS Graph API against SharePoint.
So devs had to use AppInv at site level to provide ACS permissions to their apps to use SharePoint CSOM and REST APIs.
Recently Microsoft introduced Sites.Selected SharePoint API permissions for registered Azure Apps! So now devs should be fully happy without ACS-based permissions AppInv.aspx. (See more here on disabling SP Apps Only SPNs)
Thanks to Leon Armston and Scott Murdock
References:
- Register an application with the Microsoft identity platform
- Controlling app access on a specific SharePoint site collections is now available in Microsoft Graph
- Working with SharePoint sites in Microsoft Graph
- SharePoint Add-In — Permission XML cheat sheet
- Accessing SharePoint using an application context, also known as app-only
- Connect-PnPOnline Interactive with Client App Id
- Disable Custom App Authentication