Src/Private/Get-AbrPurviewSensitivityLabel.ps1
|
function Get-AbrPurviewSensitivityLabel { <# .SYNOPSIS Used by As Built Report to retrieve Microsoft Purview Sensitivity Label information. .DESCRIPTION Collects and reports on Sensitivity Labels, Sensitivity Label Policies, and Auto-Labeling Policies configured in Microsoft Purview, including encryption settings, content marking, scopes, and auto-labeling rules. .NOTES Version: 0.1.0 Author: Pai Wei Sing .EXAMPLE Get-AbrPurviewSensitivityLabel -TenantId 'contoso.onmicrosoft.com' #> [CmdletBinding()] param ( [Parameter(Position = 0, Mandatory)] [string]$TenantId ) begin { Write-PScriboMessage -Message "Collecting Microsoft Purview Sensitivity Label information for tenant $TenantId." | Out-Null Show-AbrDebugExecutionTime -Start -TitleMessage 'Sensitivity Labels' } process { # Fetch Auto-Labeling Policies once here; reused in both the Coverage Summary # and the dedicated Auto-Labeling Policies section below. $AutoLabelPolicies = try { Get-AutoSensitivityLabelPolicy -ErrorAction Stop } catch { @() } # GAP 4 (MCCA check-IP103): AIP unified labeling migration status $AIPUnifiedLabelingEnabled = $null try { $AIPConfig = Get-AIPServiceConfiguration -ErrorAction SilentlyContinue if ($AIPConfig) { $AIPUnifiedLabelingEnabled = $AIPConfig.UnifiedLabelingEnabled } } catch { } # cmdlet only available when AIPService module present; non-fatal #region Sensitivity Labels try { $Labels = Get-Label -ErrorAction Stop if ($Labels) { Section -Style Heading2 'Sensitivity Labels' { # Summary Table $OutObj = [System.Collections.ArrayList]::new() foreach ($Label in $Labels) { try { $_pre_Enabled_44 = if ($Label.Disabled -eq $false) { 'Yes' } else { 'No' } $_pre_Encryption_46 = if ($Label.EncryptionEnabled) { 'Yes' } else { 'No' } $_pre_ContentMarking_47 = if ($Label.ContentMarkingEnabled) { 'Yes' } else { 'No' } $_pre_AutoLabeling_48 = if ($Label.AutoLabelingEnabled) { 'Yes' } else { 'No' } $inObj = [ordered] @{ 'Name' = $Label.DisplayName 'Priority' = $Label.Priority 'Enabled' = $_pre_Enabled_44 'Scope' = ($Label.ContentType -join ', ') 'Encryption' = $_pre_Encryption_46 'Content Marking' = $_pre_ContentMarking_47 'Auto Labeling' = $_pre_AutoLabeling_48 } $OutObj.Add([pscustomobject]$inObj) | Out-Null } catch { Write-PScriboMessage -IsWarning -Message "Sensitivity Label '$($Label.DisplayName)': $($_.Exception.Message)" | Out-Null } } if ($Healthcheck -and $script:HealthCheck.Purview.InformationProtection) { $OutObj | Where-Object { $_.'Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null } $TableParams = @{ Name = "Sensitivity Labels - $TenantId"; List = $false; ColumnWidths = 24, 10, 10, 18, 13, 13, 12 } if ($script:Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $OutObj | Sort-Object -Property 'Priority' | Table @TableParams #region Coverage Summary $HasEncryption = $Labels | Where-Object { $_.EncryptionEnabled } $HasAutoLabeling = $Labels | Where-Object { $_.AutoLabelingEnabled } $HasContentMarking = $Labels | Where-Object { $_.ContentMarkingEnabled } # $AutoLabelPolicies already fetched at top of process block $CovObj = [System.Collections.ArrayList]::new() $_pre_LabelsConfigured_76 = if ($Labels.Count -gt 0) { 'Yes' } else { 'No' } $_pre_LabelswithEncryption_77 = if ($null -ne $HasEncryption) { 'Yes' } else { 'No' } $_pre_LabelswithContentMar_78 = if ($null -ne $HasContentMarking) { 'Yes' } else { 'No' } $_pre_LabelswithAutoLabeli_79 = if ($null -ne $HasAutoLabeling) { 'Yes' } else { 'No' } $_pre_AutoLabelingPolicies_80 = if ($null -ne $AutoLabelPolicies -and @($AutoLabelPolicies).Count -gt 0) { 'Yes' } else { 'No' } $_pre_AIPUnifiedLabeling_81 = if ($null -eq $AIPUnifiedLabelingEnabled) { 'Unknown' } elseif ($AIPUnifiedLabelingEnabled) { 'Yes' } else { 'No — migration required' } $covInObj = [ordered] @{ 'Labels Configured' = $_pre_LabelsConfigured_76 'Labels with Encryption' = $_pre_LabelswithEncryption_77 'Labels with Content Marking' = $_pre_LabelswithContentMar_78 'Labels with Auto-Labeling (per-label)' = $_pre_LabelswithAutoLabeli_79 'Auto-Labeling Policies Configured' = $_pre_AutoLabelingPolicies_80 'AIP Unified Labeling Enabled' = $_pre_AIPUnifiedLabeling_81 } $CovObj.Add([pscustomobject]$covInObj) | Out-Null if ($Healthcheck -and $script:HealthCheck.Purview.InformationProtection) { $CovObj | Where-Object { $_.'Labels Configured' -eq 'No' } | Set-Style -Style Critical | Out-Null $CovObj | Where-Object { $_.'Labels with Encryption' -eq 'No' } | Set-Style -Style Warning | Out-Null $CovObj | Where-Object { $_.'Auto-Labeling Policies Configured' -eq 'No' } | Set-Style -Style Warning | Out-Null $CovObj | Where-Object { $_.'AIP Unified Labeling Enabled' -match 'migration' } | Set-Style -Style Warning | Out-Null } $CovTableParams = @{ Name = "Information Protection Coverage - $TenantId"; List = $true; ColumnWidths = 55, 45 } if ($script:Report.ShowTableCaptions) { $CovTableParams['Caption'] = "- $($CovTableParams.Name)" } $CovObj | Table @CovTableParams #region ACSC Inline Check — Sensitivity Labels if ($script:InfoLevel.InformationProtection -ge 3) { $HasEncryptionLabel = [bool]($Labels | Where-Object { $_.EncryptionEnabled }) $HasLabelsConfigured = ($Labels.Count -gt 0) Write-AbrPurviewACSCCheck -TenantId $TenantId -SectionName 'Sensitivity Labels' -Checks @( [pscustomobject]@{ ControlId = 'ISM-0271' E8 = 'N/A' Description = 'Information is classified before being stored or transmitted' Check = 'Sensitivity labels are configured and published to users' Status = if ($HasLabelsConfigured) { 'Pass' } else { 'Fail' } } [pscustomobject]@{ ControlId = 'ISM-0884' E8 = 'N/A' Description = 'Encryption applied to sensitive/protected information' Check = 'At least one sensitivity label has encryption enabled' Status = if ($HasEncryptionLabel) { 'Pass' } else { 'Fail' } } ) } #endregion # Per-Label Detail Sections if ($script:InfoLevel.InformationProtection -ge 2) { foreach ($Label in ($Labels | Sort-Object Priority)) { try { Section -Style Heading3 $Label.DisplayName { Paragraph "The $($Label.DisplayName) sensitivity label is configured as follows." BlankLine #region Label Details $GenObj = [System.Collections.ArrayList]::new() $_pre_ParentLabel_134 = if ($Label.ParentId) { $Label.ParentId } else { 'N/A' } $_pre_DescriptionforUsers_138 = if ($Label.Tooltip) { $Label.Tooltip } else { '--' } $_pre_DescriptionforAdmins_139 = if ($Label.Comment) { $Label.Comment } else { '--' } $_pre_LabelColour_140 = if ($Label.LabelColor) { $Label.LabelColor } else { 'None' } $genInObj = [ordered] @{ 'Parent Label' = $_pre_ParentLabel_134 'Name' = $Label.Name 'Display Name' = $Label.DisplayName 'Label Priority' = $Label.Priority 'Description for Users' = $_pre_DescriptionforUsers_138 'Description for Admins' = $_pre_DescriptionforAdmins_139 'Label Colour' = $_pre_LabelColour_140 } $GenObj.Add([pscustomobject]$genInObj) | Out-Null $GenTableParams = @{ Name = "Label Details - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $GenTableParams['Caption'] = "- $($GenTableParams.Name)" } $GenObj | Table @GenTableParams #endregion #region Scope $ScopeObj = [System.Collections.ArrayList]::new() $ContentTypes = $Label.ContentType $_pre_Filesotherdataassets_156 = if ($ContentTypes -contains 'File') { 'Checked' } else { 'Not checked' } $_pre_Emails_157 = if ($ContentTypes -contains 'Email') { 'Checked' } else { 'Not checked' } $_pre_Meetings_158 = if ($ContentTypes -contains 'Meeting') { 'Checked' } else { 'Not checked' } $_pre_Groupssites_159 = if ($ContentTypes -contains 'Site') { 'Checked' } else { 'Not checked' } $_pre_Schematizeddataasset_160 = if ($ContentTypes -contains 'SchematizedData') { 'Checked' } else { 'Not checked' } $scopeInObj = [ordered] @{ 'Files & other data assets' = $_pre_Filesotherdataassets_156 'Emails' = $_pre_Emails_157 'Meetings' = $_pre_Meetings_158 'Groups & sites' = $_pre_Groupssites_159 'Schematized data assets' = $_pre_Schematizeddataasset_160 } $ScopeObj.Add([pscustomobject]$scopeInObj) | Out-Null $ScopeTableParams = @{ Name = "Scope - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $ScopeTableParams['Caption'] = "- $($ScopeTableParams.Name)" } $ScopeObj | Table @ScopeTableParams #endregion #region Protection Settings $ProtObj = [System.Collections.ArrayList]::new() $_pre_ControlaccessEncrypt_176 = if ($Label.EncryptionEnabled) { 'Checked' } else { 'Not checked' } $_pre_Applycontentmarking_177 = if ($Label.ContentMarkingEnabled) { 'Checked' } else { 'Not checked' } $protInObj = [ordered] @{ 'Control access (Encryption)' = $_pre_ControlaccessEncrypt_176 'Apply content marking' = $_pre_Applycontentmarking_177 'Protect Teams meetings and chats' = 'N/A (Teams Premium licensing required)' } $ProtObj.Add([pscustomobject]$protInObj) | Out-Null $ProtTableParams = @{ Name = "Protection Settings - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $ProtTableParams['Caption'] = "- $($ProtTableParams.Name)" } $ProtObj | Table @ProtTableParams #endregion #region Encryption Details if ($Label.EncryptionEnabled) { $EncObj = [System.Collections.ArrayList]::new() # Parse rights definitions into readable format $RightsDisplay = if ($Label.EncryptionRightsDefinitions) { ($Label.EncryptionRightsDefinitions | ForEach-Object { "$($_.Identity): $($_.Rights -join ', ')" }) -join '; ' } else { 'N/A' } $_pre_Assignpermissionsnow_199 = if ($Label.EncryptionAdhocPermissions) { 'Let users assign permissions' } else { 'Assign permissions now' } $_pre_DoNotForward_201 = if ($Label.EncryptionDoNotForward) { 'Checked' } else { 'Not checked' } $_pre_EncryptOnly_202 = if ($Label.EncryptionEncryptOnly) { 'Checked' } else { 'Not checked' } $_pre_ContentExpiry_203 = if ($Label.EncryptionContentExpiredOnDateInDaysOrNever -and $Label.EncryptionContentExpiredOnDateInDaysOrNever -ne 'Never') { "$($Label.EncryptionContentExpiredOnDateInDaysOrNever) days" } else { 'Never' } $_pre_OfflineAccessDuratio_204 = if ($Label.EncryptionOfflineAccessDays -ge 0) { "$($Label.EncryptionOfflineAccessDays) days" } else { 'Always' } $_pre_DoubleKeyEncryptionU_205 = if ($Label.EncryptionDoubleKeyEncryptionUrl) { $Label.EncryptionDoubleKeyEncryptionUrl } else { 'N/A' } $encInObj = [ordered] @{ 'Encryption' = 'Enabled' 'Assign permissions now or let users decide' = $_pre_Assignpermissionsnow_199 'Rights Definitions' = $RightsDisplay 'Do Not Forward' = $_pre_DoNotForward_201 'Encrypt Only' = $_pre_EncryptOnly_202 'Content Expiry' = $_pre_ContentExpiry_203 'Offline Access Duration' = $_pre_OfflineAccessDuratio_204 'Double Key Encryption URL' = $_pre_DoubleKeyEncryptionU_205 } $EncObj.Add([pscustomobject]$encInObj) | Out-Null $EncTableParams = @{ Name = "Encryption - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $EncTableParams['Caption'] = "- $($EncTableParams.Name)" } $EncObj | Table @EncTableParams } #endregion #region Content Marking $cmHeaderAlign = if ($Label.ContentMarkingHeaderAlignment) { $script:TextInfo.ToTitleCase($Label.ContentMarkingHeaderAlignment) } else { 'N/A' } $cmFooterAlign = if ($Label.ContentMarkingFooterAlignment) { $script:TextInfo.ToTitleCase($Label.ContentMarkingFooterAlignment) } else { 'N/A' } $cmWaterLayout = if ($Label.ContentMarkingWaterMarkLayout) { $script:TextInfo.ToTitleCase($Label.ContentMarkingWaterMarkLayout) } else { 'N/A' } $CmObj = [System.Collections.ArrayList]::new() $_pre_Contentmarking_226 = if ($Label.ContentMarkingEnabled) { 'Enabled' } else { 'Disabled' } $_pre_Addawatermark_227 = if ($Label.ContentMarkingWaterMarkEnabled) { 'Checked' } else { 'Not checked' } $_pre_Addaheader_228 = if ($Label.ContentMarkingHeaderEnabled) { 'Checked' } else { 'Not checked' } $_pre_Headertext_229 = if ($Label.ContentMarkingHeaderText) { $Label.ContentMarkingHeaderText } else { 'N/A' } $_pre_Fontsize_230 = if ($Label.ContentMarkingHeaderFontSize) { $Label.ContentMarkingHeaderFontSize } else { 'N/A' } $_pre_Fontcolour_231 = if ($Label.ContentMarkingHeaderFontColor) { $Label.ContentMarkingHeaderFontColor } else { 'N/A' } $_pre_Addafooter_233 = if ($Label.ContentMarkingFooterEnabled) { 'Checked' } else { 'Not checked' } $_pre_Footertext_234 = if ($Label.ContentMarkingFooterText) { $Label.ContentMarkingFooterText } else { 'N/A' } $_pre_Fontsize_235 = if ($Label.ContentMarkingFooterFontSize) { $Label.ContentMarkingFooterFontSize } else { 'N/A' } $_pre_Fontcolour_236 = if ($Label.ContentMarkingFooterFontColor) { $Label.ContentMarkingFooterFontColor } else { 'N/A' } $cmInObj = [ordered] @{ 'Content marking' = $_pre_Contentmarking_226 'Add a watermark' = $_pre_Addawatermark_227 'Add a header' = $_pre_Addaheader_228 '- Header text' = $_pre_Headertext_229 '- Font size' = $_pre_Fontsize_230 '- Font colour' = $_pre_Fontcolour_231 '- Align text' = $cmHeaderAlign 'Add a footer' = $_pre_Addafooter_233 '- Footer text' = $_pre_Footertext_234 '- Font size ' = $_pre_Fontsize_235 '- Font colour ' = $_pre_Fontcolour_236 '- Align text ' = $cmFooterAlign } if ($Label.ContentMarkingWaterMarkEnabled) { $cmInObj['- Watermark text'] = if ($Label.ContentMarkingWaterMarkText) { $Label.ContentMarkingWaterMarkText } else { 'N/A' } $cmInObj['- Watermark font size'] = if ($Label.ContentMarkingWaterMarkFontSize) { $Label.ContentMarkingWaterMarkFontSize } else { 'N/A' } $cmInObj['- Watermark layout'] = $cmWaterLayout } $CmObj.Add([pscustomobject]$cmInObj) | Out-Null $CmTableParams = @{ Name = "Content Marking - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $CmTableParams['Caption'] = "- $($CmTableParams.Name)" } $CmObj | Table @CmTableParams #endregion #region Auto-labeling for files and emails $AlObj = [System.Collections.ArrayList]::new() $_pre_Autolabellingforfile_263 = if ($Label.AutoLabelingEnabled) { 'Enabled' } else { 'Disabled' } $_pre_Condition_264 = if ($Label.Conditions) { 'Configured' } else { 'N/A' } $_pre_Whencontentmatchesth_265 = if ($Label.AutoLabelingMessage) { $Label.AutoLabelingMessage } else { 'N/A' } $_pre_Displaythismessageto_266 = if ($Label.AutoLabelingPolicyTip) { $Label.AutoLabelingPolicyTip } else { 'N/A' } $alInObj = [ordered] @{ 'Auto-labelling for files and emails' = $_pre_Autolabellingforfile_263 'Condition' = $_pre_Condition_264 'When content matches these conditions' = $_pre_Whencontentmatchesth_265 'Display this message to users when label is applied' = $_pre_Displaythismessageto_266 } $AlObj.Add([pscustomobject]$alInObj) | Out-Null $AlTableParams = @{ Name = "Auto-labelling for Files and Emails - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $AlTableParams['Caption'] = "- $($AlTableParams.Name)" } $AlObj | Table @AlTableParams #endregion #region Groups & Sites $GsObj = [System.Collections.ArrayList]::new() $_pre_Privacyandexternalus_281 = if ($Label.SiteAndGroupProtectionEnabled -and $Label.SiteAndGroupPrivacy) { 'Checked' } else { 'Not checked' } $_pre_ExternalsharingandCo_282 = if ($Label.SiteAndGroupProtectionEnabled -and $Label.SiteAndGroupExternalSharingControlType) { 'Checked' } else { 'Not checked' } $_pre_Privateteamsdiscover_283 = if ($Label.SiteAndGroupProtectionEnabled -and $Label.SiteAndGroupTeamsAllowPrivateChannels) { 'Checked' } else { 'Not checked' } $gsInObj = [ordered] @{ 'Privacy and external user access' = $_pre_Privacyandexternalus_281 'External sharing and Conditional Access' = $_pre_ExternalsharingandCo_282 'Private teams discoverability and shared channel settings' = $_pre_Privateteamsdiscover_283 'Apply a label to channel meetings' = 'Not checked' } $GsObj.Add([pscustomobject]$gsInObj) | Out-Null $GsTableParams = @{ Name = "Groups & Sites - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $GsTableParams['Caption'] = "- $($GsTableParams.Name)" } $GsObj | Table @GsTableParams # Privacy & External User Access detail $sgPrivacy = if ($Label.SiteAndGroupPrivacy) { $script:TextInfo.ToTitleCase($Label.SiteAndGroupPrivacy) } else { 'None' } $sgSharing = if ($Label.SiteAndGroupExternalSharingControlType) { $script:TextInfo.ToTitleCase($Label.SiteAndGroupExternalSharingControlType) } else { 'N/A' } $sgCondAccess = if ($Label.SiteAndGroupAccessToSitesFromUnmanagedDevices) { $script:TextInfo.ToTitleCase($Label.SiteAndGroupAccessToSitesFromUnmanagedDevices) } else { 'N/A' } $PrivObj = [System.Collections.ArrayList]::new() $_pre_LetMicrosoft365Group_301 = if ($Label.SiteAndGroupAllowGuestsToBeGroupOwner) { 'Checked' } else { 'N/A' } $privInObj = [ordered] @{ 'Privacy' = $sgPrivacy 'Let Microsoft 365 Group owners add people outside your organisation as guests' = $_pre_LetMicrosoft365Group_301 } $PrivObj.Add([pscustomobject]$privInObj) | Out-Null $PrivTableParams = @{ Name = "Privacy and External User Access - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $PrivTableParams['Caption'] = "- $($PrivTableParams.Name)" } $PrivObj | Table @PrivTableParams # External Sharing & Conditional Access detail $ExtObj = [System.Collections.ArrayList]::new() $_pre_Controlexternalshari_312 = if ($Label.SiteAndGroupExternalSharingControlType) { 'Checked' } else { 'N/A' } $_pre_UseMicrosoftEntraCon_314 = if ($Label.SiteAndGroupAccessToSitesFromUnmanagedDevices) { 'Checked' } else { 'N/A' } $extInObj = [ordered] @{ 'Control external sharing from labelled SharePoint sites' = $_pre_Controlexternalshari_312 '- Content can be shared with' = $sgSharing 'Use Microsoft Entra Conditional Access to protect labelled SharePoint site' = $_pre_UseMicrosoftEntraCon_314 '- Choose an existing authentication context' = $sgCondAccess } $ExtObj.Add([pscustomobject]$extInObj) | Out-Null $ExtTableParams = @{ Name = "External Sharing and Conditional Access - $($Label.DisplayName)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $ExtTableParams['Caption'] = "- $($ExtTableParams.Name)" } $ExtObj | Table @ExtTableParams #endregion } } catch { Write-PScriboMessage -IsWarning -Message "Label Detail '$($Label.DisplayName)': $($_.Exception.Message)" | Out-Null } } } } } else { Write-PScriboMessage -Message "No Sensitivity Label information found for $TenantId. Disabling section." | Out-Null } } catch { Write-PScriboMessage -IsWarning -Message "Sensitivity Label Section: $($_.Exception.Message)" | Out-Null } #endregion #region Sensitivity Label Policies try { $LabelPolicies = Get-LabelPolicy -ErrorAction Stop if ($LabelPolicies) { Section -Style Heading2 'Sensitivity Label Policies' { # Summary Table $OutObj = [System.Collections.ArrayList]::new() foreach ($Policy in $LabelPolicies) { try { $_pre_Enabled_353 = if ($Policy.Enabled) { 'Yes' } else { 'No' } $_pre_ExchangeLocation_355 = if ($Policy.ExchangeLocation.Name) { ($Policy.ExchangeLocation.Name -join ', ') } else { 'All' } $_pre_ExcludedUsers_356 = if ($Policy.ExchangeLocationException.Name) { ($Policy.ExchangeLocationException.Name -join ', ') } else { '--' } $inObj = [ordered] @{ 'Name' = $Policy.Name 'Enabled' = $_pre_Enabled_353 'Labels' = ($Policy.Labels -join ', ') 'Exchange Location' = $_pre_ExchangeLocation_355 'Excluded Users' = $_pre_ExcludedUsers_356 'Created' = $Policy.WhenCreated.ToString('yyyy-MM-dd') } $OutObj.Add([pscustomobject]$inObj) | Out-Null } catch { Write-PScriboMessage -IsWarning -Message "Label Policy '$($Policy.Name)': $($_.Exception.Message)" | Out-Null } } if ($Healthcheck -and $script:HealthCheck.Purview.InformationProtection) { $OutObj | Where-Object { $_.'Enabled' -eq 'No' } | Set-Style -Style Critical | Out-Null } $TableParams = @{ Name = "Sensitivity Label Policies - $TenantId"; List = $false; ColumnWidths = 22, 10, 22, 18, 16, 12 } if ($script:Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $OutObj | Sort-Object -Property 'Name' | Table @TableParams #region ACSC Inline Check — Sensitivity Label Policies if ($script:InfoLevel.InformationProtection -ge 3) { $HasMandatory = [bool]($LabelPolicies | Where-Object { $_.RequireSensitivityLabelOnSave }) $HasDowngrade = [bool]($LabelPolicies | Where-Object { $_.RequireDowngradeJustification }) Write-AbrPurviewACSCCheck -TenantId $TenantId -SectionName 'Sensitivity Label Policies' -Checks @( [pscustomobject]@{ ControlId = 'ISM-0272' E8 = 'PSPF Req 59' Description = 'Labels applied to all information (mandatory labelling)' Check = 'RequireSensitivityLabelOnSave enabled in at least one label policy' Status = if ($HasMandatory) { 'Pass' } else { 'Fail' } } [pscustomobject]@{ ControlId = 'PSPF-060' E8 = 'PSPF Req 60' Description = 'Label downgrade justification required' Check = 'RequireDowngradeJustification enabled in at least one label policy' Status = if ($HasDowngrade) { 'Pass' } else { 'Fail' } } ) } #endregion # Per-Policy Detail Sections if ($script:InfoLevel.InformationProtection -ge 2) { foreach ($Policy in ($LabelPolicies | Sort-Object Name)) { try { Section -Style Heading3 $Policy.Name { Paragraph "The $($Policy.Name) label policy is configured as follows." BlankLine $PolObj = [System.Collections.ArrayList]::new() $_pre_Enabled_410 = if ($Policy.Enabled) { 'Yes' } else { 'No' } $_pre_LabelsIncluded_411 = if ($Policy.Labels) { ($Policy.Labels -join ', ') } else { '--' } $_pre_PublishedtoExchange_412 = if ($Policy.ExchangeLocation.Name) { ($Policy.ExchangeLocation.Name -join ', ') } else { 'All Users' } $_pre_ExcludedfromExchange_413 = if ($Policy.ExchangeLocationException.Name) { ($Policy.ExchangeLocationException.Name -join ', ') } else { 'N/A' } $_pre_ModernGroupLocation_414 = if ($Policy.ModernGroupLocation.Name) { ($Policy.ModernGroupLocation.Name -join ', ') } else { 'N/A' } $_pre_MandatoryLabeling_415 = if ($Policy.RequireSensitivityLabelOnSave) { 'Enabled' } else { 'Disabled' } $_pre_RequireJustification_416 = if ($Policy.RequireDowngradeJustification) { 'Enabled' } else { 'Disabled' } $_pre_ApplytoUnlabeledDocu_417 = if ($Policy.ApplyAutoLabelPolicy) { 'Enabled' } else { 'Disabled' } $_pre_MoreInfoURL_418 = if ($Policy.MoreInfoUrl) { $Policy.MoreInfoUrl } else { 'N/A' } $_pre_CreatedBy_421 = if ($Policy.CreatedBy) { $Policy.CreatedBy } else { 'N/A' } $_pre_ModifiedBy_422 = if ($Policy.LastModifiedBy) { $Policy.LastModifiedBy } else { 'N/A' } $polInObj = [ordered] @{ 'Policy Name' = $Policy.Name 'Enabled' = $_pre_Enabled_410 'Labels Included' = $_pre_LabelsIncluded_411 'Published to (Exchange)' = $_pre_PublishedtoExchange_412 'Excluded from Exchange' = $_pre_ExcludedfromExchange_413 'Modern Group Location' = $_pre_ModernGroupLocation_414 'Mandatory Labeling' = $_pre_MandatoryLabeling_415 'Require Justification to Downgrade' = $_pre_RequireJustification_416 'Apply to Unlabeled Documents' = $_pre_ApplytoUnlabeledDocu_417 'More Info URL' = $_pre_MoreInfoURL_418 'Created' = $Policy.WhenCreated.ToString('yyyy-MM-dd HH:mm') 'Last Modified' = $Policy.WhenChangedUTC.ToString('yyyy-MM-dd HH:mm') 'Created By' = $_pre_CreatedBy_421 'Modified By' = $_pre_ModifiedBy_422 } $PolObj.Add([pscustomobject]$polInObj) | Out-Null $PolTableParams = @{ Name = "Policy Detail - $($Policy.Name)"; List = $true; ColumnWidths = 40, 60 } if ($script:Report.ShowTableCaptions) { $PolTableParams['Caption'] = "- $($PolTableParams.Name)" } $PolObj | Table @PolTableParams } } catch { Write-PScriboMessage -IsWarning -Message "Label Policy Detail '$($Policy.Name)': $($_.Exception.Message)" | Out-Null } } } } } else { Write-PScriboMessage -Message "No Sensitivity Label Policy information found for $TenantId. Disabling section." | Out-Null } } catch { Write-PScriboMessage -IsWarning -Message "Sensitivity Label Policy Section: $($_.Exception.Message)" | Out-Null } #endregion #region Auto-Labeling Policies try { # $AutoLabelPolicies already fetched at top of process block — reuse here if ($AutoLabelPolicies) { Section -Style Heading2 'Auto-Labeling Policies' { $OutObj = [System.Collections.ArrayList]::new() foreach ($Policy in $AutoLabelPolicies) { try { $_pre_Enabled_465 = if ($Policy.Enabled) { 'Yes' } else { 'No' } $_pre_Labels_472 = if ($Policy.Labels) { ($Policy.Labels -join ', ') } else { '--' } $_pre_Exchange_473 = if ($Policy.ExchangeLocation) { 'Checked' } else { 'Not checked' } $_pre_SharePoint_474 = if ($Policy.SharePointLocation) { 'Checked' } else { 'Not checked' } $_pre_OneDrive_475 = if ($Policy.OneDriveLocation) { 'Checked' } else { 'Not checked' } $inObj = [ordered] @{ 'Name' = $Policy.Name 'Enabled' = $_pre_Enabled_465 'Mode' = switch ($Policy.Mode) { 'Enable' { 'On (Enforced)' } 'TestWithNotifications' { 'Simulation (notify)' } 'TestWithoutNotifications' { 'Simulation (silent)' } default { $script:TextInfo.ToTitleCase($Policy.Mode) } } 'Labels' = $_pre_Labels_472 'Exchange' = $_pre_Exchange_473 'SharePoint' = $_pre_SharePoint_474 'OneDrive' = $_pre_OneDrive_475 'Last Modified' = $Policy.WhenChangedUTC.ToString('yyyy-MM-dd') } $OutObj.Add([pscustomobject]$inObj) | Out-Null } catch { Write-PScriboMessage -IsWarning -Message "Auto-Label Policy '$($Policy.Name)': $($_.Exception.Message)" | Out-Null } } if ($Healthcheck -and $script:HealthCheck.Purview.InformationProtection) { $OutObj | Where-Object { $_.'Enabled' -eq 'No' } | Set-Style -Style Critical | Out-Null $OutObj | Where-Object { $_.'Mode' -notmatch 'Enforced' } | Set-Style -Style Warning | Out-Null } $TableParams = @{ Name = "Auto-Labeling Policies - $TenantId"; List = $false; ColumnWidths = 20, 8, 18, 16, 10, 10, 10, 8 } if ($script:Report.ShowTableCaptions) { $TableParams['Caption'] = "- $($TableParams.Name)" } $OutObj | Sort-Object -Property 'Name' | Table @TableParams #region ACSC Inline Check — Auto-Labeling Policies if ($script:InfoLevel.InformationProtection -ge 3) { $HasEnforcedALP = [bool]($AutoLabelPolicies | Where-Object { $_.Mode -eq 'Enable' }) Write-AbrPurviewACSCCheck -TenantId $TenantId -SectionName 'Auto-Labeling Policies' -Checks @( [pscustomobject]@{ ControlId = 'ISM-0271' E8 = 'N/A' Description = 'Classification applied automatically to unlabelled content' Check = 'At least one auto-labeling policy is in Enforce (Enable) mode' Status = if ($HasEnforcedALP) { 'Pass' } elseif ($AutoLabelPolicies.Count -gt 0) { 'Partial' } else { 'Fail' } } ) } #endregion } } else { Write-PScriboMessage -Message "No Auto-Labeling Policy information found for $TenantId. Disabling section." | Out-Null } } catch { Write-PScriboMessage -IsWarning -Message "Auto-Labeling Policy Section: $($_.Exception.Message)" | Out-Null } #endregion #region IRM for Exchange Online (MCCA check-IP102) try { $IRMConfig = Get-IRMConfiguration -ErrorAction SilentlyContinue if ($IRMConfig) { Section -Style Heading2 'IRM Configuration (Exchange Online)' { Paragraph "Information Rights Management (IRM) integrates sensitivity label encryption with Exchange Online. The following settings control how IRM protection is applied to email." BlankLine $_pre_InternalLicensing = if ($IRMConfig.InternalLicensingEnabled) { 'Yes' } else { 'No' } $_pre_ExternalLicensing = if ($IRMConfig.ExternalLicensingEnabled) { 'Yes' } else { 'No' } $_pre_AzureRMS = if ($IRMConfig.AzureRMSLicensingEnabled) { 'Yes' } else { 'No' } $_pre_OWAEnabled = if ($IRMConfig.OWAEnabled) { 'Yes' } else { 'No' } $_pre_SimplifiedClient = if ($IRMConfig.SimplifiedClientAccessEnabled) { 'Yes' } else { 'No' } $_pre_SearchEnabled = if ($IRMConfig.SearchEnabled) { 'Yes' } else { 'No' } $_pre_DecryptAttach = if ($IRMConfig.DecryptAttachmentForEncryptOnly) { 'Yes' } else { 'No' } $_pre_EDiscovery = if ($IRMConfig.EDiscoverySuperUserEnabled) { 'Yes' } else { 'No' } $_pre_JournalReport = if ($IRMConfig.JournalReportDecryptionEnabled) { 'Yes' } else { 'No' } $irmInObj = [ordered] @{ 'Internal Licensing Enabled' = $_pre_InternalLicensing 'External Licensing Enabled' = $_pre_ExternalLicensing 'Azure RMS Licensing Enabled' = $_pre_AzureRMS 'OWA IRM Enabled' = $_pre_OWAEnabled 'Simplified Client Access Enabled' = $_pre_SimplifiedClient 'Search of Encrypted Email Enabled' = $_pre_SearchEnabled 'Decrypt Attachments (Encrypt-Only)' = $_pre_DecryptAttach 'eDiscovery Super User Enabled' = $_pre_EDiscovery 'Journal Report Decryption Enabled' = $_pre_JournalReport } $IRMObj = [System.Collections.ArrayList]::new() $IRMObj.Add([pscustomobject]$irmInObj) | Out-Null if ($Healthcheck -and $script:HealthCheck.Purview.InformationProtection) { $IRMObj | Where-Object { $_.'Internal Licensing Enabled' -eq 'No' } | Set-Style -Style Critical | Out-Null $IRMObj | Where-Object { $_.'Azure RMS Licensing Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null $IRMObj | Where-Object { $_.'OWA IRM Enabled' -eq 'No' } | Set-Style -Style Warning | Out-Null } $IRMTableParams = @{ Name = "IRM Configuration - $TenantId"; List = $true; ColumnWidths = 45, 55 } if ($script:Report.ShowTableCaptions) { $IRMTableParams['Caption'] = "- $($IRMTableParams.Name)" } $IRMObj | Table @IRMTableParams if ($script:InfoLevel.InformationProtection -ge 3) { $_irmInternalEnabled = [bool]$IRMConfig.InternalLicensingEnabled $_irmAzureEnabled = [bool]$IRMConfig.AzureRMSLicensingEnabled Write-AbrPurviewACSCCheck -TenantId $TenantId -SectionName 'IRM Configuration' -Checks @( [pscustomobject]@{ ControlId = 'ISM-0884' E8 = 'N/A' Description = 'Encryption applied to sensitive information in email' Check = 'IRM internal licensing and Azure RMS enabled for Exchange Online' Status = if ($_irmInternalEnabled -and $_irmAzureEnabled) { 'Pass' } elseif ($_irmInternalEnabled) { 'Partial' } else { 'Fail' } } ) } } } else { Write-PScriboMessage -Message "No IRM Configuration found for $TenantId." | Out-Null } } catch { Write-PScriboMessage -IsWarning -Message "IRM Configuration Section: $($_.Exception.Message)" | Out-Null } #endregion } end { Show-AbrDebugExecutionTime -End -TitleMessage 'Sensitivity Labels' } } |