Src/Private/Get-AbrSPSharingPolicy.ps1
|
function Get-AbrSPSharingPolicy { <# .SYNOPSIS Documents SharePoint Online and OneDrive external sharing policies and link settings. .DESCRIPTION Collects and reports on: - Tenant-level external sharing levels (SharePoint and OneDrive) - Anonymous 'Anyone' link settings and expiry - Default sharing link type and permissions - Domain allow/block lists - Guest access expiry settings - ACSC E8 and CIS compliance assessment (if enabled) .NOTES Version: 0.1.0 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting SharePoint Sharing Policy data for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Sharing Policy' } process { Section -Style Heading2 'Sharing Policy' { Paragraph "The following section documents the external sharing configuration for SharePoint Online and OneDrive for Business in tenant $TenantId. Sharing settings control who can access content outside the organisation." BlankLine # Initialise variables used by compliance checks $SPExternalSharingLevel = 'Unknown' $ODExternalSharingLevel = 'Unknown' $SPExternalSharingLevelNum = 99 $ODExternalSharingLevelNum = 99 $AnyoneLinkEnabled = $false $AnyoneLinkExpiryDays = 0 $DefaultSharingLinkType = 'Unknown' $RequireAcceptingAccountMatchInvitedAccount = $false $NotifyOwnersWhenItemsShared = $false $ExternalUserExpireInDays = 0 # Map SPO sharing level enum values to readable strings and numeric rank $SharingLevelMap = @{ 'Disabled' = @{ Label = 'Disabled'; Rank = 0 } 'ExistingExternalUserSharingOnly'= @{ Label = 'Existing guests only'; Rank = 1 } 'ExternalUserSharingOnly' = @{ Label = 'New and existing guests'; Rank = 2 } 'ExternalUserAndGuestSharing' = @{ Label = 'Anyone (including anonymous)'; Rank = 3 } } #region Sharing Settings via PnP try { if (-not $script:PnPAvailable) { Paragraph " [!] PnP.PowerShell is not available. Detailed sharing policy settings require PnP.PowerShell. Install with: Install-Module -Name PnP.PowerShell -Force" } else { $SPTenant = Get-PnPTenant -ErrorAction Stop # Decode sharing levels $spLevelKey = $(ConvertTo-SPEnumString $SPTenant.SharingCapability 'Disabled') $odLevelKey = $(ConvertTo-SPEnumString $SPTenant.OneDriveSharingCapability 'Disabled') $SPExternalSharingLevel = if ($SharingLevelMap[$spLevelKey]) { $SharingLevelMap[$spLevelKey].Label } else { $spLevelKey } $ODExternalSharingLevel = if ($SharingLevelMap[$odLevelKey]) { $SharingLevelMap[$odLevelKey].Label } else { $odLevelKey } $SPExternalSharingLevelNum = if ($SharingLevelMap[$spLevelKey]) { $SharingLevelMap[$spLevelKey].Rank } else { 99 } $ODExternalSharingLevelNum = if ($SharingLevelMap[$odLevelKey]) { $SharingLevelMap[$odLevelKey].Rank } else { 99 } $AnyoneLinkEnabled = ($spLevelKey -eq 'ExternalUserAndGuestSharing') $AnyoneLinkExpiryDays = $SPTenant.RequireAnonymousLinksExpireInDays $AnyoneLinkState = if ($AnyoneLinkEnabled) { 'Enabled' } else { 'Disabled' } $DefaultSharingLinkType = $(ConvertTo-SPEnumString $SPTenant.DefaultSharingLinkType 'Direct') $RequireAcceptingAccountMatchInvitedAccount = $SPTenant.RequireAcceptingAccountMatchInvitedAccount $NotifyOwnersWhenItemsShared = $SPTenant.NotifyOwnersWhenItemsReshared $ExternalUserExpireInDays = $SPTenant.ExternalUserExpireInDays #region Sharing Summary Table $ShareObj = [System.Collections.ArrayList]::new() $shareInObj = [ordered] @{ 'SharePoint External Sharing' = $SPExternalSharingLevel 'OneDrive External Sharing' = $ODExternalSharingLevel 'Anonymous (Anyone) Links' = $AnyoneLinkState 'Anyone Link Expiry (days)' = if ($AnyoneLinkExpiryDays -gt 0) { $AnyoneLinkExpiryDays } else { 'No expiry set' } 'Default Sharing Link Type' = $DefaultSharingLinkType 'Default Link Permission' = $(ConvertTo-SPEnumString $SPTenant.DefaultLinkPermission 'View') 'Require Account Match for Invitations' = $RequireAcceptingAccountMatchInvitedAccount 'Notify Owners When Items Re-shared' = $NotifyOwnersWhenItemsShared 'Guest Access Expiry (days)' = if ($ExternalUserExpireInDays -gt 0) { $ExternalUserExpireInDays } else { 'Not configured' } 'Prevent External Users from Re-sharing' = $SPTenant.PreventExternalUsersFromResharing 'Allow Guests Sign In with Email' = $SPTenant.AllowGuestUserToSignIn } $ShareObj.Add([pscustomobject](ConvertTo-HashToYN $shareInObj)) | Out-Null $null = (& { if ($HealthCheck.SharePoint.SharingPolicy) { # Flag 'Anyone' sharing as critical $null = ($ShareObj | Where-Object { $_.'SharePoint External Sharing' -like '*Anyone*' } | Set-Style -Style Critical | Out-Null) $null = ($ShareObj | Where-Object { $_.'OneDrive External Sharing' -like '*Anyone*' } | Set-Style -Style Critical | Out-Null) } }) $TableParams = @{ Name = "External Sharing Settings - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $ShareObj | Table @TableParams BlankLine #endregion #region Domain Allow/Block List try { $AllowedDomains = $SPTenant.SharingAllowedDomainList $BlockedDomains = $SPTenant.SharingBlockedDomainList $DomainMode = $(ConvertTo-SPEnumString $SPTenant.SharingDomainRestrictionMode 'None') if ($DomainMode -ne 'None' -or $AllowedDomains -or $BlockedDomains) { $DomainObj = [System.Collections.ArrayList]::new() $domainInObj = [ordered] @{ 'Domain Restriction Mode' = $DomainMode 'Allowed Domains' = if ($AllowedDomains) { $AllowedDomains -join ', ' } else { 'None configured' } 'Blocked Domains' = if ($BlockedDomains) { $BlockedDomains -join ', ' } else { 'None configured' } } $DomainObj.Add([pscustomobject]$domainInObj) | Out-Null $DomainTableParams = @{ Name = "Sharing Domain Restrictions - $TenantId"; List = $true; ColumnWidths = 40, 60 } if ($Report.ShowTableCaptions) { $DomainTableParams['Caption'] = "- $($DomainTableParams.Name)" } $DomainObj | Table @DomainTableParams BlankLine } } catch { Write-AbrDebugLog "Could not retrieve domain restriction settings: $($_.Exception.Message)" 'WARN' 'SHARING' } #endregion # Add to Excel $script:ExcelSheets['Sharing Policy'] = $ShareObj } } catch { Write-AbrSectionError -Section 'Sharing Policy' -Message "$($_.Exception.Message)" } #endregion #region ACSC E8 Assessment if ($script:IncludeACSCe8) { BlankLine Paragraph "ACSC Essential Eight Assessment -- External Sharing" $E8Defs = Get-AbrSPE8Checks -Section 'SharingPolicy' $E8Vars = @{ SPExternalSharingLevel = $SPExternalSharingLevel ODExternalSharingLevel = $ODExternalSharingLevel SPExternalSharingLevelNum = $SPExternalSharingLevelNum ODExternalSharingLevelNum = $ODExternalSharingLevelNum AnyoneLinkEnabled = $AnyoneLinkEnabled AnyoneLinkExpiryDays = $AnyoneLinkExpiryDays AnyoneLinkState = if ($AnyoneLinkEnabled) { 'Enabled' } else { 'Disabled' } RequireAcceptingAccountMatchInvitedAccount = $RequireAcceptingAccountMatchInvitedAccount NotifyOwnersWhenItemsShared = $NotifyOwnersWhenItemsShared } $E8Checks = Build-AbrSPComplianceChecks -Definitions $E8Defs -Framework 'E8' -CallerVariables $E8Vars New-AbrSPE8AssessmentTable -Checks $E8Checks -Name 'Sharing Policy' -TenantId $TenantId # Accumulate for consolidated E8 sheet foreach ($row in $E8Checks) { $null = $script:E8AllChecks.Add([pscustomobject](@{ Section = 'Sharing Policy' } + ($row | ConvertTo-HashTableSP))) } } #endregion #region CIS Baseline Assessment if ($script:IncludeCISBaseline) { BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Sharing Policy" $CISDefs = Get-AbrSPCISChecks -Section 'SharingPolicy' $CISVars = @{ SPExternalSharingLevel = $SPExternalSharingLevel ODExternalSharingLevel = $ODExternalSharingLevel AnyoneLinkEnabled = $AnyoneLinkEnabled AnyoneLinkExpiryDays = $AnyoneLinkExpiryDays DefaultSharingLinkType = $DefaultSharingLinkType RequireAcceptingAccountMatchInvitedAccount = $RequireAcceptingAccountMatchInvitedAccount ExternalUserExpireInDays = $ExternalUserExpireInDays } $CISChecks = Build-AbrSPComplianceChecks -Definitions $CISDefs -Framework 'CIS' -CallerVariables $CISVars New-AbrSPCISAssessmentTable -Checks $CISChecks -Name 'Sharing Policy' -TenantId $TenantId foreach ($row in $CISChecks) { $null = $script:CISAllChecks.Add([pscustomobject](@{ Section = 'Sharing Policy' } + ($row | ConvertTo-HashTableSP))) } } #endregion } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Sharing Policy' } } function ConvertTo-HashTableSP { <# .SYNOPSIS Converts a PSCustomObject to an ordered hashtable. Used for Excel accumulation. #> [CmdletBinding()] param([Parameter(Mandatory, ValueFromPipeline)][pscustomobject]$InputObject) process { $ht = [ordered]@{} foreach ($prop in $InputObject.PSObject.Properties) { $ht[$prop.Name] = $prop.Value } return $ht } } |