-
Notifications
You must be signed in to change notification settings - Fork 121
Data-35016: Mandatory labeling enabled for sensitivity labels #757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
sandeepjha000
wants to merge
12
commits into
main
Choose a base branch
from
feature-35016
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3e2331e
Test 35016: Initial commit
sandeepjha000 c8e9578
Refactored report tables
sandeepjha000 db30371
refactored remediation action
sandeepjha000 2a073cf
refactored output tables to display enabled label policies and update…
sandeepjha000 8ba2aff
Removing extra backticks
sandeepjha000 0891620
updated Q1 comment to be more descriptive
sandeepjha000 726b41c
adding defensive error handling to log when settings don't match this…
sandeepjha000 cb0f334
refactor mandatory labeling logic to parse PolicySettingsBlob XML for…
alexandair b7acf46
enhance XML parsing in Test-Assessment-35016 to handle null values an…
alexandair 5f79fb9
updated code to detrmine Scope of label polices
sandeepjha000 d1f0884
updated logic to determine policy scope
sandeepjha000 b65232c
simplifying scope check
sandeepjha000 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| When sensitivity labels are not mandatory, users can send unclassified emails, share unclassified files and documents, create unclassified sites and groups, and publish unclassified Power BI content without applying appropriate protection labels. This creates a significant security and compliance risk because threat actors can easily exfiltrate sensitive data without any classification metadata to indicate its sensitivity level or trigger automated protection policies. Mandatory labeling must be configured across all workloads (Outlook for emails, Teams for teamwork, SharePoint/Microsoft 365 Groups for sites and groups, and Power BI for analytics content) to ensure comprehensive coverage. If data loss prevention (DLP) policies rely on label detection to identify and block sensitive content, unclassified data bypasses these controls entirely. Additionally, users may accidentally share confidential information without realizing it lacks proper protection, and organizations lose audit trail visibility into what data is being handled and how. Without mandatory labeling across all platforms, compliance frameworks such as GDPR, HIPAA, or industry-specific regulations cannot be effectively enforced because sensitive data remains unidentified. Organizations should implement at least one sensitivity label policy with mandatory labeling enabled across Outlook, Teams/Teamwork, SharePoint/Sites and Groups, and Power BI to ensure all communications, documents, and analytics content are classified before sharing, enabling both automated protection mechanisms and complete audit visibility. | ||
|
|
||
| **Remediation action** | ||
| 1. Navigate to Sensitivity label policies in Microsoft Purview | ||
| - [Sensitivity label policies](https://purview.microsoft.com/informationprotection/labelpolicies) | ||
| 2. Create or update a policy to enable mandatory labeling for target workloads (Outlook, Teams, SharePoint, Power BI) | ||
| 3. Enable specific settings: | ||
| - "Require users to apply a label to their email" (Outlook) | ||
| - "Require users to apply a label for Teams, groups, and SharePoint content" (collaboration) | ||
| - Mandatory labeling for Power BI content | ||
| 4. Set policy scope (global or specific groups) | ||
| 5. Test with pilot users before organization-wide rollout | ||
|
|
||
| **Learn More:** [Require users to apply a label](https://learn.microsoft.com/en-us/purview/sensitivity-labels-office-apps#require-users-to-apply-a-label-to-their-email-and-documents) | ||
|
|
||
| <!--- Results ---> | ||
| %TestResult% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,255 @@ | ||
| <# | ||
| .SYNOPSIS | ||
| Mandatory Labeling Enabled for Sensitivity Labels | ||
| #> | ||
|
|
||
| function Test-Assessment-35016 { | ||
| [ZtTest( | ||
| Category = 'Information Protection', | ||
| ImplementationCost = 'Medium', | ||
| MinimumLicense = ('Microsoft 365 E3'), | ||
| Pillar = 'Data', | ||
| RiskLevel = 'High', | ||
| SfiPillar = 'Protect tenants and production systems', | ||
| TenantType = ('Workforce','External'), | ||
| TestId = 35016, | ||
| Title = 'Mandatory labeling enabled for sensitivity labels', | ||
| UserImpact = 'High' | ||
| )] | ||
| [CmdletBinding()] | ||
| param() | ||
|
|
||
| #region Data Collection | ||
| Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose | ||
|
|
||
| $activity = 'Checking mandatory labeling configuration' | ||
| Write-ZtProgress -Activity $activity -Status 'Getting sensitivity label policies' | ||
|
|
||
| $errorMsg = $null | ||
| $enabledPolicies = @() | ||
|
|
||
| try { | ||
| # Q1: Retrieve all enabled sensitivity label policies to assess mandatory labeling configuration | ||
| $enabledPolicies = Get-LabelPolicy -ErrorAction Stop | Where-Object { $_.Enabled -eq $true } | ||
| } | ||
| catch { | ||
| $errorMsg = $_ | ||
| Write-PSFMessage "Error querying label policies: $_" -Level Error | ||
| } | ||
| #endregion Data Collection | ||
|
|
||
| #region Assessment Logic | ||
| $allPolicySettings = @() | ||
| $mandatoryPolicies = @() | ||
| $xmlParseErrors = @() | ||
| $passed = $false | ||
| $customStatus = $null | ||
|
|
||
| if ($errorMsg) { | ||
| $testResultMarkdown = "⚠️ Unable to determine mandatory labeling status due to error: $errorMsg`n`n" | ||
| $customStatus = 'Investigate' | ||
| } | ||
| else { | ||
| Write-PSFMessage "Found $($enabledPolicies.Count) enabled label policies" -Level Verbose | ||
|
|
||
| try { | ||
| # Examine label policy settings for mandatory labeling | ||
| foreach ($policy in $enabledPolicies) { | ||
| # Determine policy scope: | ||
| # - Global if any location is set to "All" | ||
| # - Scoped if specific users/groups are defined | ||
| $allLocationNames = @( | ||
| $policy.ExchangeLocation.Name | ||
| $policy.ModernGroupLocation.Name | ||
| $policy.SharePointLocation.Name | ||
| $policy.OneDriveLocation.Name | ||
| $policy.SkypeLocation.Name | ||
| $policy.PublicFolderLocation.Name | ||
| ) | Where-Object { $_ } | ||
|
|
||
| $isGlobal = $allLocationNames -contains 'All' | ||
|
|
||
| $policySettings = @{ | ||
| PolicyName = $policy.Name | ||
| Guid = $policy.Guid | ||
| Enabled = $policy.Enabled | ||
| EmailMandatory = $false | ||
| TeamworkMandatory = $false | ||
| SiteGroupMandatory = $false | ||
| PowerBIMandatory = $false | ||
| EmailOverride = $false | ||
| Scope = if ($isGlobal) { 'Global' } else { 'Scoped' } | ||
| LabelsCount = $policy.Labels.Count | ||
| } | ||
|
|
||
| # Parse PolicySettingsBlob XML for mandatory labeling flags | ||
| if (-not [string]::IsNullOrWhiteSpace($policy.PolicySettingsBlob)) { | ||
| try { | ||
| $xmlSettings = [xml]$policy.PolicySettingsBlob | ||
|
|
||
| # Validate XML structure before accessing properties | ||
| if ($xmlSettings.settings -and $xmlSettings.settings.setting) { | ||
| # Access settings as XML elements for direct property lookup | ||
| foreach ($setting in $xmlSettings.settings.setting) { | ||
| # Add null safety for key and value attributes | ||
| if (-not $setting.key -or -not $setting.value) { | ||
| Write-PSFMessage "Skipping setting with null key or value in policy '$($policy.Name)'" -Level Verbose | ||
| continue | ||
| } | ||
|
|
||
| $key = $setting.key.ToLower() | ||
| $value = $setting.value.ToLower() | ||
|
|
||
| switch ($key) { | ||
| 'mandatory' { | ||
| $policySettings.EmailMandatory = ($value -eq 'true') | ||
| } | ||
| 'teamworkmandatory' { | ||
| $policySettings.TeamworkMandatory = ($value -eq 'true') | ||
| } | ||
| 'siteandgroupmandatory' { | ||
| $policySettings.SiteGroupMandatory = ($value -eq 'true') | ||
| } | ||
| 'powerbimandatory' { | ||
| $policySettings.PowerBIMandatory = ($value -eq 'true') | ||
| } | ||
| 'disablemandatoryinoutlook' { | ||
| $policySettings.EmailOverride = ($value -eq 'true') | ||
| } | ||
| default { | ||
| Write-PSFMessage "Unknown setting key '$key' in policy '$($policy.Name)'" -Level Verbose | ||
| } | ||
| } | ||
| } | ||
| } | ||
| else { | ||
| Write-PSFMessage "Policy '$($policy.Name)' has PolicySettingsBlob but no settings elements found" -Level Verbose | ||
| } | ||
| } | ||
| catch { | ||
| # Track parsing errors for reporting | ||
| $xmlParseErrors += [PSCustomObject]@{ | ||
| PolicyName = $policy.Name | ||
| Error = $_.Exception.Message | ||
| } | ||
| Write-PSFMessage "Error parsing PolicySettingsBlob XML for policy '$($policy.Name)': $_" -Level Warning | ||
| } | ||
| } | ||
|
|
||
| # Per Microsoft documentation, disablemandatoryinoutlook can be set to explicitly | ||
| # disable mandatory labeling in Outlook even when the 'mandatory' setting is true. | ||
| # This provides an exception path for organizations that need mandatory labeling | ||
| # for files but not emails. Apply the override logic: | ||
| if ($policySettings.EmailMandatory -and $policySettings.EmailOverride) { | ||
| $policySettings.EmailMandatory = $false | ||
| } | ||
|
|
||
| # Store all policy settings | ||
| $allPolicySettings += [PSCustomObject]$policySettings | ||
|
|
||
| # Determine if this policy has ANY mandatory setting enabled (after applying overrides) | ||
| $hasMandatory = $policySettings.EmailMandatory -or | ||
| $policySettings.TeamworkMandatory -or | ||
| $policySettings.SiteGroupMandatory -or | ||
| $policySettings.PowerBIMandatory | ||
|
|
||
| if ($hasMandatory) { | ||
| $mandatoryPolicies += [PSCustomObject]$policySettings | ||
| } | ||
| } | ||
| } | ||
| catch { | ||
| Write-PSFMessage "Error parsing label policy settings: $_" -Level Error | ||
| $testResultMarkdown = "⚠️ Unable to determine mandatory labeling status due to unexpected policy settings structure: $_`n`n" | ||
| $customStatus = 'Investigate' | ||
| } | ||
|
|
||
| # Determine pass/fail status and message (only if no error occurred) | ||
| if ($null -eq $customStatus) { | ||
| if ($mandatoryPolicies.Count -gt 0) { | ||
| $passed = $true | ||
| $testResultMarkdown = "✅ Mandatory labeling is configured and enforced through at least one active sensitivity label policy across one or more workloads (Outlook, Teams/OneDrive, SharePoint/Microsoft 365 Groups, or Power BI).`n`n%TestResult%" | ||
| } | ||
| else { | ||
| $passed = $false | ||
|
|
||
| if ($enabledPolicies.Count -eq 0) { | ||
| $testResultMarkdown = "❌ No enabled sensitivity label policies were found in your tenant.`n`n%TestResult%" | ||
| } | ||
| else { | ||
| $testResultMarkdown = "❌ No sensitivity label policies require users to apply labels across any workload (emails, files, sites, groups, or Power BI content).`n`n%TestResult%" | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #endregion Assessment Logic | ||
|
|
||
| #region Report Generation | ||
| $mdInfo = '' | ||
|
|
||
| # Show table whenever we have policy settings | ||
| if ($allPolicySettings.Count -gt 0) { | ||
| # Build policy table | ||
| $mdInfo += "`n`n### [Enabled label policies](https://purview.microsoft.com/informationprotection/labelpolicies)`n" | ||
| $mdInfo += "| Policy name | Email | Files/Collab | Sites/Groups | Power BI | Email override | Scope | Labels |`n" | ||
| $mdInfo += "| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |`n" | ||
|
|
||
| foreach ($policy in $allPolicySettings) { | ||
| $policyName = Get-SafeMarkdown -Text $policy.PolicyName | ||
| $emailIcon = if ($policy.EmailMandatory) { '✅' } else { '❌' } | ||
| $teamworkIcon = if ($policy.TeamworkMandatory) { '✅' } else { '❌' } | ||
| $siteGroupIcon = if ($policy.SiteGroupMandatory) { '✅' } else { '❌' } | ||
| $powerBIIcon = if ($policy.PowerBIMandatory) { '✅' } else { '❌' } | ||
| $overrideIcon = if ($policy.EmailOverride) { 'Yes' } else { 'No' } | ||
| $mdInfo += "| $policyName | $emailIcon | $teamworkIcon | $siteGroupIcon | $powerBIIcon | $overrideIcon | $($policy.Scope) | $($policy.LabelsCount) |`n" | ||
| } | ||
|
|
||
| # Build summary metrics | ||
| $emailCount = ($mandatoryPolicies | Where-Object { $_.EmailMandatory }).Count | ||
| $teamworkCount = ($mandatoryPolicies | Where-Object { $_.TeamworkMandatory }).Count | ||
| $siteGroupCount = ($mandatoryPolicies | Where-Object { $_.SiteGroupMandatory }).Count | ||
| $powerBICount = ($mandatoryPolicies | Where-Object { $_.PowerBIMandatory }).Count | ||
|
|
||
| $mdInfo += "`n`n### Summary`n" | ||
| $mdInfo += "| Metric | Count |`n" | ||
| $mdInfo += "| :--- | :--- |`n" | ||
| $mdInfo += "| Total enabled label policies | $($allPolicySettings.Count) |`n" | ||
| $mdInfo += "| Total enabled label policies with mandatory labeling | $($mandatoryPolicies.Count) |`n" | ||
| $mdInfo += "| Email mandatory labeling | $emailCount |`n" | ||
| $mdInfo += "| File/collaboration mandatory labeling | $teamworkCount |`n" | ||
| $mdInfo += "| Site/group mandatory labeling | $siteGroupCount |`n" | ||
| $mdInfo += "| Power BI mandatory labeling | $powerBICount |" | ||
| } | ||
|
|
||
| # Report XML parsing errors if any occurred | ||
| if ($xmlParseErrors.Count -gt 0) { | ||
| $mdInfo += "`n`n### ⚠️ XML Parsing Errors`n" | ||
| $mdInfo += "The following policies could not be parsed and were excluded from analysis:`n`n" | ||
| $mdInfo += "| Policy Name | Error |`n" | ||
| $mdInfo += "| :--- | :--- |`n" | ||
| foreach ($error in $xmlParseErrors) { | ||
| $errorMsg = Get-SafeMarkdown -Text $error.Error | ||
| $policyName = Get-SafeMarkdown -Text $error.PolicyName | ||
| $mdInfo += "| $policyName | $errorMsg |`n" | ||
| } | ||
| $mdInfo += "`n**Note**: These policies were treated as having no mandatory labeling configured.`n" | ||
| } | ||
|
|
||
| $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo | ||
| #endregion Report Generation | ||
|
|
||
| $params = @{ | ||
| TestId = '35016' | ||
| Title = 'Mandatory labeling enabled for sensitivity labels' | ||
| Status = $passed | ||
| Result = $testResultMarkdown | ||
| } | ||
|
|
||
| # Add CustomStatus if status is 'Investigate' | ||
| if ($null -ne $customStatus) { | ||
| $params.CustomStatus = $customStatus | ||
| } | ||
|
|
||
| Add-ZtTestResultDetail @params | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.