TBP
Category Archives: Microsoft Graph
SharePoint Sites Lookup
That’s a very common problem in SharePoint world. You are looking for a site owner but there is no tool available for regular user to find who owns the site.
Scenarios.
You get a link to some SharePoint site, but you do not have access to it. You requested access but nobody has responded. You need to find who is the site owner.
(To be continued)
PowerShell scripts for Microsoft 365 SharePoint
Microsoft 365 SharePoint: prevent throttling with RateLimit headers
Bert Jansen (Microsoft) revealed some details on throttling when you access Microsoft 365 programmatically – via Microsoft Graph or CSOM and guided developers on how to regulate request traffic for optimized throughput using RateLimit headers (Here).
Demystifying SharePoint throttling
Throttling is necessary to ensure that no single user or application consumes too many resources compromising the stability of the entire system, which is used by many clients.
Throttling happens at
- User (there are user request limits. Microsoft counts all requests linked to user
- Application (Delegated or Application permissions)
- Resource units per app per minute
- Resource units per app per day
- Farm – Spike protection
Very common reason for throttling – when an Application (Delegated or Application permissions) reaches “Resource units per app per minute” threshold.
Usually you catch HTTP errors 429 or 503, wait for some time (respect Retry-after header) and try again.
SharePoint provides various APIs. Different APIs have different costs depending on the complexity of the API, but Microsoft favor Graph API over SharePoint REST/CSOM. The cost of APIs is normalized by SharePoint and expressed by resource units. Application’s limits are also defined using resource units.
Quota depends on tenant size.
Resource unit limits for an application in a tenant (please refer to the Microsoft article)

Predefined costs for Microsoft Graph calls:

Links
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 permissions to SharePoint Admin Site
WIP
What if we provide Sites.Selected App permissions to SharePoint Admin Site?
TBP