Auditing External SharePoint Sharing with PowerShell and Microsoft Graph
Sharing links make collaboration easy, but they can also leave files accessible to people outside your organisation. When you need a tenant-wide view of where external access exists, checking sites one at a time quickly becomes tedious.
This PowerShell script uses Microsoft Graph to inventory SharePoint sites and inspect the sharing permissions on their drive items. It reports anonymous links and external direct shares, and can optionally include internal sharing links in a CSV report.
What the report covers
For each site it finds, the script records a site-level row and checks drive items for sharing permissions. The report can include:
- Anonymous (
Anyone) links - Direct shares where the recipient's email address appears to be outside the tenant
- Link scope, access role, and expiry where Graph returns those details
- The recipient name and email for direct shares, when available
- Item name, URL, type, size, and last-modified time
- Site and item IDs to help locate the source later
By default, the report focuses on external and anonymous sharing. Use -IncludeInternalLinks to include internal links as well.
This is an inventory aid, not a permissions remediation tool. Review findings with the relevant site owner and confirm access in SharePoint before removing or changing a share.
Prerequisites and permissions
The script requires PowerShell 7.0 or later and makes direct HTTPS requests to Microsoft Graph. It has no external PowerShell module dependencies.
You will also need an Entra app registration configured for app-only authentication:
- Create a multitenant app registration in the tenant where you manage your applications.
- Add the Microsoft Graph application permission
Sites.Read.Alland grant admin consent in that tenant. - Grant admin consent for the app in each target tenant. If you use CIPP, its Deploy App Registration workflow can deploy the app and consent to it for a customer tenant.
- Provide the app's client ID and client secret when running the script.
Sites.Read.All gives the app broad read access to SharePoint sites in a consented tenant. Use a dedicated app, protect its credentials in an approved secrets store, and only grant access to tenants you are authorised to assess. Do not commit a client secret to source control or put it in a script that will be shared.
The script resolves the tenant ID from the SharePoint hostname using Microsoft's OpenID Connect metadata endpoint. You don't need to look up or pass a tenant ID yourself.
The script
This script was updated after being published, if you're using it please compare the version you have with the version available here.
This script was last updated on 2026/05/11.
loading...
Running a scan
To scan one site, pass its URL. The script extracts the SharePoint hostname from it:
$parameters = @{
SiteUrl = 'https://contoso.sharepoint.com/sites/Finance'
ClientId = '<application-id>'
ClientSecret = $clientSecret # Retrieve this from your approved secrets store.
}
.\Get-SharePointSharingReport.ps1 @parameters
To scan sites across a tenant, supply the tenant's SharePoint hostname instead:
$parameters = @{
SharePointHostname = 'contoso.sharepoint.com'
ClientId = '<application-id>'
ClientSecret = $clientSecret # Retrieve this from your approved secrets store.
OutputPath = 'C:\Reports\SharePoint'
}
.\Get-SharePointSharingReport.ps1 @parameters
The script handles one tenant per run. For an MSP workflow, deploy and consent to the multitenant app in each customer tenant, then run the scan for each tenant you are authorised to access.
Output and resuming a scan
By default, the script creates a timestamped CSV in the current directory. Set -OutputPath to choose another directory. It writes each row as it is found, so completed work is retained if a long scan is interrupted.
To resume, specify the existing CSV with -CsvPath and add -Resume:
.\Get-SharePointSharingReport.ps1 `
-SharePointHostname 'contoso.sharepoint.com' `
-ClientId '<application-id>' `
-ClientSecret $clientSecret `
-CsvPath 'C:\Reports\SharePoint\contoso-sharing.csv' `
-Resume
Resume mode loads site and permission identifiers from that CSV to avoid writing the same rows again. If you reuse a CSV without -Resume, the script removes the existing file before starting a new report, so take care to select the intended path.
Managing scan size and request rate
A tenant-wide scan can take time: the script makes a permission request for each item it checks. Two parameters help tune a run:
| Parameter | Default | Purpose |
|---|---|---|
MaxItemsPerSite | 500 | Sets the Graph $top value used when requesting drive items. Graph paging is followed, so treat this as the requested page size—not a guaranteed total item cap. |
PermissionRequestsPerMinute | 250 | Adds a rolling-window pause between permission requests to limit request volume. The accepted range is 1–5000. |
Start with a single site to validate consent, connectivity, output, and runtime in your environment. Then run a tenant-wide scan and adjust the request rate to suit your service limits and operational window.
How the script identifies external sharing
The script treats a link with an anonymous scope as external. For direct shares, it compares the returned recipient email address with the tenant's SharePoint hostname prefix. If -IncludeInternalLinks is enabled, recognized sharing links and direct shares that would otherwise be excluded are also written to the report.
Graph may return incomplete recipient details for some permission types, and a domain comparison is a useful indicator rather than a full identity or access review. Treat the CSV as a starting point for investigation, not proof that every possible access path has been found or that an unlisted item is private.
Things to keep in mind
- Tenant-wide access requires successful app consent in the target tenant. A missing or invalid consent will prevent the script from retrieving sites.
- A scan can generate many Graph requests and take a while on large tenants; the request-rate guard may pause the run.
- The scan is read-only. It reports shares but does not revoke links or modify permissions.
- CSV reports can contain recipient email addresses and sensitive site or file metadata. Store and share them according to your organisation's data-handling policies.