There are many scenarios for SharePoint or Teams automations, and in most cases you need to run some code on scheduled basis (e.g. every 5 minutes or every 24 hours etc.). This is where timer-triggered Azure Functions might help. In this article I will provide use cases, overview of the whole scenario and technical setup, and provide links to the detailed step-by-step guides for configuring different parts of the entire solution.
Possible scenarios
Possible scenarios (end-user-oriented):
Create site upon user request
Convert site to a HUB site upon user request
Set site search scope upon user request
Setup site metadata (site custom properties)
Request usage reports/analytics
Possible scenarios (admin-oriented):
Provide temporary access to the site (e.g. during troubleshooting)
Provide Sites.Selected permissions for the App to the Site
Disable custom scripts or ensure custom scripts are disabled
Enable custom scripts (e.g. during site migration)
Monitor licenses – available, running out etc.
Typical setup
Front-end
SharePoint site works as a front-end. You do not need to develop a separate web application, as It’s already there, with reach functionality, secured and free.
The site can have: – one or more lists to accept intake requests – Power Apps to customize forms – Power Automate to implement (e.g. approval) workflows, send notifications etc. – site pages as a solution documentation – libraries to store documents provided as response to requests
You can provide org-wide access to the site if your intention is to allow all users to submit requests or secure the site if you want to accept requests only from a specific limited group of people.
Back-end
Timer-triggered Azure Function works as a back-end. The function can be scheduled to run based on job specific requirements (e.g. every 5 or 10 minutes, or daily or weekly etc.). The function can be written in PowerShell, C#, Python etc.
The function’s logic is to
read SharePoint list, iterate through items to get intake requests
validate request eligibility
perform action
share results (e.g. update intake form, send e-mail, save document to library etc.)
Configuration
There should not be an issue to setup a front-end. You’d just need a solid SharePoint and Power Platform skills.
For the back-end the solution stack would include the following tools/skills: – Azure subscription to host solution – Registered Apps to configure credentials and API access permissions – Azure Function App to actually run the code – Azure Key Vault to securely save credentials – programming skills in language/platform of choice – SharePoint API, Microsoft Graph API
If you need to update a SharePoint site title (site name) programmatically (e.g. with PowerShell), and if this site is a group-based site (e.g. Microsoft Teams team site or Viva Engage community site or…) – you should not update SharePoint site title, but you should update group display name instead. Here is why.
In Microsoft 365 there is no sync from SharePoint site title to a group name. When you are updating SharePoint site title with GUI – you can see that new site title becomes new group/team name as well. So you might think that if you update SharePoint site title – Microsoft synchronizes it to connected group name. That’s not true. Actually when you are updating a group-based (e.g. teams-connected) SharePoint site title with GUI – Microsoft updates group first, then syncs updated group display name to SharePoint site name (title).
Here is the proof:
That’s a network trace I got with browser dev tools when I renamed site (updated site title) with GUI. So you can see the first API call is to update group, then group properties are synced back to site.
When we are updating a standalone site title – we are not seeing these calls.
So, if you need to update group-based site title programmatically – you must update group instead.
# does not work for group-based (e.g. Teams) sites:
Set-PnPTenantSite -Identity ... -Title "New Site Title"
# instead, you'd update group display name
Set-PnPMicrosoft365Group -Identity ... -DisplayName "New Display Name"
# and site title will be updated accordingly
There is a Microsoft.Graph PowerShell module provided by Microsoft which simplifies usage of Microsoft Graph API. Below is how to authenticate to MS Graph and how to search within SharePoint and Teams Microsoft 365 content using Microsoft.Graph PowerShell module.
For daemon app authentication we need a certificate configured in Azure App and installed on the user machine. Daemon app authentication code sample (please specify your tenant id, app (client) id and certificate thumbprint:
Below is how do I search Microsoft 365 content programmatically from PowerShell using MS Graph API, PowerShell PnP, Microsoft Graph module, MSAL library being authenticated as user or daemon application. Let me focus on SharePoint content here but you can use the same technique to search through other Microsoft 365 services. Also, I’ll be using PowerShell but same ideas should work for other platforms/languages – Python, C#, node.js etc.
To search on behalf of currently authenticated user we need delegated “Sites.Read.All” API permissions. I recommend you to add both Graph API and SharePoint API permissions as different libraries might use different API’s under the hood. Ensure you add delegated “Sites.Read.All” even if you already have “Sites.FullControl.All” as by some reason “Sites.FullControl.All” does not always work for search.
Here is how an app API permissions to search as current user should look like:
For unattended search – e.g. search on behalf of daemon app – we need application “Sites.Read.All” API permissions. Again, I suggest both Graph API and SharePoint API permissions added. Here is how an app API permissions to search as daemon app should look like:
Ensure you got admin consent for API permissions.
In case you have incorrect permissions in your app – Microsoft Graph will be kind enough to inform you exactly what you need. Example:
“Access to ChatMessage in Graph API requires the following permissions: Chat.Read or Chat.ReadWrite, ChannelMessage.Read.All. However, the application only has the following permissions granted: Sites.Read.All, User.Read”
Assuming we have configured apps – let us get started with
Microsoft Graph API
Microsoft Graph API allows search through all the Microsoft 365 content – including Exchange e-mail messages, Yammer (Viva Engage) and Teams chat messages and surely OneDrive and SharePoint content (please refer to the original doc).
Authenticate as current user to Search with Graph API
I use MSAL.PS PowerShell module to get token, then I build a headers variable
If you are getting error message “SearchRequest Invalid (Region is required when request with application permission.)”:
that’s OK, just modify your body to include region like this (“region”: “NAM” for North America or “GBR” or …). Also, I can modify body with from/size for paging (technique used to iterate through search results if there are many) and return just specific fields to decrease traffic and improve performance:
For daemon app authentication we need a certificate configured in Azure App and installed on the user machine. Daemon app authentication code sample (please specify your tenant id, app (client) id and certificate thumbprint:
What if you need to bulk update Microsoft 365 groups membership e.g. to add a group owner or member for tens of thousands m365 groups? Iterating through groups one-by-one is unproductive and could take days. Can we do it faster? Here is what I found.
In my case, it was Microsoft 365 ownerless groups policy implementation for large tenant… Skipping details – I needed to update ownership for 10,000 Microsoft 365 groups and I was looking for a best/fastest possible option maybe some kind of bulk update or with multiple threads. And I figured out that the fastest way is to use PnP.PowerShell that calls Microsoft Graph API but run it against list of groups with PowerShell parallel trick. Here is the sample PowerShell code:
Sometimes, mostly during PoC or testing policies like retention policy or lifecycle policy you would need some documents created and updated weeks, months or even years ago.
But if you create or upload a document in SharePoint library – it will be just a regular new document. So, how to get old documents in the new environment?
I see two options:
Sync with OneDrive If you sync a library with your local folder (done Microsoft by OneDrive desktop app) and put some old document in your synced folder – the doc will be synchronized back to SharePoint library with Created and Modified properties preserved.
Make the document older with PowerShell With “Set-PnPListItem” PowerShell command you can update not only such properties like Title, but also “Created By”, “Modified By” and even date and time document was created and modified via “Created” and “Modified”. Optionally you can play with document history with “-UpdateType” parameter. UpdateType possible values are:
Update: Sets field values and creates a new version if versioning is enabled for the list
SystemUpdate: Sets field values and does not create a new version. Any events on the list will trigger.
UpdateOverwriteVersion: Sets field values and does not create a new version. No events on the list will trigger
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 a specific SharePoint sites using Microsoft Graph API and/or SharePoint API (since Microsoft announced 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-prem, later in SPO. We used to get SharePoint app-only service principals via appregnew.aspx and provide permissions via 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 tenant.
Then, in 2021 Microsoft introduced Graph API “Sites.Selected” application permissions. Hooray! But the problem was devs had 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 (see Step 2 and below).
2. Update the app “API permissions” – you’d need both – MS Graph API Sites.Selected and SharePoint Sites.Selected application API permissions configured:
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 against Microsoft 365 tenant with app-only authentication path, but should not be able to access any site.
Having just Sites.Selected API permissions configured for app does not mean your app has access to any SharePoint site automatically. Access for the app to a specific site is provided by 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 specific SharePoint site/sites – you can use app client id and client secret (or certificate) to work with SharePoint from your code using MS Graph API or SharePoint API. Exact technique depends on language/platform you use, but there are some good tutorials published:
Generally, this Sites.Selected permissions allows 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 does not allow search operations, anything related to group or team etc.
If you are facing issues – first of all you’d try to isolate the issue – is it permissions to blame or 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 allows you call Microsoft Graph API with client Id and client secret or certificate. Calling SharePoint API with client secret is not supported. You have to use 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 strictly not recommended due to announced App-Only (ACS) retirement – 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 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 keep your secrets. You can configure managed identity for your application and provide access to the key vault for you application 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 even more secure setup – no app registration and no key vault is needed as there is no credentials to save, 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 allows 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 registerd 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!
Note: you would not provide Sites.Selected permissions just upon user/developer request. You’d always get an approval from target site owner. Target site owner must understand that application will have permanent unattended access to 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).