tests/Test-Assessment.35012.ps1
|
<#
.SYNOPSIS Validates that container labels are configured for Teams, Groups, and Sites. .DESCRIPTION This test evaluates sensitivity label configuration to ensure container labels are enabled for Microsoft Teams, Microsoft 365 Groups, and SharePoint sites. Container labels enforce consistent security policies at the workspace level, controlling external sharing, guest access, and device restrictions. .NOTES Test ID: 35012 Category: Sensitivity Labels Configuration Required APIs: Get-Label (Exchange PowerShell) #> function Test-Assessment-35012 { [ZtTest( Category = 'Sensitivity Labels Configuration', ImplementationCost = 'Medium', MinimumLicense = ('Microsoft 365 E5'), Pillar = 'Data', RiskLevel = 'Medium', SfiPillar = 'Protect tenants and production systems', TenantType = 'Workforce', TestId = 35012, Title = 'Container labels are configured for Teams, Groups, and Sites', UserImpact = 'High' )] [CmdletBinding()] param() #region Helper Functions function Get-ContainerLabelSummary { <# .SYNOPSIS Extracts container protection settings from a sensitivity label's LabelActions JSON. .OUTPUTS PSCustomObject with container protection details. #> param( [object]$Label, [object]$ProtectGroupAction, [object]$ProtectSiteAction ) # Extract content types from label $contentType = if ($Label.ContentType) { $Label.ContentType -join ', ' } else { 'Not specified' } # Extract Group Privacy Setting from protectgroup action $groupPrivacy = 'Not configured' if ($ProtectGroupAction -and $ProtectGroupAction.Settings) { $privacySetting = $ProtectGroupAction.Settings | Where-Object { $_.Key -eq 'privacy' } if ($privacySetting) { $groupPrivacy = switch ($privacySetting.Value) { '1' { 'Public' } '2' { 'Private' } default { $privacySetting.Value } } } } # Extract Site External Sharing from protectsite action $siteExternalSharing = 'Not configured' $siteGuestAccess = 'Not configured' if ($ProtectSiteAction -and $ProtectSiteAction.Settings) { # External sharing setting $sharingSetting = $ProtectSiteAction.Settings | Where-Object { $_.Key -eq 'externalsharingcontrol' } if ($sharingSetting) { $siteExternalSharing = switch ($sharingSetting.Value) { '0' { 'Full Access' } '1' { 'Limited Access' } '2' { 'Block Access' } default { $sharingSetting.Value } } } # Guest access setting $guestSetting = $ProtectSiteAction.Settings | Where-Object { $_.Key -eq 'allowaccesstoguestusers' } if ($guestSetting) { $siteGuestAccess = switch ($guestSetting.Value) { 'true' { 'Allowed' } 'false' { 'Blocked' } default { $guestSetting.Value } } } } return [PSCustomObject]@{ LabelName = $Label.DisplayName LabelId = $Label.Guid ContentType = $contentType GroupPrivacySetting = $groupPrivacy SiteExternalSharing = $siteExternalSharing SiteGuestAccess = $siteGuestAccess } } function Test-ContainerLabel { <# .SYNOPSIS Tests if a label has both protectgroup and protectsite actions in LabelActions. .OUTPUTS Hashtable with IsContainer boolean and parsed actions, or $null if parsing fails. #> param([object]$Label) try { if ([string]::IsNullOrWhiteSpace($Label.LabelActions)) { return @{ IsContainer = $false; ProtectGroup = $null; ProtectSite = $null } } $actions = $Label.LabelActions | ConvertFrom-Json -ErrorAction Stop $protectGroup = $actions | Where-Object { $_.Type -eq 'protectgroup' } $protectSite = $actions | Where-Object { $_.Type -eq 'protectsite' } return @{ IsContainer = ($null -ne $protectGroup -and $null -ne $protectSite) ProtectGroup = $protectGroup ProtectSite = $protectSite } } catch { # Emit verbose message to aid troubleshooting JSON parsing failures $labelIdentifier = $null if ($null -ne $Label) { if ($Label.PSObject.Properties.Match('DisplayName').Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($Label.DisplayName)) { $labelIdentifier = $Label.DisplayName } elseif ($Label.PSObject.Properties.Match('Name').Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($Label.Name)) { $labelIdentifier = $Label.Name } elseif ($Label.PSObject.Properties.Match('Id').Count -gt 0 -and -not [string]::IsNullOrWhiteSpace($Label.Id)) { $labelIdentifier = $Label.Id } } if (-not $labelIdentifier) { $labelIdentifier = '<unknown label>' } $errorMessage = $_.Exception.Message Write-PSFMessage -Level Verbose -Tag Test -Message ("Failed to parse LabelActions JSON for label '{0}': {1}" -f $labelIdentifier, $errorMessage) # Return null to indicate parsing failure return $null } } #endregion Helper Functions #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Evaluating container label configuration' Write-ZtProgress -Activity $activity -Status 'Retrieving sensitivity labels' # Query Q1: Retrieve all sensitivity labels $allLabels = $null $containerLabels = @() $queryError = $false try { $allLabels = Get-Label -ErrorAction Stop } catch { Write-PSFMessage -Level Warning -Message "Failed to retrieve sensitivity labels: $_" $queryError = $true } # Query Q2: Filter for container-enabled labels (both protectgroup and protectsite actions) $parseError = $false $containerLabelData = @() if ($null -ne $allLabels -and $allLabels.Count -gt 0) { Write-ZtProgress -Activity $activity -Status 'Filtering container-enabled labels' foreach ($label in $allLabels) { $result = Test-ContainerLabel -Label $label if ($null -eq $result) { # JSON parsing failed for at least one label $parseError = $true } elseif ($result.IsContainer) { $containerLabelData += @{ Label = $label ProtectGroup = $result.ProtectGroup ProtectSite = $result.ProtectSite } } } $containerLabels = $containerLabelData } #endregion Data Collection #region Assessment Logic # Initialize evaluation containers $passed = $false $customStatus = $null $testResultMarkdown = '' $labelResults = @() # Step 1: Check if query execution failed if ($queryError) { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ Query fails or LabelActions JSON cannot be parsed due to permissions issues or service connection failure. Ensure the Security & Compliance PowerShell module is connected and the account has appropriate permissions to retrieve label properties.`n`n%TestResult%" } # Step 2: Check if container labels exist (count >= 1) - Pass (even if some labels had parse errors) elseif ($containerLabels.Count -ge 1) { # Container labels are configured - Pass $passed = $true $testResultMarkdown = "✅ Container labels are configured for Teams, Groups, and SharePoint sites.`n`n%TestResult%" # Build label results for reporting foreach ($data in $containerLabels) { $labelResults += Get-ContainerLabelSummary -Label $data.Label -ProtectGroupAction $data.ProtectGroup -ProtectSiteAction $data.ProtectSite } } # Step 3: Check if LabelActions JSON parsing failed for any label (only Investigate when no container labels found) elseif ($parseError) { $customStatus = 'Investigate' $testResultMarkdown = "⚠️ Query fails or LabelActions JSON cannot be parsed due to permissions issues or service connection failure. Some labels could not be evaluated.`n`n%TestResult%" } # Step 4: Count = 0 - Fail else { # No container labels configured # Per spec: "Fail: No container labels are configured (acceptable if Teams/Groups not used; may be a gap if collaboration workspaces exist)" $passed = $false $testResultMarkdown = "❌ No container labels are configured (acceptable if Teams/Groups not used; may be a gap if collaboration workspaces exist).`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $mdInfo = "`n## Summary`n`n" $mdInfo += "| Metric | Value |`n|---|---|`n" $mdInfo += "| Total sensitivity labels | $(if ($allLabels) { $allLabels.Count } else { 0 }) |`n" $mdInfo += "| Container-protected labels | $($containerLabels.Count) |`n`n" if ($labelResults.Count -gt 0) { $tableRows = "" $formatTemplate = @' ## [Container label details](https://purview.microsoft.com/informationprotection/informationprotectionlabels/sensitivitylabels) | Label name | Content type | Group privacy setting | Site external sharing | Site guest access | |---|---|---|---|---| {0} '@ foreach ($r in $labelResults) { $labelLink = "https://purview.microsoft.com/informationprotection/informationprotectionlabels/sensitivitylabels" $linkedLabelName = "[{0}]({1})" -f (Get-SafeMarkdown $r.LabelName), $labelLink $tableRows += "| $linkedLabelName | $($r.ContentType) | $($r.GroupPrivacySetting) | $($r.SiteExternalSharing) | $($r.SiteGuestAccess) |`n" } $mdInfo += $formatTemplate -f $tableRows } # Replace the placeholder with detailed information $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ TestId = '35012' Title = 'Container labels are configured for Teams, Groups, and Sites' Status = $passed Result = $testResultMarkdown } # Add CustomStatus if status is 'Investigate' if ($null -ne $customStatus) { $params.CustomStatus = $customStatus } # Add test result details Add-ZtTestResultDetail @params } |