Copilot

Find and Revoke SharePoint Sharing Links with PowerShell

Sharing links are the quietest oversharing problem in SharePoint. Somebody clicks Share, picks “anyone in the organisation”, and eighteen months later that link is still live and nobody remembers it exists. It does not show up as a normal permission. It hides as a group with a name like SharingLinks.042f4d62-d446-4d80-83d1-fdb40ce5956d.Flexible.23ce70ce, which nobody reads.

Then you turn on Copilot, and Copilot is very good at finding files people can reach. Every one of those forgotten links is now one prompt away from the wrong person.

I could not find a free tool that lists these links across a tenant, rates how risky each one is, and lets you revoke the bad ones. So I wrote one. It is on the PowerShell Gallery:

Install-Module SharingLinkAudit

It is on the PowerShell Gallery if you want to see the version and what is in the package first, and the source is on GitHub, MIT licensed. This post is how to use it.

Before you start

It needs PnP.PowerShell and your own Entra ID app registration, the same one any PnP tool needs since the shared app was retired. If you have not set that up, I wrote it up separately in Connect-PnPOnline: Specified Method Is Not Supported. Once you have the client ID, you are ready.

Audit one site

Get-SharingLink -SiteUrl "https://contoso.sharepoint.com/sites/Sales" `
    -ClientId "your-app-id" -Interactive

A browser opens, you sign in, and you get one object per link. A risky one looks like this:

ItemName    : Monthly Report.txt
ItemPath    : /sites/Sales/Shared Documents/Monthly Report.txt
Scope       : Organization
Access      : Edit
Recipients  : Everyone in the organisation
Expiration  :
HasPassword : False
RiskLevel   : High
WebUrl      : https://contoso.sharepoint.com/:t:/s/Sales/IQBiTS8ERt...
LinkId      : d695e0ac-057f-49fb-8d01-eff6bb843a44

Read that in one line: anyone in the organisation can edit this file, through a link that never expires. That is why it scored High.

If a site is clean it tells you so, rather than printing nothing and leaving you wondering whether it worked:

No sharing links found across 1 site(s).

How the risk is scored

The RiskLevel is the whole point. It is what turns a long list into a short list of things to actually deal with.

  • Anonymous “anyone with the link” is the worst starting point. Organization “anyone in the company” is a real exposure but internal. Users, specific named people, is what sharing is supposed to be.
  • Edit is worse than View.
  • No expiry counts against the broad links, because those are the ones that linger.
  • An anonymous link with no password is a plain open door.

So a specific-person view link is Low, an org-wide edit link with no expiry is High, and an anonymous no-password edit link is Critical. You filter to what matters:

Get-SharingLink -SiteUrl $url -ClientId $id -Interactive -MinimumRisk High

Audit the whole tenant

This is the one people actually want. It needs SharePoint admin and the admin URL:

Get-SharingLink -TenantWide `
    -TenantAdminUrl "https://contoso-admin.sharepoint.com" `
    -ClientId "your-app-id" -Interactive -MinimumRisk High

It walks every site in the tenant. On a real tenant that is the slow mode, so it does not fight SharePoint’s throttling. If it gets a 429 it waits the amount SharePoint asks for and carries on rather than falling over.

The way it finds the links is the part worth understanding. It does not walk every file, which on a big library would be tens of thousands of calls. It reads the hidden SharingLinks groups, which tell it exactly which files have a link without touching the rest. A library of fifty thousand documents with two hundred shared items costs about two hundred lookups, not fifty thousand. That is what makes tenant-wide practical.

Turn it into a report

Nobody reads a thousand rows scrolling past. Send it to an HTML report instead:

Get-SharingLink -TenantWide -TenantAdminUrl $admin -ClientId $id -Interactive -MinimumRisk High |
    Export-SharingLinkReport -Path .\sharing.html -Html
Report generated: 14 link(s) written to C:\reports\sharing.html

That is a self-contained page, colour-coded by risk and sorted worst first with no external anything, that you can open in a browser and email to whoever asked “are we overshared”. There is a .csv option too if you want it in Excel.

[SCREENSHOT: the HTML report open in a browser, showing the colour-coded risk pills and the table of links. Take it from a run against the demo site, not a real tenant.]

Revoke the bad ones, carefully

Revoking a sharing link cannot be undone. The link is gone and whoever relied on it has to be re-shared. So always look before you leap:

Get-SharingLink -SiteUrl $url -ClientId $id -Interactive |
    Where-Object Scope -eq 'Anonymous' |
    Remove-SharingLink -WhatIf

-WhatIf shows you exactly what would be removed and deletes nothing. Read that list. Then, when you are sure, drop the -WhatIf:

Get-SharingLink -SiteUrl $url -ClientId $id -Interactive |
    Where-Object Scope -eq 'Anonymous' |
    Remove-SharingLink

Because it reads from the pipeline, you revoke exactly what you filtered, whether that is the anonymous ones, the expired ones, or everything on one site. I would not do a blanket “remove every link in the tenant”, because most of those links are shares people meant to create. Find the genuinely bad ones, agree them with the site owners, then remove that set.

Running it unattended

For a weekly report on a schedule, swap the browser sign-in for a certificate:

Get-SharingLink -TenantWide -TenantAdminUrl $admin `
    -ClientId $id -CertificatePath .\audit.pfx -Tenant contoso.onmicrosoft.com |
    Export-SharingLinkReport -Path .\weekly-sharing.csv

That version needs the app to have Sites.FullControl.All, which is a high permission. Grant it deliberately, to a dedicated app, not to the interactive one you use day to day.

That is the whole thing

Audit a site or the tenant, filter to what is risky, report it, revoke what you agree to. It found genuine org-wide edit links the first time I pointed it at a real tenant, which is rather the point. This stuff is already there, quietly, waiting for Copilot to surface it.

It is free and open source. If you find a bug or it misses something, the GitHub repo is the place. I would rather fix it than have you work around it 🙂