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.
After many years working with SharePoint I wrote a lot of PowerShell scripts that help me support, troubleshoot, administer and secure SharePoint. So I’m sharing my scripts with you.
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:
Assuming 2 resource units per request is a safe bet.
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).
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:
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))
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
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 “app@sharepoint.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. (Update: there are tools – check Azure ACS retirement: Track down ACS apps).
So, SharePoint Sites.Selected application API permissions provided in Azure is a significant step to make Microsoft 365 SharePoint environment more secure and manageable.
2024: Delegated Sites.Selected Permissions
Since Feb 2024 Microsoft supports also delegated Sites.Selected permissions. Delegated Sites.Selected permissions are assigned the same way as application Sites.selected permissions – through the /permissions endpoint. You still assign only the application id and role. When the call is made the permissions are calculated either as application or delegated, and assuming the request is authorized it will go through.
Microsoft implemented granular permissions ( e.g. to a list, item or file) alongside with Sites.Selected permissions. Original implementations of Sites.Selected allowed access to entire site collection only. With new ‘Lists.SelectedOperations.Selected’, ‘ListItems.SelectedOperations.Selected’ and ‘Files.SelectedOperations.Selected’ permissions it is possible to provide application permissions to list, library or list item or particular document (reference).
Details To Be Provided…
Delegated Sites.Selected API permissions
Since the beginning of 2024 Microsoft supports Delegated Sites.Selected API permissions. This is to support security best practices – the minimally possible access should be provided. I.e. the idea is: even Sites.FullControl.All delegated permissions allows access not to all sites, but to sites current user was provided access to, it would be a good idea to restrict access with only sites this specific app is required access to. That’s good.