Microsoft 365 Email & Collaboration

Governing SharePoint and OneDrive External Sharing: Tenant vs Site Controls, Sensitivity Labels, and Access Reviews

Oversharing in SharePoint and OneDrive is rarely a single misconfiguration – it is the sum of a permissive tenant default, a site owner who clicked “Anyone,” and a guest invited three years ago who still resolves in the people picker. The fix is not one switch. It is a layered control plane where the tenant sets the ceiling, sensitivity labels enforce per-site behavior, link defaults remove the easy mistakes, and access reviews expire the long tail of guests. This guide builds that plane end to end in SharePoint Online PowerShell, Microsoft Graph, and Purview, with the precedence rules made explicit so you do not promise a control you cannot actually enforce.

The sharing control hierarchy: tenant ceiling vs per-site

The single rule that governs everything below: a site can be more restrictive than the tenant, never more permissive. The tenant-level external sharing setting is a hard ceiling. If the tenant is set to “New and existing guests,” a site owner cannot enable “Anyone” links no matter what they do in the UI – the option is simply unavailable. This is the most-restrictive-wins model, and it is why the tenant value matters more than any single site.

Concern Where the control lives Tooling
Maximum external sharing for the whole tenant SharingCapability on the tenant SPO PowerShell / Graph
Per-site sharing (at or below the ceiling) SharingCapability on the site SPO PowerShell
Default link type and permission DefaultSharingLinkType, DefaultLinkPermission SPO PowerShell
Link expiration for Anyone links RequireAnonymousLinksExpireInDays SPO PowerShell
Domain / security-group allowlists SharingDomainRestrictionMode, allow/block lists SPO PowerShell
Privacy, unmanaged device, sharing per site Container + file sensitivity labels Purview + Graph
Guest lifecycle / expiration Access reviews, B2B settings Entra ID Governance

Licensing reality check: tenant and site sharing controls are part of SharePoint Online and need no add-on. Container sensitivity labels require Microsoft Purview Information Protection (included in M365 E3 for manual labeling; auto-labeling and several enforcement paths need E5). Unmanaged-device download blocking through app-enforced restrictions works with SharePoint controls alone; the Conditional Access app control path needs Entra ID P1 plus Defender for Cloud Apps. Access reviews require Entra ID P2 (or the Entra ID Governance SKU). Verify entitlements before you commit to a steering committee.

Connect once. The SharePoint Online Management Shell carries the tenant and site cmdlets; Graph and Security & Compliance PowerShell handle labels and reviews.

# SharePoint Online admin cmdlets (Set-SPOTenant, Set-SPOSite, ...)
Install-Module Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser
Connect-SPOService -Url https://kloudvin-admin.sharepoint.com

# Purview (sensitivity labels) and Graph (reviews) come later
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Install-Module Microsoft.Graph -Scope CurrentUser

Step 1 – Set the tenant ceiling

SharingCapability accepts four values, ordered from most to least permissive. Pick the most restrictive value that still lets the business function, because everything inherits from here.

Value Meaning
ExternalUserAndGuestSharing “Anyone” links (anonymous, no sign-in) plus guest sharing
ExternalUserSharing New and existing guests; recipients must authenticate
ExistingExternalUserSharing Only guests already in the directory
Disabled Internal sharing only

For most regulated tenants, “New and existing guests” is the right ceiling: external collaboration stays possible, but every external recipient authenticates and is auditable as a guest object. Anonymous “Anyone” links are the highest-risk surface and should be off at the tenant unless a specific business case survives review.

# Tenant ceiling: authenticated guests only, no anonymous links
Set-SPOTenant -SharingCapability ExternalUserSharing

# OneDrive can be set independently and is usually held tighter than SharePoint
Set-SPOTenant -OneDriveSharingCapability ExistingExternalUserSharing

OneDriveSharingCapability is the often-missed companion: it lets you keep team-site collaboration open while clamping personal OneDrive to existing guests only. Confirm the result before moving on.

Get-SPOTenant | Select-Object SharingCapability, OneDriveSharingCapability

Step 2 – External sharing levels and domain / security-group limits

The ceiling controls whether external sharing happens; domain restriction controls with whom. SharingDomainRestrictionMode has three states: None, AllowList (share only with the listed domains), and BlockList (share with everyone except the listed domains). An allowlist is the stronger posture – you enumerate the partners you trust and nothing else gets out.

# Allowlist mode: external sharing only to named partner domains
Set-SPOTenant -SharingDomainRestrictionMode AllowList `
    -SharingAllowedDomainList "contoso.com fabrikam.com"

# Alternative -- blocklist a known consumer / competitor domain
# Set-SPOTenant -SharingDomainRestrictionMode BlockList `
#     -SharingBlockedDomainList "gmail.com"

The domain list is space-delimited and replaces the existing list on every call – there is no append. Read the current value, add to it in your script, and write the full set back, or you will silently drop partners. Treat the list as state in source control, not as something you mutate by hand in the portel.

To restrict who can invite guests at all – a different axis from which domains – use Entra B2B collaboration restrictions, which apply tenant-wide across SharePoint, Teams, and the rest of M365. The cleanest pattern is to permit guest invitations only for members of a designated security group, then govern membership of that group.

# Entra B2B: only members of a security group may invite guests
# allowInvitesFrom = "adminsAndGuestInviters" restricts inviting to
# Global Admins, User Admins, Guest Inviter role, and members of the
# group referenced by guestInviteSettings.
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"

$body = @{ allowInvitesFrom = "adminsAndGuestInviters" } | ConvertTo-Json
Invoke-MgGraphRequest -Method PATCH `
    -Uri "https://graph.microsoft.com/v1.0/policies/authorizationPolicy/authorizationPolicy" `
    -Body $body -ContentType "application/json"

Step 3 – Default link type, expiration, and people-picker restrictions

Most oversharing is accidental: a user hits “Copy link” and accepts whatever the default is. So make the default safe. Two tenant settings carry the weight: DefaultSharingLinkType (what gets created when someone copies a link) and DefaultLinkPermission (view vs edit).

Set-SPOTenant `
    -DefaultSharingLinkType Direct `
    -DefaultLinkPermission View

DefaultSharingLinkType Direct means the default link grants access only to specific people – the user must explicitly add recipients, who then authenticate. This is the single highest-leverage anti-oversharing setting: the lazy path now requires naming a person rather than producing a company-wide or anonymous link. The alternative, Internal, defaults to “people in your organization,” which is exactly the company-wide link you are trying to discourage.

For any anonymous “Anyone” links that remain permitted on specific sites, force expiration and downgrade edit rights:

# Anyone links expire after 30 days and can only view, never edit
Set-SPOTenant `
    -RequireAnonymousLinksExpireInDays 30 `
    -FileAnonymousLinkType View `
    -FolderAnonymousLinkType View

You can also cap how long guest (authenticated external) access survives and require periodic reattestation with ExternalUserExpirationRequired and ExternalUserExpireInDays – a lightweight expiry that complements the heavier access-review machinery in Step 8.

Set-SPOTenant -ExternalUserExpirationRequired $true -ExternalUserExpireInDays 60

Step 4 – Sensitivity-label-driven site controls

Tenant settings are blunt – one ceiling for everyone. Sensitivity labels let you attach different sharing behavior to different sites based on the data they hold, and have it travel with the container. A label scoped to Groups & sites can enforce three site-level controls: privacy (public vs private), external sharing capability, and conditional access for unmanaged devices. Critically, the label-driven sharing setting overrides the per-site SharingCapability for any site that label is applied to, giving you a data-classification-driven policy instead of a per-site one.

Author the label in Security & Compliance PowerShell. The site controls live in SiteAndGroupProtectionEnabled plus the advanced settings.

Connect-IPPSSession -UserPrincipalName admin@kloudvin.com

New-Label -Name "Confidential-NoExternal" `
    -DisplayName "Confidential - Internal Only" `
    -Tooltip "Sites holding confidential data. No external sharing." `
    -SiteAndGroupProtectionEnabled $true

# Private sites, no external (guest) sharing, no access from unmanaged devices
Set-Label -Identity "Confidential-NoExternal" -AdvancedSettings @{
    SiteAndGroupProtectionPrivacy        = "private"
    SiteAndGroupExternalSharingControlType = "ExternalUserSharingOnly"
    SiteAndGroupProtectionAllowAccessToGuestUsers = "False"
    SiteAndGroupProtectionAllowFullAccess = "False"
    SiteAndGroupProtectionAllowLimitedAccess = "True"
    SiteAndGroupProtectionBlockAccess     = "False"
}

SiteAndGroupExternalSharingControlType mirrors the tenant capability values (ExternalUserAndGuestSharing, ExternalUserSharingOnly, ExistingExternalUserSharingOnly, Disabled). The unmanaged-device trio (AllowFullAccess / AllowLimitedAccess / BlockAccess) maps to “full,” “web-only (no download),” and “block” – and these require the SharePoint app-enforced restrictions feature to be on, which is the subject of Step 5.

Publish the label, then a tenant admin (or your provisioning pipeline) applies it to the container – it is not auto-applied to existing sites:

New-LabelPolicy -Name "Site labels" -Labels "Confidential-NoExternal" `
    -ExchangeLocation All
# Apply to an existing site via Graph (group-connected site -> patch the group)
Connect-MgGraph -Scopes "Group.ReadWrite.All"
Update-MgGroup -GroupId "<group-id>" -AssignedLabels @(
    @{ LabelId = "<label-guid>" }
)

Step 5 – Block download on unmanaged devices

There are two layers of unmanaged-device control, and they are easy to conflate.

The SharePoint-native layer is app-enforced restrictions. It is simple, needs no Defender for Cloud Apps, and gives you three outcomes: full access, allow limited web-only access (browser, no download/print/sync), or block. Turn it on at the tenant and it backs the label settings from Step 4.

# Tenant: unmanaged devices get browser-only access, no download/sync
Set-SPOTenant -ConditionalAccessPolicy AllowLimitedAccess

The Entra Conditional Access app control layer is richer: it routes SharePoint sessions through Defender for Cloud Apps as a reverse proxy and can block download specifically while still allowing edit-in-browser, scoped by user, app, and device state. Build it as a CA policy with session controls – “use Conditional Access App Control” -> “Block download” – against unmanaged devices (device not compliant and not Hybrid Entra joined).

{
  "displayName": "SPO - block download on unmanaged devices",
  "state": "enabled",
  "conditions": {
    "applications": { "includeApplications": ["00000003-0000-0ff1-ce00-000000000000"] },
    "users": { "includeUsers": ["All"] },
    "clientAppTypes": ["browser"]
  },
  "grantControls": null,
  "sessionControls": {
    "cloudAppSecurity": {
      "isEnabled": true,
      "cloudAppSecurityType": "blockDownloads"
    }
  }
}

The app ID 00000003-0000-0ff1-ce00-000000000000 is the well-known first-party identifier for “Office 365 SharePoint Online” and is stable across tenants. The CA app-control path requires the session to be browser-based (clientAppTypes: browser); rich desktop and mobile clients are governed instead by app-enforced restrictions or app protection policies. Do not assume blockDownloads covers the Teams or OneDrive desktop client – it does not.

Step 6 – Restrict OneDrive sharing and govern departed-user accounts

OneDrive is personal storage, so it leaks differently: a user shares their own files, then leaves, and the orphaned account keeps sharing. Hold OneDrive tighter than SharePoint (Step 1), and put a deterministic process around departed accounts.

When an account is deleted from Entra, the OneDrive enters a retention window (default 30 days, set by OrphanedPersonalSitesRetentionPeriod, up to 3650). Two things must happen before that window closes: reassign access to a manager so nothing is lost, and revoke any external links the departed user created.

# How long a departed user's OneDrive is retained before permanent deletion
Set-SPOTenant -OrphanedPersonalSitesRetentionPeriod 90

# Grant the manager full access to the departed user's OneDrive
$od = Get-SPOSite -IncludePersonalSite $true -Limit All `
    -Filter "Url -like '-my.sharepoint.com/personal/leaver_kloudvin_com'"
Set-SPOUser -Site $od.Url -LoginName manager@kloudvin.com -IsSiteCollectionAdmin $true

To find and kill external sharing that a specific (departed or current) user created across OneDrive and SharePoint, run the sharing report against their personal site, then act on the offenders. Automated access expiration (Step 3) is the safety net, but a leaver should be remediated immediately rather than waiting 60 days.

Step 7 – Detect oversharing: reports and Restricted SharePoint Search

You cannot govern what you cannot see. SharePoint can generate a per-site sharing report – a CSV of every shared item, who it is shared with, link type, and permission – written to a document library you nominate. Start the job per site and collect them centrally.

$site = "https://kloudvin.sharepoint.com/sites/Finance"
Start-SPOSharingDataExternalReport `
    -ReportName "Finance-external-Q2" `
    -StartTime (Get-Date).AddDays(-90) `
    -EndTime (Get-Date) `
    -FileFormat CSV

For Microsoft 365 Copilot tenants, oversharing has a new blast radius: Copilot can surface content a user technically has access to but never knew existed. Restricted SharePoint Search (RSS) is the emergency brake – it restricts both org-wide search and Copilot to an admin-curated allowlist of up to 100 sites plus each user’s OneDrive, buying time while you remediate permissions properly. It is a stopgap, not a destination, because it disables enterprise search broadly.

# Enable Restricted SharePoint Search, then curate the allowed sites
Set-SPOTenantRestrictedSearchMode -Mode Enabled
Add-SPOTenantRestrictedSearchAllowedList -Url "https://kloudvin.sharepoint.com/sites/Finance"

RSS is deliberately coarse. The durable fix for Copilot oversharing is Restricted Content Discovery on individual sensitive sites (excludes a site from Copilot/search results without breaking direct access) combined with sensitivity labels and tightened permissions. Reach for RSS to stop the bleeding on day one; reach for Restricted Content Discovery and proper permissioning for the long term.

Step 8 – Guest lifecycle: access reviews, expiration, and B2B cleanup

Every guest you ever invited is a standing risk until something expires them. The system, not a person, must do this on a schedule. Entra access reviews are the durable control: a recurring review where group owners (or named reviewers) reattest each guest, and unreviewed or denied guests are removed automatically.

Create a quarterly review over all guests with auto-apply of denied results – this is where most “guest who left the partner two years ago” accounts finally die.

Connect-MgGraph -Scopes "AccessReview.ReadWrite.All"

$params = @{
  displayName = "Quarterly guest access review"
  descriptionForAdmins = "Reattest all guests; remove if not approved"
  scope = @{
    "@odata.type" = "#microsoft.graph.accessReviewQueryScope"
    query = "/users?`$filter=(userType eq 'Guest')"
    queryType = "MicrosoftGraph"
  }
  reviewers = @(@{ query = "/users?`$filter=userType eq 'Member'"; queryType = "MicrosoftGraph" })
  settings = @{
    mailNotificationsEnabled = $true
    reminderNotificationsEnabled = $true
    defaultDecisionEnabled = $true
    defaultDecision = "Deny"               # no decision -> remove
    autoApplyDecisionsEnabled = $true
    instanceDurationInDays = 14
    recurrence = @{
      pattern = @{ type = "absoluteMonthly"; interval = 3; dayOfMonth = 1 }
      range   = @{ type = "noEnd"; startDate = "2026-07-01" }
    }
  }
}
New-MgIdentityGovernanceAccessReviewDefinition -BodyParameter $params

Pair reviews with a hard backstop: B2B accounts inactive for a defined period should be removed regardless of any review outcome. Query last sign-in via Graph and disable, then delete, stale guests.

# Guests with no interactive sign-in in 90 days
$cutoff = (Get-Date).AddDays(-90).ToString("o")
Get-MgUser -Filter "userType eq 'Guest'" `
  -Property "id,displayName,signInActivity" -All |
  Where-Object { $_.SignInActivity.LastSignInDateTime -lt $cutoff } |
  ForEach-Object { Update-MgUser -UserId $_.Id -AccountEnabled:$false }

Verify

Confirm each layer independently – a control you cannot prove is a control you do not have.

# 1. Tenant ceiling and OneDrive clamp
Get-SPOTenant | Select-Object SharingCapability, OneDriveSharingCapability, `
    DefaultSharingLinkType, DefaultLinkPermission, `
    RequireAnonymousLinksExpireInDays, SharingDomainRestrictionMode

# 2. A specific site is at or below the ceiling
Get-SPOSite -Identity "https://kloudvin.sharepoint.com/sites/Finance" |
    Select-Object Url, SharingCapability, ConditionalAccessPolicy

# 3. The site carries the expected sensitivity label
Get-SPOSite -Identity "https://kloudvin.sharepoint.com/sites/Finance" |
    Select-Object Url, SensitivityLabel
# 4. The access review exists and is recurring
Get-MgIdentityGovernanceAccessReviewDefinition |
    Where-Object DisplayName -eq "Quarterly guest access review" |
    Select-Object Id, DisplayName, Status

Then validate behaviorally, which catches what config dumps miss:

Enterprise scenario

A 9,000-seat engineering firm rolled out Microsoft 365 Copilot and within two weeks the security team had escalations: Copilot was summarizing salary bands and an unannounced acquisition deck for users who had never opened those files. Nothing was breached – every result came from content the asking user technically had access to via a years-old “people in your organization” link on a finance site. The constraint was brutal: legal wanted Copilot oversharing stopped within 48 hours, but the platform team could not re-permission tens of thousands of files in two days, and disabling Copilot entirely was off the table after a board demo.

They solved it in two moves. Day one, they enabled Restricted SharePoint Search to immediately cap Copilot and org-wide search to a 40-site curated allowlist that explicitly excluded Finance and Corp-Dev – this stopped the bleeding tenant-wide in minutes. Over the following weeks, they replaced the blunt RSS instrument with targeted controls: a Highly Confidential container label enforcing private + no-external on the sensitive sites, Restricted Content Discovery to keep those specific sites out of Copilot grounding without breaking direct access for the people who legitimately needed it, and a tenant flip of DefaultSharingLinkType to Direct so no new “company-wide” links could be minted by accident. Then they turned RSS back off and restored full enterprise search.

The flip that bought them the 48 hours:

Set-SPOTenantRestrictedSearchMode -Mode Enabled
"https://corp.sharepoint.com/sites/Engineering",
"https://corp.sharepoint.com/sites/Support" |
  ForEach-Object { Add-SPOTenantRestrictedSearchAllowedList -Url $_ }

The lesson the architecture team took away: RSS is a tourniquet, not a treatment. It stops loss fast but degrades search for everyone, so the plan must include the labeled, per-site controls that let you remove it. Tenants that enabled RSS and never followed through ended up with permanently crippled search and unhappy users – and the oversharing was still there underneath, waiting for the day someone turned RSS off.

Checklist

SharePointOneDriveExternal SharingSensitivity LabelsGovernance

Comments

Keep Reading