Src/Private/Get-AbrSPExternalAccess.ps1
|
function Get-AbrSPExternalAccess { <# .SYNOPSIS Documents SharePoint Online external access controls, device access policies, idle session settings, and network location restrictions. .NOTES Version: 0.1.2 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting SharePoint External Access data for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'External Access' } process { Section -Style Heading2 'External Access Controls' { Paragraph "The following section documents the access control policies applied to SharePoint Online and OneDrive for Business for tenant $TenantId." BlankLine # Compliance check variables -- safe defaults $ConditionalAccessPolicy = 'AllowFullAccess' $SignOutInactiveUsersAfter = 0 $BlockDownloadPolicy = 'Not configured' $BlockDownloadLinksFileType = 'None' $DefaultSharingLinkType = 'Unknown' try { if (-not $script:PnPAvailable) { Paragraph " [!] PnP.PowerShell is not available. Detailed access control settings require PnP.PowerShell." } else { $SPTenant = Get-PnPTenant -ErrorAction Stop # Safe enum-to-string: guard every .ToString() against null $ConditionalAccessPolicy = ConvertTo-SPEnumString $SPTenant.ConditionalAccessPolicy 'AllowFullAccess' $DefaultSharingLinkType = ConvertTo-SPEnumString $SPTenant.DefaultSharingLinkType 'Direct' #region Access Control Summary $AccessObj = [System.Collections.ArrayList]::new() $CALabel = switch ($ConditionalAccessPolicy) { 'AllowFullAccess' { 'Allow full access (unmanaged devices permitted)' } 'AllowLimitedAccess' { 'Limited web-only access (no download/print/sync)' } 'BlockAccess' { 'Block access from unmanaged devices' } default { $ConditionalAccessPolicy } } $accessInObj = [ordered] @{ 'Unmanaged Device Policy' = $CALabel 'Block Download (All File Types)' = ($null -ne $SPTenant.BlockDownloadLinksFileType -and "$($SPTenant.BlockDownloadLinksFileType)" -ne 'None') 'Sync Client Domain Restriction' = ($null -ne $SPTenant.AllowedDomainGuidsForSyncApp -and @($SPTenant.AllowedDomainGuidsForSyncApp).Count -gt 0) 'Legacy Authentication Protocols' = $SPTenant.LegacyAuthProtocolsEnabled 'Email Attestation Required' = $SPTenant.EmailAttestationRequired 'Email Attestation Re-auth Days' = if ($SPTenant.EmailAttestationReAuthDays -gt 0) { $SPTenant.EmailAttestationReAuthDays } else { 'Not set' } } $AccessObj.Add([pscustomobject](ConvertTo-HashToYN $accessInObj)) | Out-Null $null = (& { if ($HealthCheck.SharePoint.ExternalAccess) { $null = ($AccessObj | Where-Object { $_.'Unmanaged Device Policy' -like '*unmanaged devices permitted*' } | Set-Style -Style Warning | Out-Null) $null = ($AccessObj | Where-Object { $_.'Legacy Authentication Protocols' -eq 'Yes' } | Set-Style -Style Warning | Out-Null) } }) $TableParams = @{ Name = "Access Control Settings - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $AccessObj | Table @TableParams BlankLine #endregion #region Idle Session Sign-Out try { $IdleObj = [System.Collections.ArrayList]::new() $SignOutInactiveUsersAfter = if ($SPTenant.SignOutInactiveUsersAfter) { $SPTenant.SignOutInactiveUsersAfter } else { 0 } $SignOutEnabled = ($SignOutInactiveUsersAfter -gt 0) $WarnBeforeSignOut = if ($SPTenant.WarnBeforeSignOut) { $SPTenant.WarnBeforeSignOut } else { 0 } $idleInObj = [ordered] @{ 'Idle Session Sign-Out Enabled' = $SignOutEnabled 'Sign Out After (minutes)' = if ($SignOutInactiveUsersAfter -gt 0) { $SignOutInactiveUsersAfter } else { 'Not configured' } 'Warn Before Sign-Out (minutes)' = if ($WarnBeforeSignOut -gt 0) { $WarnBeforeSignOut } else { 'Not configured' } } $IdleObj.Add([pscustomobject](ConvertTo-HashToYN $idleInObj)) | Out-Null $null = (& { if ($HealthCheck.SharePoint.ExternalAccess) { $null = ($IdleObj | Where-Object { $_.'Idle Session Sign-Out Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null) } }) $IdleTableParams = @{ Name = "Idle Session Sign-Out - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($Report.ShowTableCaptions) { $IdleTableParams['Caption'] = "- $($IdleTableParams.Name)" } $IdleObj | Table @IdleTableParams BlankLine } catch { Write-AbrDebugLog "Could not retrieve idle session settings: $($_.Exception.Message)" 'WARN' 'EXTERNAL-ACCESS' } #endregion #region Network Location (IP Restriction) try { $IPRanges = $SPTenant.IPAddressAllowList if ($IPRanges) { $NetObj = [System.Collections.ArrayList]::new() $netInObj = [ordered] @{ 'IP Address Restriction Enabled' = $true 'Enforcement Mode' = if ($SPTenant.IPAddressEnforcement) { 'Enforced' } else { 'Audit only' } 'Allowed IP Ranges' = ($IPRanges -join ', ') } $NetObj.Add([pscustomobject](ConvertTo-HashToYN $netInObj)) | Out-Null $NetTableParams = @{ Name = "Network Location (IP Restriction) - $TenantId"; List = $true; ColumnWidths = 40, 60 } if ($Report.ShowTableCaptions) { $NetTableParams['Caption'] = "- $($NetTableParams.Name)" } $NetObj | Table @NetTableParams } else { Paragraph "Network location (IP range) restriction: Not configured. All IP addresses are permitted." } BlankLine } catch { Write-AbrDebugLog "Could not retrieve network location settings: $($_.Exception.Message)" 'WARN' 'EXTERNAL-ACCESS' } #endregion $BlockDownloadLinksFileType = ConvertTo-SPEnumString $SPTenant.BlockDownloadLinksFileType 'None' $BlockDownloadPolicy = if ($ConditionalAccessPolicy -ne 'AllowFullAccess') { $ConditionalAccessPolicy } else { 'AllowFullAccess' } $script:ExcelSheets['External Access Controls'] = $AccessObj } } catch { Write-AbrSectionError -Section 'External Access Controls' -Message "$($_.Exception.Message)" } #region ACSC E8 Assessment if ($script:IncludeACSCe8) { BlankLine Paragraph "ACSC Essential Eight Assessment -- External Access Controls" $E8Defs = Get-AbrSPE8Checks -Section 'ExternalAccess' $E8Vars = @{ ConditionalAccessPolicy = $ConditionalAccessPolicy SignOutInactiveUsersAfter = $SignOutInactiveUsersAfter BlockDownloadPolicy = $BlockDownloadPolicy BlockDownloadLinksFileType = $BlockDownloadLinksFileType DefaultSharingLinkType = $DefaultSharingLinkType RequireAcceptingAccountMatchInvitedAccount = $false NotifyOwnersWhenItemsShared = $false } $E8Checks = Build-AbrSPComplianceChecks -Definitions $E8Defs -Framework 'E8' -CallerVariables $E8Vars New-AbrSPE8AssessmentTable -Checks $E8Checks -Name 'External Access' -TenantId $TenantId foreach ($row in $E8Checks) { $null = $script:E8AllChecks.Add([pscustomobject](@{ Section = 'External Access' } + ($row | ConvertTo-HashTableSP))) } } #endregion #region CIS Baseline Assessment if ($script:IncludeCISBaseline) { BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- External Access Controls" $CISDefs = Get-AbrSPCISChecks -Section 'ExternalAccess' $CISVars = @{ ConditionalAccessPolicy = $ConditionalAccessPolicy SignOutInactiveUsersAfter = $SignOutInactiveUsersAfter DefaultSharingLinkType = $DefaultSharingLinkType } $CISChecks = Build-AbrSPComplianceChecks -Definitions $CISDefs -Framework 'CIS' -CallerVariables $CISVars New-AbrSPCISAssessmentTable -Checks $CISChecks -Name 'External Access' -TenantId $TenantId foreach ($row in $CISChecks) { $null = $script:CISAllChecks.Add([pscustomobject](@{ Section = 'External Access' } + ($row | ConvertTo-HashTableSP))) } } #endregion } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'External Access' } } |