Src/Private/Get-AbrSPCompliance.ps1
|
function Get-AbrSPCompliance { <# .SYNOPSIS Documents SharePoint and OneDrive compliance settings. .NOTES Version: 0.1.5 Author: Pai Wei Sing #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting SharePoint Compliance data for $TenantId." Show-AbrDebugExecutionTime -Start -TitleMessage 'Compliance' } process { #region Pre-compute all values BEFORE any Section{} block # CRITICAL: PScribo Section{} creates a child scope. Variables assigned inside # a Section{} block are NOT visible outside it. All data must be gathered here, # in the process{} scope, before any Section{} is opened. # Audit log status $AuditEnabled = $true # M365 enables this by default for all tenants (Oct 2023) $AuditNote = 'Microsoft 365 enables unified audit logging by default for all tenants (since October 2023). Verify status and retention period in Microsoft Purview > Audit.' $AuditSource = 'M365 platform default' if (Get-Command Get-AdminAuditLogConfig -ErrorAction SilentlyContinue) { try { $AuditConfig = Get-AdminAuditLogConfig -ErrorAction Stop $AuditEnabled = [bool]$AuditConfig.UnifiedAuditLogIngestionEnabled $AuditNote = if ($AuditEnabled) { 'Unified audit log ingestion is ENABLED (verified via Exchange Online).' } else { 'Unified audit log ingestion is DISABLED. Enable in Microsoft Purview > Audit.' } $AuditSource = 'Exchange Online (Get-AdminAuditLogConfig)' } catch { Write-AbrDebugLog "Exchange Online audit check failed: $($_.Exception.Message)" 'WARN' 'COMPLIANCE' } } # Recycle Bin status - get from PnP, default to $true (M365 default) $RecycleBinEnabled = $true if ($script:PnPAvailable) { try { $SPTenantForRB = Get-PnPTenant -ErrorAction Stop # Guard against $null -- PnP may return $null for this field if ($null -ne $SPTenantForRB.RecycleBinEnabled) { $RecycleBinEnabled = [bool]$SPTenantForRB.RecycleBinEnabled } } catch { Write-AbrDebugLog "Could not retrieve Recycle Bin status: $($_.Exception.Message)" 'WARN' 'COMPLIANCE' } } # Sensitivity labels from pre-fetched cache $Labels = $script:CachedLabels #endregion Section -Style Heading2 'Compliance & Data Governance' { Paragraph "The following section documents the compliance and data governance configuration for SharePoint Online and OneDrive for Business in tenant $TenantId." BlankLine #region Audit Logging try { Section -Style Heading3 'Audit Logging' { $AuditObj = [System.Collections.ArrayList]::new() $auditInObj = [ordered] @{ 'Unified Audit Log Status' = if ($AuditEnabled) { 'Enabled' } else { 'Disabled' } 'Verification Source' = $AuditSource 'Note' = $AuditNote 'Verify in Purview' = 'https://compliance.microsoft.com/auditlogsearch' } $AuditObj.Add([pscustomobject]$auditInObj) | Out-Null $null = (& { if ($HealthCheck.SharePoint.Compliance) { $null = ($AuditObj | Where-Object { $_.'Unified Audit Log Status' -eq 'Disabled' } | Set-Style -Style Critical | Out-Null) $null = ($AuditObj | Where-Object { $_.'Unified Audit Log Status' -eq 'Enabled' } | Set-Style -Style OK | Out-Null) } }) $AuditTableParams = @{ Name = "Audit Logging - $TenantId"; List = $true; ColumnWidths = 30, 70 } if ($Report.ShowTableCaptions) { $AuditTableParams['Caption'] = "- $($AuditTableParams.Name)" } $AuditObj | Table @AuditTableParams $script:ExcelSheets['Audit Settings'] = $AuditObj } } catch { Write-AbrSectionError -Section 'Audit Logging' -Message "$($_.Exception.Message)" } #endregion #region Recycle Bin try { if ($script:PnPAvailable) { Section -Style Heading3 'Recycle Bin' { $RBObj = [System.Collections.ArrayList]::new() $rbInObj = [ordered] @{ 'Recycle Bin Enabled' = $RecycleBinEnabled 'First Stage Retention' = '30 days (Microsoft default)' 'Second Stage Retention' = '63 days additional (93 days total)' 'Admin Deleted Items Retention' = '14 days (site collection recycle bin)' } $RBObj.Add([pscustomobject](ConvertTo-HashToYN $rbInObj)) | Out-Null $null = (& { if ($HealthCheck.SharePoint.Compliance) { $null = ($RBObj | Where-Object { $_.'Recycle Bin Enabled' -eq 'No' } | Set-Style -Style Critical | Out-Null) } }) $RBTableParams = @{ Name = "Recycle Bin Configuration - $TenantId"; List = $true; ColumnWidths = 45, 55 } if ($Report.ShowTableCaptions) { $RBTableParams['Caption'] = "- $($RBTableParams.Name)" } $RBObj | Table @RBTableParams } } } catch { Write-AbrSectionError -Section 'Recycle Bin' -Message "$($_.Exception.Message)" } #endregion #region DLP Policies (guidance only) try { Section -Style Heading3 'Data Loss Prevention (DLP)' { Paragraph "DLP policy enumeration requires Exchange Online PowerShell (Get-DlpCompliancePolicy) or the Microsoft Purview Compliance portal. These are outside the SharePoint/Graph scopes used by this report." BlankLine $DLPNoteObj = [System.Collections.ArrayList]::new() $dlpInObj = [ordered] @{ 'Review Location' = 'Microsoft Purview > Data Loss Prevention > Policies' 'Purview DLP URL' = 'https://compliance.microsoft.com/datalossprevention/policies' 'Recommended Actions' = 'Verify at least one DLP policy targets SharePoint and OneDrive with sensitive information types (PII, financial, health records)' 'PowerShell Check' = "Connect-IPPSSession; Get-DlpCompliancePolicy | Where-Object { `$_.Workload -like '*SharePoint*' }" } $DLPNoteObj.Add([pscustomobject]$dlpInObj) | Out-Null $DLPTableParams = @{ Name = "DLP Policy Guidance - $TenantId"; List = $true; ColumnWidths = 25, 75 } if ($Report.ShowTableCaptions) { $DLPTableParams['Caption'] = "- $($DLPTableParams.Name)" } $DLPNoteObj | Table @DLPTableParams } } catch { Write-AbrSectionError -Section 'DLP Policies' -Message "$($_.Exception.Message)" } #endregion #region Sensitivity Labels try { Section -Style Heading3 'Sensitivity Labels' { if ($Labels -and @($Labels).Count -gt 0) { $LabelObj = [System.Collections.ArrayList]::new() foreach ($Label in ($Labels | Sort-Object name)) { $labelInObj = [ordered] @{ 'Label Name' = $Label.name 'Sensitivity' = if ($Label.sensitivity) { $Label.sensitivity } else { '--' } 'Content Marking' = if ($Label.contentMarkings -and @($Label.contentMarkings).Count -gt 0) { 'Yes' } else { 'No' } 'Encryption Enabled' = if ($Label.encryptionConfiguration) { 'Yes' } else { 'No' } 'Color' = if ($Label.color) { $Label.color } else { '--' } } $LabelObj.Add([pscustomobject]$labelInObj) | Out-Null } $LabelTableParams = @{ Name = "Sensitivity Labels - $TenantId"; ColumnWidths = 35, 15, 15, 15, 20 } if ($Report.ShowTableCaptions) { $LabelTableParams['Caption'] = "- $($LabelTableParams.Name)" } $LabelObj | Table @LabelTableParams $script:ExcelSheets['Sensitivity Labels'] = $LabelObj } else { Paragraph "Sensitivity labels could not be retrieved (scope InformationProtectionPolicy.Read.All not requested), or no labels are configured." BlankLine $LabelGuideObj = [System.Collections.ArrayList]::new() $lgInObj = [ordered] @{ 'Configure Labels' = 'Microsoft Purview > Information Protection > Labels' 'Purview URL' = 'https://compliance.microsoft.com/informationprotection' 'Enable for SPO/OD' = "Run: Set-SPOTenant -EnableAIPIntegration `$true (SharePoint Admin PS)" 'Recommended Actions' = 'Publish labels to users, configure auto-labeling for SharePoint libraries' } $LabelGuideObj.Add([pscustomobject]$lgInObj) | Out-Null $LGTableParams = @{ Name = "Sensitivity Label Guidance - $TenantId"; List = $true; ColumnWidths = 25, 75 } if ($Report.ShowTableCaptions) { $LGTableParams['Caption'] = "- $($LGTableParams.Name)" } $LabelGuideObj | Table @LGTableParams } } } catch { Write-AbrSectionError -Section 'Sensitivity Labels' -Message "$($_.Exception.Message)" } #endregion #region ACSC E8 Assessment if ($script:IncludeACSCe8) { BlankLine Paragraph "ACSC Essential Eight Assessment -- Compliance & Data Governance" $E8Defs = Get-AbrSPE8Checks -Section 'Compliance' $E8Vars = @{ AuditEnabled = $AuditEnabled AuditEnabledStr = if ($AuditEnabled) { 'Yes' } else { 'No' } RecycleBinEnabled = $RecycleBinEnabled RecycleBinEnabledStr = if ($RecycleBinEnabled) { 'Yes' } else { 'No' } } $E8Checks = Build-AbrSPComplianceChecks -Definitions $E8Defs -Framework 'E8' -CallerVariables $E8Vars New-AbrSPE8AssessmentTable -Checks $E8Checks -Name 'Compliance' -TenantId $TenantId foreach ($row in $E8Checks) { $null = $script:E8AllChecks.Add([pscustomobject](@{ Section = 'Compliance' } + ($row | ConvertTo-HashTableSP))) } } #endregion #region CIS Baseline Assessment if ($script:IncludeCISBaseline) { BlankLine Paragraph "CIS Microsoft 365 Foundations Benchmark Assessment -- Compliance & Data Governance" $CISDefs = Get-AbrSPCISChecks -Section 'Compliance' $CISVars = @{ AuditEnabled = $AuditEnabled AuditEnabledStr = if ($AuditEnabled) { 'Yes' } else { 'No' } RecycleBinEnabled = $RecycleBinEnabled RecycleBinEnabledStr = if ($RecycleBinEnabled) { 'Yes' } else { 'No' } } $CISChecks = Build-AbrSPComplianceChecks -Definitions $CISDefs -Framework 'CIS' -CallerVariables $CISVars New-AbrSPCISAssessmentTable -Checks $CISChecks -Name 'Compliance' -TenantId $TenantId foreach ($row in $CISChecks) { $null = $script:CISAllChecks.Add([pscustomobject](@{ Section = 'Compliance' } + ($row | ConvertTo-HashTableSP))) } } #endregion } } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Compliance' } } |