A decision tree for the permission ticket. SharePoint access is decided by up to five layers stacked on top of each other; the trick is checking them in the order they actually apply, not the order the portal shows them.
"Access denied" conflates two different failures: the user never authenticated to SharePoint at all (a sign-in problem wearing a SharePoint costume), or they authenticated fine and SharePoint's authorisation layers said no. Separate those first, because the fix lives in completely different places.
Pin down: the exact URL that fails (site, library or single file; they triage differently), the user's UPN, whether they are internal or external/guest, whether it ever worked for them, and the exact error text. "Access denied" and "You need permission" and "This link has been removed" are three different problems.
Does the user get past authentication?
Error before any SharePoint page renders (sign-in loop, AADSTS code, "your sign-in was blocked"): this is not a SharePoint permission issue. Take the AADSTS code to the sign-in triage guide. Device-compliance and unmanaged-device policies (53000-family) hit SharePoint particularly often because of SharePoint's own unmanaged-device access controls.
SharePoint renders and then says "Access denied" / "You need permission to access this site": authentication worked; authorisation refused. Continue to Step 2.
"This link has been removed" or "link doesn't work": a sharing-link problem, not a permission problem. Skip to Step 4.
Check the user's effective presence on the site collection:
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/TheSite" -Interactive Get-PnPUser | Where-Object Email -eq "user@contoso.com" Get-PnPGroup | Select-Object Title, Users
User absent entirely: they were never granted access. Decide the right level and add them through a site group, not directly, so the next audit stays readable.
Add-PnPGroupMember -Group "TheSite Members" -LoginName "user@contoso.com"
User present but access still denied: the deny is on something narrower than the site. Go to Step 3.
Group-connected site (most team sites): membership should be managed through the Microsoft 365 group, not SharePoint groups. Check Get-MgGroupMember for the owning group; a user added to the SharePoint group but not the M365 group gets inconsistent access across Teams, Planner and the site.
The site owner can answer this faster than PowerShell can: "Site permissions" in the site's gear menu. PowerShell earns its keep when you manage many tenants or need the answer for many users at once.
Site access is fine but one library, folder or file denies. Does it have unique permissions?
Get-PnPList -Identity "Documents" -Includes HasUniqueRoleAssignments | Select-Object Title, HasUniqueRoleAssignments
HasUniqueRoleAssignments is True: someone broke inheritance, usually years ago via "Stop sharing" or a fine-grained share. The user's site-level access does not apply there. Either add them at that level or, better, restore inheritance if the split was accidental:
Set-PnPList -Identity "Documents" -ResetRoleInheritance
False: permissions flow from the site, so the deny is coming from somewhere else entirely, most likely a sensitivity label with encryption, or a Conditional Access / unmanaged-device restriction that degrades rather than blocks. Check whether the user is on a personal device and whether the file has a label.
Broken inheritance is the top cause of "but they're a member of the site!" tickets, and it is invisible in the modern UI until you look for it. Deep folder hierarchies with per-folder sharing accumulate dozens of unique-permission islands; resetting inheritance and re-sharing deliberately is usually kinder than patching one user in.
Which link failure is it?
"This link has been removed": the share was deleted, or the file moved/renamed (links do not follow moves between sites). Re-share from the current location.
"This link works for people with existing access" surprise: the sharer picked the wrong link type. "People with existing access" grants nothing new; the recipient needed a "specific people" link. Have it re-shared with the right audience.
External recipient gets "you need permission" after signing in: identity mismatch. The invite went to one address and they signed in with another (personal vs work account is the classic). Re-invite to the address they will actually authenticate with, or check the guest object:
Get-MgUser -Filter "mail eq 'partner@fabrikam.com'" | Select-Object DisplayName, UserPrincipalName, ExternalUserState
ExternalUserState is PendingAcceptance: the invitation was never redeemed. Resend it with New-MgInvitation.
External users fail on one site but work elsewhere (or nowhere). Compare the sharing capability at both levels:
Get-PnPTenant | Select-Object SharingCapability Get-PnPTenantSite -Url "https://contoso.sharepoint.com/sites/TheSite" | Select-Object Url, SharingCapability, ConditionalAccessPolicy
Site is more restrictive than the tenant: normal and often deliberate (a finance or HR site set to Disabled). Confirm with the data owner before loosening; the restriction usually exists for a reason.
Tenant is the blocker: tenant SharingCapability caps every site; a site can never share more broadly than the tenant allows. Changing this is a security decision, not a support fix, so check who set it and why first.
Domain allow/block lists: sharing may be restricted to specific partner domains. Check SharingAllowedDomainList / SharingBlockedDomainList on the tenant.
The failing URL is under /personal/, which is someone's OneDrive.
Accessing a leaver's OneDrive: grant a site collection admin rather than fighting share links. This is the standard offboarding move, automated in the incident builder's leaver runbook:
Set-PnPTenantSite -Url "https://contoso-my.sharepoint.com/personal/leaver_contoso_com" -Owners "manager@contoso.com"
"User's OneDrive not provisioned": first-ever access provisions it lazily; it may simply not exist yet. Have the user visit OneDrive once, or pre-provision with Request-PnPPersonalSite.
OneDrive of a deleted user: retained 30 days by default (configurable up to 10 years via Set-PnPTenant -OrphanedPersonalSitesRetentionPeriod). Past retention, it is gone, so check the retention setting before promising recovery.
Many users failing on many sites at once points above the permission layers: a Conditional Access change (check the CA baseline guide and recent policy edits in the audit log), the unmanaged-device access control tightened tenant-wide (ConditionalAccessPolicy on Get-PnPTenant), or an authentication change like legacy-auth blocking finally landing. The audit-log command for "who changed what" is in the sign-in guide, Step 8.
Authentication or authorisation first: AADSTS errors go to the sign-in guide. Then walk the layers from widest to narrowest: tenant sharing capability, site sharing setting, site membership, broken inheritance on the library or item, and finally the sharing link itself. External users add one more check: did they redeem the invite with the identity it was sent to. The layer that says no is almost never the one the ticket blames.