As SharePoint or Teams admin you manage Microsoft 365 groups (create, update, delete, manage membership etc.) having your admin role activated. I prefer PowerShell 7 and Microsoft.Graph PowerShell module, and I need an Azure registered app with “Group.ReadWrite.All” Microsoft Graph API delegated permission.
Some findings:
If a user was not a group member or group owner – and the user is added to the group members – this user will get notification “You’ve joined the <Group Name> group” via e-mail that comes from a group e-mail address.
When a user is added to the group owners (or elevated to group owner if user was a group member) – user does not get notification.
When a user was a group owner and now you are adding this user to the group members – user does not get notification.
All the actions are logged into Microsoft 365 audit log under your personal Id.
Script samples:
# This script is just a sample to demonstrate basic technique on getting, updating groups membership and deletion m365 groups with PowerShell and MS Graph
#
# please do not run this script as is, but update it based on your needs
# authentication with personal Id
# app must have as minimum "Group.ReadWrite.All" Microsoft Graph API delegated permission
# user must have SharePoint admin (or Teams admin) roles activated
Connect-MgGraph -ClientId $clientid -TenantId $tenantId
Get-MgContext | Select-Object Scopes -ExpandProperty Scopes
# sample data
$groups = @()
$groups += [PSCustomObject]@{GroupId = '443d22ae-683a-4fe4-8875-7bd78227a026' }
$groups += [PSCustomObject]@{GroupId = 'e5805388-c18c-48c0-b42d-6223cf8f3d82' }
# Get Groups
foreach ($group in $groups) {
Get-MgGroup -GroupId $group.GroupId
}
# add members to the group
$groupId = '443d22ae-683a-4fe4-8875-7bd78227a026'
$userId = 'df74e0d3-d78c-495b-b47a-549437d93cf7' # Adele
New-MgGroupMember -GroupId $groupId -DirectoryObjectId $userId
# add Owner to the group
$groupId = '443d22ae-683a-4fe4-8875-7bd78227a026'
$userId = 'eacd52fb-5ae0-45ec-9d17-5ded9a0b9756' # Megan
New-MgGroupOwner -GroupId $groupId -DirectoryObjectId $userId
# Delete group
$groupId = '443d22ae-683a-4fe4-8875-7bd78227a026'
Remove-MgGroup -GroupId $groupId
Sites.Selected permissions are required to get access to only one or a few specific SharePoint sites using Microsoft Graph API and/or SharePoint API (since Microsoft announced the EOL of SharePoint App-only service principals, Sites.Selected is the only option going forward). Below are
Historically, we utilized so-called SharePoint app-only service principals to get unattended (daemon/service) access to one specific site programmatically. Initially in on-premises, later in SPO. We used to get SharePoint app-only service principals via appregnew.aspx and provide permissions via the appinv.aspx page. SharePoint app-only principals allow calls to SharePoint (REST) API and usage of SharePoint CSOM via ACS-based authentication.
Then Microsoft started developing Graph API. You’d need to register your app in Azure to get App ID and App Secret to authenticate to Microsoft Graph API. You’d also configure specific API permissions for this app to get access to services you need (SharePoint, Exchange, Teams etc.). Unfortunately, for a long time there were no options to get access to only one specific site with Graph API. Available API permissions (e.g. Sites.Read.All) allowed access to all SharePoint sites in the tenant.
Then, in 2021 Microsoft introduced Graph API “Sites.Selected” application permissions. Hooray! But the problem was developers needed to have two service principals – new Sites.Selected to call Graph API and classic SP-App-only to call SharePoint API. Later, in 2022 Microsoft added SharePoint “Sites.Selected” API permissions – so both APIs are available for a single App Id (more App-Only and Sites.Selected history details…).
Long story short, below are the detailed steps to configure Sites.Selected for you unattended app access to SharePoint site.
Steps to get and configure Sites.Selected permissions
1. Register an application in Azure (Entra Id) via Azure portal GUI, PowerShell script or helpdesk/servicedesk request. E.g. with GUI you’d login to portal.azure.com, then “Entra Id” -> “App registrations” and select “+ New registration”:
If you are not allowed to register an Entra Id app due to restrictions in your company – connect with your IT/admins, as there must me some way to request an app.
Once you get an application registration and you are this app owner – you should be able to navigate to your app registration in Azure Portal and configure it (Step 2 below).
2. “API permissions” – you’d need both – MS Graph API Sites.Selected and SharePoint Sites.Selected application API permissions configured (as libraries/modules and 3-rd party software can use both APIs under the hood):
Then you’d request tenant admin consent for your API permissions. Finally your app registration “API permissions” should look like:
3. App Secret or Certificate Under Certificates and secrets – generate client secret, copy secret value to safe location. Or you can get a certificate (obtain trusted or create self-signed), and upload it to your app registration. Certificates are considered as more secure option.
4. At the Overview page – grab your app client id and tenant id :
At this moment, having tenant id, app (client) id and client secret (or certificate) – you should be able to authenticate to Microsoft 365 tenant with app-only authentication flow, but will not be able to access any resource.
Having just Sites.Selected API permissions configured for the app does not mean your app has access to any SharePoint site automatically. Access to a specific site for the app is granted by the SharePoint team via another Graph API call. That leads us to the next step.
5. Application access to SharePoint site You need to request this from your SharePoint service admin (or if you are an admin – DIY), but access needs to be provided for the specific app to the specific site with specified permissions: Read-Only or Read/Write or Manage or Full Control. – Here is the Graph API – Here is PowerShell PNP cmdlet
Write role is similar to “Contributor” user permissions – it allows CRUD operations against list items (library documents and metadata)
Manage role allows create/update/delete lists/libraries
FullControl is full control
At this moment you should be able to access SharePoint sites. How? See below:
Use Sites.Selected permissions
Once your SharePoint tenant/service admin confirmed that access has been provided for you app to a specific SharePoint site/sites – you can use the app client id and client secret (or certificate) to work with SharePoint from your code using MS Graph API or SharePoint API. The exact technique depends on the language/platform you use, but there are some good tutorials published:
Generally, these Sites.Selected permissions allow you to make calls that are documented under “Files” and “Sites and Lists” Graph API documentation. I.e. get site details, get site lists/libraries, create lists and libraries, CRUD operations against list items, download/upload library documents – all within the specific site. Sites.Selected permissions do not allow search operations, anything related to groups or teams etc.
If you are facing issues – first of all, try to isolate the issue – are permissions to blame, or is it something else? To ensure permissions for your app were provided correctly – you can validate your app access to the target SharePoint site with simple PowerShell scripts: here is the sample code
Important Note: Sites.Selected API permissions allow you to call Microsoft Graph API with client Id and client secret or certificate. Calling SharePoint API with a client secret is not supported. You have to use a certificate to call SharePoint API.
Calls to SharePoint API with client Id and client secret are possible only if ACS-based permissions are provided for the app to the site (with appinv.aspx), which is strongly discouraged due to the announced retirement of App-Only (ACS) – see updates in the end of the article.
Secure your credentials
You should not hard-code your secret as you do not want your credentials to be leaked. So you need to secure your secrets in production. Solutions for secrets are included in cloud providers’ offerings, you can also use environment variables. If you are hosting your application in Azure – consider using key vault to store your secrets. You can configure managed identity for your application and provide access to the key vault for you application’s managed id. (Actually, if your app is running in Azure – it is possible to provide permissions for your app directly – via managed identity. This is considered as an even more secure setup – no app registration and no Key Vault is needed, as there are no credentials to store, but that’s a separate story: Connecting to Microsoft 365 and Graph API from the Azure Function App via Managed Identity).
Govern Sites.Selected permissions
(For SharePoint admins).
Existing admins API/cmdlets allow yo to provide Sites.Selected permissions for specific app to specific site, and to get Sites.Selected permissions provided to the specific site. But there is no API/cmdlet for the specific app to get all sites (with permissions) this app has access to. Meantime as SharePoint admin if you keep providing permissions upon users/devs requests – after some time you have no idea what app has access to what site with what level of access, especially in large organizations. Surely you can (and should) pull reports on all registered apps with access to SharePoint, but…
There is a solution developed by Joe Rodgers (Microsoft). This solution use SharePoint list as an inventory/storage and Power Automate flows to pull data from Entra Id and SharePoint and provides kind of dashboard so you can review details of all app registrations in the tenant with at SharePoint Online permission. Cool!
Also, if you have a licensed Copilot for Microsoft 365 – you have a license for SharePoint Advanced Management (SAM). And under SAM – Reports – you have aт “App insights” report, that tells you how various non-Microsoft applications registered to your Microsoft Entra Id access your SharePoint content (report is based on the Microsoft 365 audit log).
Note: you would not provide Sites.Selected permissions just upon user/developer request. You’d always get an approval from the target site owner. The site owner must understand that application will have permanent unattended access to the entire SharePoint site with permissions specified (read or write or manage or full control).
Generally, to provide an Application with Sites.Selected API permissions configured access to a specific site, SharePoint admin would run a set of PowerShell commands (or C# program or…) to ensure the client id exists, API permissions are configured and consented, to get app owners, target site owners, to get existing app permissions etc. Finally, admin would provide permissions and validate that permissions were provided correctly. It does not take long…
Update: Microsoft announced ACS permissions (app-only principals) retirement in 2026. So using ACS for any new development is not recommended.
It may be acceptable to grant ACS permissions to existing legacy custom applications or third-party or Microsoft apps/web apps (e.g. Alteryx, Azure Data Factory) – applications that only support a client ID and secret and use the SharePoint API under the hood – but only to avoid disruption to business processes and keeping in mind that ACS will expire soon, so these applications must be replaced/updated before 2026.
Update: Microsoft is implementing granular (permissions to 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).
Let say you need to run some code against Microsoft 365 on a scheduled basis. For this, you can use Azure Function App and timer-triggered Functions. From the code you’d call Microsoft Graph API and/or SharePoint API, so you’d need your Function somehow to get credentials on the fly from the KV and use it to call APIs. For this – you’d have a key vault where credentials will be be stored and retrieved by functions when needed. How does KV know if it’s ok to share secrets with the function app? Below I’ll share step-by-step how to create, configure and secure an Azure Function with system-assigned managed identity, and Azure Key Vault.
Below is a “classic” approach with client id, client secret and a key vault.
First, we’ll create a resource group, e.g. “Azure-Func-Secured”.
Next, we’ll create a function app:
We’ll avoid the default “Flex Consumption” service plan (as it is Linux-only and does not support dependencies) and select “Consumption” a hosting option for now:
Runtime stack we’ll be using is PowerShell Core 7.4, but you can choose your own (options are – Python, .Net (C#), Node.js, Java). Let us leave other configuration settings by default (e.g. Enable Public access: On) for now, we’ll fix (secure) it later.
Ok, the function app has been created. Notice that app service plan, application insights and storage account, as well as some other services were created automatically under the same resource group:
Now we can create one or more functions under this function app, also we’d need to create a key vault and a registered app. Let us start with function and VS Code would be our environment of choice.
Let us start Visual Studio Code in a separate folder. Ensure you have “Azure Functions”, “Azure Resources” and PowerShell extensions by Microsoft. You’d sign-in to Azure:
and create a function project:
You’d choose “Timer triggered” function (as we need the function running on schedule), the function name would be MyScheduledFunct01 and we’d leave the default schedule as “0 */5 * * * *” – so our function will be triggered every 5 minutes.
Let us deploy the function right away and see if it works. For this, we can use “deploy” icon under “WORKSPACE” panel:
or “Deploy to function app” option from the function app context pop-up menu under the Azure “RESOURCES” panel:
After successful deployment give it some time, then check function invocations and ensure function is triggered an running:
Next, we’d update “requirements.psd1” to include Azure Key Vault and PnP PowerShell modules as it takes some time for the function app to pull in and install dependencies. Requirements.psd1:
# This file enables modules to be automatically managed by the Functions service.
# See https://aka.ms/functionsmanageddependency for additional information.
#
@{
# For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. Uncomment the next line and replace the MAJOR_VERSION, e.g., 'Az' = '5.*'
# 'Az' = 'MAJOR_VERSION.*'
'Az.KeyVault' = '6.*'
'PnP.PowerShell' = '2.*'
}
And we’d update function itself to monitor if dependencies are installed, than we’d deploy the function again so time would work for us. MyScheduledFunction/run.ps1:
# Input bindings are passed in via param block.
param($Timer)
$currentUTCtime = (Get-Date).ToUniversalTime()
if ($Timer.IsPastDue) {
Write-Host "PowerShell timer is running late!"
}
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
#############################################################################################
Write-Host "Check modules installed:"
Import-Module PnP.PowerShell
Import-Module Az.KeyVault
Get-Module PnP.PowerShell
Get-Module Az.KeyVault
Write-Host "Check command available:"
Get-Command -Name Connect-PnPOnline -Module PnP.PowerShell
At first, we might see warning like “The first managed dependency download is in progress, function execution will continue when it’s done. Depending on the content of requirements.psd1, this can take a few minutes. Subsequent function executions will not block and updates will be performed in the background.”. So we’d just wait.
After some time the function will be able to use required modules. Here is the invocation output example:
App Registration To get unattended access to SharePoint (or Teams or Exchange etc.) as a service (daemon) application – we need credentials. It is done via “App Registration” under Entra Id (Azure AD) – here is the detailed step-by-step guide on registering apps in Azure.
Finally, we’d have a service principal with permissions we need:
We do not hard-code secrets, so let us create a key vault to keep secrets safe:
We’d leave all other configuration option by default, including “Azure role-based access control”, Allow access from: All Networks etc.
Here is the trick: even if I created this key vault and have an admin-level access, I’m not able to work with secrets values. So I need to provide additional access to myself. Being at the key vault, navigate to “Access control (IAM)” and add role assignment…
We’d select “Key Vault Secrets Officer”, next, select members… and select own name:
From this moment you should be able to CRUD secrets with values.
Now, let us generate a secret under App registration and copy secret value:
Then navigate to the Key Vault -> Object/Secrets -> Generate/Import – and save the secret value there:
Now you can get this secret… but the function cannot reach the secret… Here is the proof. Let us update the function code with:
and check the function output. You’d see something like (which is not very descriptive):
ERROR: Run Connect-AzAccount to login. Exception : Type : System.Management.Automation.PSInvalidOperationException ErrorRecord : Exception : Type : System.Management.Automation.ParentContainsErrorRecordException Message : Run Connect-AzAccount to login. HResult : -2146233087 CategoryInfo : InvalidOperation: (:) [], ParentContainsErrorRecordException FullyQualifiedErrorId : InvalidOperation TargetSite : Name : get_DefaultContext DeclaringType : [Microsoft.Azure.Commands.ResourceManager.Common.AzureRMCmdlet] MemberType : Method Module : Microsoft.Azure.PowerShell.Clients.ResourceManager.dll Message : Run Connect-AzAccount to login. Source : Microsoft.Azure.PowerShell.Clients.ResourceManager HResult : -2146233079 StackTrace : at Microsoft.Azure.Commands.ResourceManager.Common.AzureRMCmdlet.get_DefaultContext() at Microsoft.Azure.Commands.KeyVault.Models.KeyVaultCmdletBase.get_DataServiceClient() at Microsoft.Azure.Commands.KeyVault.GetAzureKeyVaultSecret.ExecuteCmdlet() at Microsoft.WindowsAzure.Commands.Utilities.Common.CmdletExtensions.<>c__31.<ExecuteSynchronouslyOrAsJob>b__3_0(T c) at Microsoft.WindowsAzure.Commands.Utilities.Common.CmdletExtensions.ExecuteSynchronouslyOrAsJob[T](T cmdlet, Action1 executor) at Microsoft.WindowsAzure.Commands.Utilities.Common.CmdletExtensions.ExecuteSynchronouslyOrAsJob[T](T cmdlet) at Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePSCmdlet.ProcessRecord() CategoryInfo : CloseError: (:) [Get-AzKeyVaultSecret], PSInvalidOperationException FullyQualifiedErrorId : Microsoft.Azure.Commands.KeyVault.GetAzureKeyVaultSecret InvocationInfo : MyCommand : Get-AzKeyVaultSecret ScriptLineNumber : 19 OffsetInLine : 13 HistoryId : 1 ScriptName : C:\home\site\wwwroot\myScheduledFunction\run.ps1 Line : $kvSecret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretName -AsPlainText Statement : Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretName -AsPlainText PositionMessage : At C:\home\site\wwwroot\myScheduledFunction\run.ps1:19 char:13 + $kvSecret = Get-AzKeyVaultSecret -VaultName $vaultName -Name $secretN … + ~~~~~~~~~~~~~ PSScriptRoot : C:\home\site\wwwroot\myScheduledFunction PSCommandPath : C:\home\site\wwwroot\myScheduledFunction\run.ps1 InvocationName : Get-AzKeyVaultSecret CommandOrigin : Internal ScriptStackTrace : at , C:\home\site\wwwroot\myScheduledFunction\run.ps1: line 19 PipelineIterationInfo : 0 1
So, how’d we allow key vault access for the function app? It’s as simple as that: – first, we’d need some identity assigned to function app – second, we’d provide access to the key vault to this identity
Managed Identity: assign an identity to the function. For this, you’d go to function Settings/Identity, and under System Assigned, you’d switch status to On and Save settings.
Then, you’d go to your key vault, Access Control (IAM) and add role assignment. But this time, you’d select the role “Key Vault Secrets User” (more about roles), and “Assign access to” Managed Identity, and select members – select your function app identity (notice, identity is assigned to function app, so all functions under the app will be able to use the identity):
Now, we’d check the next function invocation detail, and voila:
You can see that azure function was able on the fly to pull secret from the key vault, so now we should be able to use these credentials to access SharePoint.
As you know, having client id and client secret would allow you to call MS Graph API. But calling SharePoint API would require authentication with a certificate. “PnP.PowerShell” module use both APIs under the hood, so we’d need a certificate to connect to tenant and work with SharePoint using “PnP.PowerShell” module. Please refer to this article on how to run Connect-PnPOnline with Certificate stored in the Key Vault.
Securing Azure Function
Out-of-the-box Azure Function App is created in a not very secure manner. Here is whan we’d need to do to secure Azure Functions:
Finally, it’d be more secure not to have a key vault, but provide permissions to the managed identity of the Azure Function App.
How do we know when the SharePoint site was last updated?
We have multiple “when the site was modified last time” properties – e.g. some we can retrieve with SharePoint CSOM:
Site LastContentModifiedDate
Web LastItemModifiedDate
Web LastItemUserModifiedDate
Also we can get
MS Graph site object with LastModifiedDateTime property
get usage reports via Microsoft Graph (activity reports), and
use “Last activity” field via Admin Center GUI
On the other hand – we can view and modify site in multiple ways – visit site home page, open and/or update document/list item, change site/library settings, configure site permissions, assign site sensitivity label, setup site property and so on.
Question: which site “last modified” or “last activity” properties reflect what events/actions?
This might be important if we think of retention policies, or any kind of clean-up processes… Let say, we are getting report on abandoned sites (inactive sites), but we are also assigning sites sensitivity labels, or we are updating site custom properties (e.g. for adaptive scopes), we have an ownerless groups policy working etc.
What if we assign site sensitivity label to an old inactive (5 years old) site – would it affect retention policy since site was updated this way?
Results
So i did some tests and based on detailed results below, it seems like
Web LastItemModifiedDate is triggered when user just visited site (but property LastItemUserModifiedDate is not triggered)
If a document or list Item updated by user or app – all properties are triggered
MS Graph site property LastModifiedDateTime, root web property LastItemModifiedDate and Site LastContentModifiedDate – same values
If site custom property is updated – it does not affect any site “last modified” property
The same for sensitivity label updated by app – it does not affect any site “last modified” property
The same for Microsoft ownerless groups policy – when user accept or decline group membership – no site “last modified” properties are changed (the same is true for Microsoft 365 group last modified date/time property).
Please refer to the table below
Detailed test results
Test results if the event triggers property update:
Event
Last Content Modified Date
Last Item Modified Date
Last Item User Modified Date
Graph Last Modified DateTime
GUI Last activity
Page viewed by user
Yes
Yes
No
Yes
Home Page viewed by user
Site Page viewed by user
Document or list item updated by user
Yes
Yes
Yes
Yes
Document or list item updated by app
Yes
Yes
Yes
Yes
Site config settings updated by user
Site config settings updated by app
Site custom property updated by app
No
No
No
No
Site Sensitivity label updated by user via SharePoint
Yes
No
No
No
Site/Group Sensitivity label updated by user via Teams
Site/Group Sensitivity label updated by user via Azure
No
No
No
No
Site Sensitivity label updated by app
No
No
No
No
Site collection admin updated by user
Yes
Yes
No
Yes
Site collection admin updated by app
Yes
Yes
No
Yes
SharePoint group membership updated by user
Yes
Yes
No
Yes
Standalone Site connected to a group by user
Yes
Yes
Yes
Yes
Add Microsoft Teams to Site by User
Yes
Yes
Yes
Yes
Update m365 group membership via M365 admin console by admin
Yes
Yes
No
Yes
Update m365 group membership via Azure by admin
Update m365 group membership via Teams by user
No
No
No
Yes
Update m365 group membership via App
Accept group ownership invitation sent by ownerless groups policy
No
No
No
No
Decline group ownership invitation sent by ownerless groups policy
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.
This PowerShell script pulls all tenant sites and all sites owners. The script require app authentication with Sites.FullControl.All and Directory.Read.All permissions. PnP.PowerShell for PowerShell 7 is used.
The script generates two reports
Owners report: one user per line, include: Site Url, Title, Owner e-mail, name and type
Sites report: one site per line, include: Site Url, Title, list of owners e-mails
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.
Here I’m trying to figure out – how much PowerShell Parallel option is beneficial and how to avoid throttling…
Let us test, how long would it take to create a SharePoint site, if we use regular (sequential) loop or parallelism (I’m creation a sample set of 50 SharePoint Sites in a row):
Regular (Sequential) seconds per site
Parallel, 100 sites in batch seconds per site
Parallel, 500 sites in batch seconds per site
Regular (Sequential)
3.0
Parallel, ThrottleLimit = 2
1.60
0.91
Parallel, ThrottleLimit = 5
0.69
Parallel, ThrottleLimit = 10
0.2 – 0.3
Parallel, ThrottleLimit = 20
0.17
Interesting, but I did not get even one (throttling or any other) error during creation 500 sites.
Get sites details
Now let us test, how long it takes to get sites details with Get-PnPTenantSite (I use a sample set of 500 sites):
Test type
Regular (Sequential), seconds per site
Parallel sample = 100 sites, seconds per site
Parallel sample = 200 sites, seconds per site
Parallel sample = 500 sites, seconds per site
Regular (Sequential)
0.65
Parallel, ThrottleLimit = 2
0.40
0.33
0.31
Parallel, ThrottleLimit = 5
0.17
0.14
0.36 (errors)
Parallel, ThrottleLimit = 10
0.11 (errors)
0.11 (errors)
0.34 (errors)
Parallel, ThrottleLimit = 20
0.12 errors+
0.07 errors+
0.52 (errors)
(errors) means there were small number of errors during test… e.g.
Some templates can be applied by regular users (site admins) and some templates would require SharePoint tenant admin permissions. But now it’s only via PowerShell. You can get an idea how templates look like at
PnP provisioning engine is something that us used under the hood.
If you are interested in automation of provisioning templates – please let me know in comments below or via site feedback.
===============================
So the information below is obsolete and I will keep it just for the sake of history of SharePoint:
SharePoint Look Book
SharePoint Look Book – a site with a collection of modern SharePoint site templates. You can browse through dozens of good-looking templates… but how do you apply chosen template to your site?
Gotcha #1
There is a button “Add to your tenant>” and it says “You must be a tenant administrator to deploy this template.” Really? No… but Actually, SharePoint Administrator role is required to apply template from lookbook. So yes, tenant-level admin role but just SharePoint service admin role. Site admin role is not enough…
Gotcha #2
Next, when you try to get template by clicking “Add to your tenant>” button, it actually offers you to create a new site. But it also says “…can use existing URL”. Really? No. When you type existing site Url into the “Relative URL to be used for the site” field – You can get “Can’t add this template. The provided site is already in use and the current template cannot be provisioned onto an already existing site. Please provide a different URL” message:
Or, if you managed to enter existing Url, you might get: “Unfortunately your site provisioning at least partially failed!”:
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).