Microsoft recently (Apr 2024) announced general availability for it’s new SharePoint Archive feature (learn more). So if you are seeing “The site is archived” and “A SharePoint Administrator archived this site. If you need access, ask an admin to reactivate it.” error message and the page in your browser is “sharepointerror.aspx?scenario=SiteArchived” then… guess what… your site was archived.
And if you need this site – please reach your SharePoint admin as soon as possible, as reactivating the site within 7 days is free, otherwise it might cost your company some dollars.
Though the page Url is “https://yourtenant.sharepoint.com/_layouts/15/sharepointerror.aspx?scenario=SiteArchived” and the page title is “Error”:
this is not an error but just a new SharePoint feature 🙂
Microsoft 365 groups is a key concept in today’s collaboration landscape that includes Microsoft Teams, Viva Engage, SharePoint etc. Access to resources is organized via groups. It is essential that every Microsoft 365 group has an owner (owners) so we have somebody to enforce Collaboration governance through.
Scenario
Let say you administer a large Microsoft 365 environment (e.g. ~100k+ users and/or ~50K+ sites) and after some years you have a lot of ownerless groups and sites (around 5k probably), and a lot of inactive groups and sites (maybe 15k). You are getting more and more ownerless groups – dozens each week. You are thinking of stopping bleeding and cleaning this up…
Out-of-the-box we have Microsoft 365 groups expiration policy and Microsoft 365 ownerless groups policy. You might also have some 3-rd party tools implemented – e.g. ShareGate, SysKit Point.
If you do not care – you might just activate both OotB Microsoft policies – via GUI – they are simple to activate. But once you activated policies – they will trigger thousands of emails. Now imagine a person is getting dozens of emails asking him/her to be an owner or to renew the group that probably he/she has no idea about… What will happen next? People will probably ignore these alerts. Then? Groups and sites will be automatically deleted. And then? Right, there will be a huge noise and many angry users and high-priority tickets and you will have to restore sites/teams and finally you’ll have to deal with all that mess manually.
So, what is the right way to clean-up a large Microsoft 365 environment from ownerless and inactive teams, groups sites? Not a trivial question, hah?
Solution
Disclaimer: I’m sharing here my personal opinion with no obligations or warranty etc., so you’d dig into all the technologies used and based on your particular situation build your own plan. But my personal opinion is based on my 15+ years experience with SharePoint, including really large environments.
Note: It is always a good idea to discuss your plans with you org’s communication team and helpdesk/service-desk to adjust clean-up activities with other initiatives and let other people be prepared.
High-level steps for group-based Sites:
consider implementing Minimum 2 owners per group policy to stop bleeding. Currently Microsoft 365 does not have such functionality, so consider 3-rd party tool like SysKit Point or custom PowerShell script that sends notifications
apply this policy to groups where you already have 2+ owners – it’ll be safe
apply this policy to all other groups by chanks
consider custom PowerShell clean-up, e.g. you can simply delete groups with no owners and no members and/or inactive groups with no content and/or groups that are inactive for a long time (this must be aligned with business and legal)
avoid scoping down this policy via people (security groups)
implement it for all groups all users with 6-7 weeks and custom e-mail template
implement Microsoft groups expiration policy in “Clean-Up” configuration… again, there are a few different strategies – see this article
change Microsoft Ownerless groups policy configuration to a “Permanent” mode configuration set
(or) change Microsoft 365 groups expiration policy with a “Permanent” mode configuration
(or) develop and implement custom staged decommissioning process – kind of “last chance” set of scripts to discontinue groups that are still ownerless after all efforts above. Staged means we do not just delete these groups, but e.g. we can – rename ownerless groups – convert groups from public to private – set teams to archived mode – exclude sites from copilot search with “Restricted SharePoint Search” etc. – set site to no-access mode – remove members from the group – and finally delete the group with connected team team and site I have a separate article on custom staged decommissioning process
Note: There will always be ownerless groups in large environment. We have to live with it. So all steps above – think of it as a processes – we’d need to do it on regular basis.
All above was mostly about group-based sites (as we have OotB Microsoft policies for groups), but we probably have the same problem (or even worth) with standalone sites (that would be a separate topic).
If you are working with SharePoint Online list and select Integrate – Power BI – Visualize the list, but it gives you error message “You cannot use Power BI to visualize this list”, “Looks like the feature for visualizing lists is turned off. Please contact your admin to enable this feature”:
The issue appears to be not in SharePoint, but in Power BI. Note it says “You cannot use Power BI to visualize this list” and “Looks like the feature for visualizing lists is turned off. Please contact your admin to enable this feature.”
Also the url of this page is Power BI Url: “https://app.powerbi.com/sharepointlist?spListId=%7Bd3b56”, so you’d need contact Power Platform Administrators, not SharePoint administrators.
Power BI administrator would go to Microsoft Fabric Admin portal
and ensure “Integration with SharePoint and Microsoft Lists” is Enabled for the entire organization or for specific security groups. In the last case – ensure user who is getting “You cannot use Power BI to visualize this list” is added to at least one of the groups but not added to “Except specific groups”.
If the user is allowed under “Integration with SharePoint and Microsoft Lists” so “Users in the organization can launch Power BI from SharePoint lists and Microsoft Lists. Then they can build Power BI reports on the data in those lists and publish them back to the lists.” then, normally, user would see:
secrets is a Python file where I assign client secret to variable clientSc (so my secret is not shared on github). This is ok for demo purposes but generally, you should not hard-code secrets but keep secrets somewhere safe (Vault).
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:
Let me quote Microsoft just to start (Dec 18, 2023):
“SharePoint App-Only is the older, but still very relevant, model of setting up app-principals.”
“… we will be retiring the use of Azure ACS (Access Control Services) for SharePoint Online auth needs and believe Microsoft 365 customers will be better served by modern auth…”
“Azure ACS will stop working for new tenants as of November 1st, 2024 and it will stop working for existing tenants and will be fully retired as of April 2nd, 2026… There will not be an option to extend using Azure ACS with SharePoint Online beyond April 2nd, 2026″
“… we recommend switching those applications to use Microsoft Entra ID for authorization and authentication needs…”
So, for new development it is strictly recommended to use Azure Registered Apps to access Microsoft 365 resources programmatically.
You still need ACS in some cases
But, as always, it all is not so simple, as
there are still plenty of 3-rd party applications written and used widely that require ACS-based permissions. Moreover, there are still some 1-st party applications (Microsoft apps and services) that require ACS-based permissions
though Microsoft Graph API is good and provide a lot of functionality and is developing rapidly, it cannot cover all SharePoint dev’s needs, so using SharePoint REST API could be unavoidable… and that is where some complications are coming
permissions to specific SharePoint sites (not to all tenant sites, but to one or several SharePoint sites in tenant) for apps is done via Sites.Selected, but this works to entire site collection only. E.g. via Sites.Selected you cannot provide granular permissions (e.g. to specific list) for an app, which might be crucial in some cases, so you’d still have to use ACS-based permissions
Hopefully, Microsoft will resolve all the issues above before April 2026… But for now we have to live with both – Azure Registered applications and API permissions configured in Entra ID and with SharePoint app-only service principals and ACS-based permissions.
Azure Apps and Entra Id vs SharePoint app-only spn and ACS
Comparison between Azure Apps and Entra Id API permissions vs SharePoint app-only spn and ACS-based permissions
ACS-based SharePoint app/permissions
Apps registered in Azure with Sites.Selected API permissions
support authentication with client secret only, secret is valid for 1 year exactly
support authentication with client secret and/or certificate, custom expiration time
support granular access to SharePoint site content – e.g. to entire site collection or web (subsite) or a specific list
support only access to entire site collection (but Microsoft is working on granular access) Now (Sep 2024) Microsoft supports granular (list, library, Item, Document level) access for an app to a site.
support only classic SharePoint REST API and CSOM
support both – classic SharePoint REST API and CSOM and Microsoft Graph API
app id (client id) is created via appregnew.aspx at a specific SharePoint site by site collection administrator
app id (client id) is created in Azure portal, API Sites.Selected permissions are configured via Azure portal and require tenant admin consent
permissions for the app to a site are provided at the site by site collection administrator via appinv.aspx page
permissions for the App to to a specific SharePoint site/list/item are provided by SharePoint admin with PowerShell script or Graph API calls
logging
audit log
SharePoint app-only service principal and ACS-based permissions
Since SharePoint app-only service principals and ACS-based permissions were introduced for SharePoint 2013 as part of Add-Ins feature – there are plenty of articles from Microsoft and MVPs and SharePoint gurus on this. But I would like to highlight one thing:
AppRegNew page creates service principal and allows authentication
AppInv page provides permissions and allows authorization to SharePoint
In cases Graph API does not provide required functionality – it’s ok to use SharePoint API, but please ensure certificate is used (not secret).
For SharePoint admins
Encourage users register applications in Azure (not in SharePoint)
Disable ability for site owners register service principals in SharePoint via appregnew.aspx Your users will start seeing “Your SharePoint tenant admin doesn’t allow site collection admins…” message (see details), but that’s ok.
Create a process so users can request application permissions to SharePoint. Provide Sites.Selected permissions by default. Consider automation. In rare cases when 3-rd party apps require legacy ACS-based permissions, it would be you (SharePoint service admin) who will provide ACS-based access to sites. Track this activity (so you know for whom this ACS-based permissions were provided). Inform every developer that ACS will be gone.
Keep audit logs Starting today and until it’s over you’d get audit logs from Microsoft 365 purview center – consider selecting all events anyone visited appinv.aspx page.
In March-April 2025 (1 year before) ACS EOL, start notifying developers who use ACS. You can get list of developers combining – audit log data – report from Entra Id on apps owners
In advance ( let say, starting September 2025) you can try to temporary switch off ACS (“scream test”).
Using Microsoft Graph API is a preferred and recommended way to connect to SharePoint Online and other Microsoft 365 resources programmatically from Python code. But if by some reason you are required to use classic SharePoint REST API, here is how it is done.
Authentication blade must be configured for authenticate as current user
Certificates and/or secrets must be configured for daemon app (unattended access)
API permissions your Azure app registration must have API permissions configured based on resources you need access to and authentication method chosen here is how to configure API permissions for your app
Office365-REST-Python-Client library installed
Using client Id and client secret to access SharePoint REST API from Python
Errors
Possible errors and diagnostic messages
HTTPError: 401 Client Error: Unauthorized for url… The provided client secret keys for app … are expired. Visit the Azure portal to create new keys for your app or consider using certificate credentials for added security
Microsoft Graph API allows you to work with all the Microsoft 365 content – including search through Exchange e-mail messages, Yammer (Viva Engage) and Teams chat messages and surely OneDrive and SharePoint content (please refer to the original doc). Let me focus on searching in SharePoint Online and OD here but you can use the same technique to search through other Microsoft 365 services. I will use PowerShell but same ideas should work for other platforms/languages – Python, C#, node.js etc.
Assuming we have a registered Azure app configured correctly, including Secrets/Certificates blade and API permissions provided – we should be ready to authenticate and call Graph API unattended – on behalf of application itself.
Let us authenticate as a service/daemon app with client id and client secret:
# Authenticate to M365 as an unattended application
# specify your app id. app secret, tenant id:
$clientID = ""
$clientSc = ""
$TenantId = ""
# Construct URI and body needed for authentication
$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
$body = @{
client_id = $clientID
client_secret = $clientSc
scope = "https://graph.microsoft.com/.default"
grant_type = "client_credentials"
}
# Get OAuth 2.0 Token
$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$token = ($tokenRequest.Content | ConvertFrom-Json).access_token
$headers = @{Authorization = "Bearer $token" }
Below is how I search Microsoft 365 content programmatically from PowerShell using MS Graph API being authenticates as user.
Notice we use “region” – it is required to search with Graph API under application credentials. Otherwise you will get an error message “SearchRequest Invalid (Region is required when request with application permission.)”:
Parameter “fields” allows you to request only fields you need to be returned. As returning object will be smaller your request will perform faster.
There might be a big number of objects found in m365 upon your request. Graph will not always return to you all the results. AFAIK currently the limit is 500, so if there are more than 500 objects found – only first 500 will be returned. You can specify how many objects you need to be returned per call with “size” parameter.
You can check value of $res.value[0].hitsContainers[0].moreResultsAvailable property and if it’s True – that means there are more results. The value above and parameters “from” and “size” would allow you to organize a loop so you can call search API many times to return all results.