|

User Access Explorer: a tool to see what a user can reach in SharePoint

A tenant user logs into Copilot, tells Copilot to summarize last quarter’s data and Copilot returns a number from a workbook that he/she wasn’t supposed to access. All along the permission was there; no one saw it as SharePoint has no unified view that answers the one question that an admin always needs answered – what could this user see and how?

I was tired of piecing together that answer manually, so I created a little tool to do just that. It is named User Access Explorer, it is free and MIT-licensed and it is available at github.com/gvijaikumar9/UserAccessExplorer.

What it actually does

Choose your user. The tool traverses the sites he or she has access to and displays every route through which this user gains access, one row per route. Access through such a route comes either from a direct grant, from membership of a SharePoint group, an Entra (M365) group, an Everyone or Everyone except external users claim, or a share.

Important routes are ranked higher. I classify each access into Granted (granted on purpose by someone) and Overshared (a claim or a share granted access to too many people). This Overshared classification came from Microsoft itself, where they use this term for the Copilot and SharePoint Advanced Management reports, so I did not change it. This Overshared access is the only purpose of the tool. This list is what you give to the owner of the site and demand an explanation of.

A few facts that I found useful while developing it:

  • Not Contributor. Reading permissions require Full Control. The script with just a contributor account would produce confidently incorrect results because such script simply doesn’t know the role assignments. You need Site Owner or Site Collection Admin for one site. You need an application registration with Sites.FullControl.All for a whole tenant, since the tenant admin does not mean site admin.
  • The “scope” in the sharing link group name is just a scam. A link is internally represented as a group called SharingLinks.<guid>.Flexible.<id>. And the “Flexible” here is just the name of that scope, which tells you absolutely nothing about the scope itself. Get-PnPFileSharingLink is the only way to retrieve the actual scope. Always.
  • A sharing link is not a user-per-user permission. When the tool checks “what a particular user can do with a particular file,” links won’t appear in any of the result scopes because links assign permissions not to the user but to some internal group. That is why the tool has to answer a second question about each item that was shared – whether the link audience contains this user or not. Anonymous and Organization links do contain them. Users link only if user’s name is mentioned in the audience.

What you need

  • PowerShell 7.2 or newer.
  • PnP.PowerShell 2.12 or newer (Install-Module PnP.PowerShell).
  • An Entra app registration you can sign in with interactively. If you do not have one, the next step makes one. The GUI also has a “Register one for this tenant” link in the connect box that does the same thing.
  • For the extras: User.ReadBasic.All so the people picker can search, and GroupMember.Read.All so it can confirm group membership instead of guessing.

Register the app

Skip this if you already have an app registration with SharePoint Full Control. If not, PnP will create one for you and walk you through consent in the browser:

Register-PnPEntraIDAppForInteractiveLogin `
    -ApplicationName "User Access Explorer" `
    -Tenant contoso.onmicrosoft.com `
    -Interactive

It opens a sign-in window and asks you to grant consent. The one thing to get right is the consent: this tool reads permissions, so it needs Sites.FullControl.All, and for the people picker and membership checks it needs the Graph scopes User.ReadBasic.All and GroupMember.Read.All. Granting that consent needs a tenant admin, so if that is not you, send them the link the command prints.

When it finishes it prints the new Client ID. Copy it, you paste it into the tool in a moment.

Install it

git clone https://github.com/gvijaikumar9/UserAccessExplorer.git
cd UserAccessExplorer
Import-Module .\UserAccessExplorer.psd1

That gives you the PowerShell commands. For the app, run the GUI script:

pwsh -File .\gui\Show-UserAccessExplorer.ps1

Using the app

The connect box opens on launch. Paste your app’s Client ID and your tenant admin URL (the ...-admin.sharepoint.com one), sign in, and the chip at the top right turns green with your tenant name.

From there:

  1. Start typing a name or email in User. It searches after the second letter and drops the matches below, so you pick rather than type the whole thing.
  2. Choose a Scope. One site checks a single site. One site (deep) walks that site’s subsites, lists and items that have their own broken-inheritance permissions, which is where oversharing hides. Whole tenant sweeps every site, and it is slow, so it runs in the background with a progress count and a Stop button.
  3. Hit Scan.

The four tiles on top are the summary of all the information you need to have at once: the number of routes, the number of them being Overshared (if the tile turns pink, there are Overshared routes), the number of sites, and the highest level of access someone has among those routes. The matrix below shows each route in one line with a column “Grant Path” that looks like Everyone Claim -> Read or Marketing Members -> Edit. Sort by clicking on any column’s header, group or filter with the chevron. The little link icon in the column “Site” allows you to access the page in SharePoint immediately..

 

Comparing two people

Change the “Single user” tag and add another user so that the comparison can be done. You will get three buckets: Shared, First alone, Second alone. This is the tool I use whenever I am told to give a new starter “the same access as her manager,” and I need to know exactly what the differences between these two users are.

All completed scans are stored in Scan history, and thus they can always be opened again at a later time without SharePoint access, and there is also an export option that produces an HTML report.

Turn it around: who can reach a site

And there is the mirror of this question, which is equally important. Instead of a user, take a site and check who can access it and how. Toggle the By user / By site switch in the header, and now you will get the site as the subject.

Then every row is the principal capable of accessing it – a user, SharePoint group, Entra group, Everyone claim or a sharing link – and once again Overshared items are on top. The groups have their member count in the name, so Marketing Members (2) and Marketing Members (340) are easily distinguishable. Do it thoroughly, and it will analyze the subsites, document libraries and individual documents, so if a document is shared with Everyone three levels down the hierarchy, it will appear in a separate row.

 

This is the perspective to have going into a site when you unleash Copilot on it. The issue becomes not just whether one person sees something but whether someone sees it, how many people see it, and whether someone wanted them to.

To view scan history and reports

If you prefer the command line

The GUI is a shell over the module, so everything is scriptable. The one flag to remember is -OversharedOnly, which drops the noise and leaves you only the routes worth acting on:

Get-UserAccess -User jane@contoso.com `
    -SiteUrl "https://contoso.sharepoint.com/sites/Marketing" `
    -ClientId "<your-app-id>" -Interactive -OversharedOnly |
    Export-UserAccessReport -Path .\jane-access.html -Html

Swap -SiteUrl for -TenantWide -TenantAdminUrl "https://contoso-admin.sharepoint.com" to sweep everything, or add -Deep -IncludeItems to go down to the file level on a single site.

The by-site side has its own command, Get-SiteAccess:

Get-SiteAccess -SiteUrl "https://contoso.sharepoint.com/sites/Marketing" `
    -ClientId "<your-app-id>" -Interactive -Deep -OversharedOnly

Add -ExpandMembers and every group becomes one row per person, for the full list of who can actually reach the site.

It pairs well with the sharing-link work in my PnP PowerShell posts. If you run it and something looks off, open an issue on the repo.

Related posts

Similar Posts