-
Notifications
You must be signed in to change notification settings - Fork 118
35001 - Add test for Conditional Access RMS exclusions #719
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a new assessment test (35001) to verify that Microsoft Rights Management Service (RMS) is properly excluded from Conditional Access policies, addressing the issue where RMS authentication blocking can prevent users from accessing encrypted content protected by sensitivity labels.
Key Changes
- Implements test logic to identify CA policies that target RMS without excluding it
- Adds comprehensive unit tests covering multiple scenarios (no policies, properly excluded RMS, blocking policies, error handling, and disabled policies)
- Provides detailed documentation explaining the RMS exclusion requirement and remediation steps
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/powershell/tests/Test-Assessment.35001.ps1 | Main test implementation that queries CA policies and identifies those that block RMS authentication |
| src/powershell/tests/Test-Assessment.35001.md | Documentation explaining why RMS should be excluded from CA policies and remediation steps |
| code-tests/test-assessments/Test-Assessment.35001.Tests.ps1 | Comprehensive unit tests covering various policy scenarios and edge cases |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #region Data Collection | ||
| Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose | ||
|
|
||
| $activity = 'Checking Conditional Access RMS Exclusions' | ||
| Write-ZtProgress -Activity $activity -Status 'Getting Conditional Access policies' |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test specifies 'MIP_P1' as the MinimumLicense but doesn't include a license check at the beginning of the function. Other tests in the codebase that require specific licenses use Get-ZtLicense to check for the license and skip execution if not present. However, Get-ZtLicense currently only supports 'EntraIDP1', 'EntraIDP2', 'EntraIDGovernance', 'EntraWorkloadID', and 'Intune' - it does not support 'MIP_P1'.
You should either:
- Add support for 'MIP_P1' in the Get-ZtLicense function and add a license check at the beginning of this test
- Use an alternative license (like EntraIDP1 or EntraIDP2) that includes MIP capabilities, or
- If no license check is needed, consider updating the MinimumLicense attribute to reflect this
|
|
||
| $testResultDetail = @{ | ||
| TestId = '35001' | ||
| Title = 'Conditional Access RMS Exclusions' |
Copilot
AI
Dec 22, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'Title' parameter is included in the testResultDetail hashtable, but when looking at other tests in the codebase, the Title is typically derived from the ZtTest attribute and not passed explicitly to Add-ZtTestResultDetail. This duplication may be unnecessary. Consider removing the Title parameter from the hashtable to follow the pattern used by other tests, unless there's a specific reason for this deviation.
| Title = 'Conditional Access RMS Exclusions' |
Change Pillar from 'Devices' to 'Data'
Fix policy link to be consistent to other tests Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <# | ||
| # Import required module functions | ||
| @( | ||
| "private/core/Add-ZtTestResultDetail.ps1" | ||
| "private/core/Write-ZtProgress.ps1" | ||
| "private/core/Get-ZtTestStatus.ps1" | ||
| "private/core/Get-SafeMarkdown.ps1" | ||
| ) | ForEach-Object { . (Join-Path $srcRoot $_) } | ||
| #> |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is commented-out code that appears to be for importing required module functions. This commented code should either be removed if it's no longer needed, or uncommented and used if it's required for the tests to run properly. Leaving commented-out code can cause confusion about whether these imports are necessary.
| <# | |
| # Import required module functions | |
| @( | |
| "private/core/Add-ZtTestResultDetail.ps1" | |
| "private/core/Write-ZtProgress.ps1" | |
| "private/core/Get-ZtTestStatus.ps1" | |
| "private/core/Get-SafeMarkdown.ps1" | |
| ) | ForEach-Object { . (Join-Path $srcRoot $_) } | |
| #> |
|
|
||
| $policyName = Get-SafeMarkdown -Text $policy.displayName | ||
|
|
||
| $testResultMarkdown += "| [$policyName]($policyLink) | $($policy.state) | Yes | No | $grantDisplay | $sessionDisplay |`n" |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "RMS Targeted" column always shows "Yes" for all policies in the blocking policies list. While this is technically correct (these policies target RMS), it would be more informative to show whether RMS is targeted via "All" apps or explicitly by its App ID. Consider changing this to show the actual targeting method (e.g., "All apps" or "Explicit").
|
|
||
| $policyName = Get-SafeMarkdown -Text $policy.displayName | ||
|
|
||
| $testResultMarkdown += "| [$policyName]($policyLink) | $($policy.state) | Yes | No | $grantDisplay | $sessionDisplay |`n" |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "RMS Excluded" column always displays "No" for all entries in the blocking policies table because these policies are in the list specifically because they don't exclude RMS (see the filter logic on lines 52-57). This makes the column redundant. Consider removing the "RMS Excluded" column from the table header (line 74) and the table row (line 133) since it provides no additional information - by definition, all policies in this table have RMS not excluded.
| if ($policy.grantControls.builtInControls) { $grantControls += $policy.grantControls.builtInControls } | ||
| if ($policy.grantControls.termsOfUse) { $grantControls += "Terms of Use" } |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition checks if the policy has grantControls before accessing its properties, but does not check if termsOfUse exists before accessing it. While this may not cause an error if termsOfUse is null or undefined in PowerShell, it's inconsistent with the defensive pattern used for builtInControls. Consider adding a null check before accessing policy.grantControls.termsOfUse to maintain consistency.
| if ($policy.grantControls.builtInControls) { $grantControls += $policy.grantControls.builtInControls } | |
| if ($policy.grantControls.termsOfUse) { $grantControls += "Terms of Use" } | |
| $grantControlsObj = $policy.grantControls | |
| if ($grantControlsObj.builtInControls) { $grantControls += $grantControlsObj.builtInControls } | |
| if ($grantControlsObj.PSObject.Properties['termsOfUse'] -and $grantControlsObj.termsOfUse) { $grantControls += "Terms of Use" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,19 @@ | |||
| Microsoft Rights Management Service (RMS) is the protection technology that enforces encryption for sensitivity labels and information protection policies. When users access encrypted content, their applications must authenticate to the RMS service (App ID: `00000012-0000-0000-c000-000000000000`) to decrypt the content. If Conditional Access policies incorrectly block or restrict this authentication - for example, by requiring multi-factor authentication (MFA), device compliance, or specific network locations - users will be unable to open encrypted emails, documents, or files protected by sensitivity labels. | |||
| This is most notable when trying to collaborate on MIP protected content from an external tenant to the source tenant. | |||
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The phrase "collaborate on MIP protected content from an external tenant to the source tenant" is unclear. Consider revising to "when external users try to collaborate on MIP-protected content with users in the source tenant" for better clarity.
| This is most notable when trying to collaborate on MIP protected content from an external tenant to the source tenant. | |
| This is most notable when external users try to collaborate on MIP-protected content with users in the source tenant. |
|
|
||
| if ($isSet) { | ||
| $displayName = $name -replace '([a-z])([A-Z])', '$1 $2' | ||
| $displayName = $displayName.Substring(0,1).ToUpper() + $displayName.Substring(1) |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Substring operation on line 112 could fail if the displayName string is empty after the regex replacement. While unlikely in practice, consider adding a guard check to ensure the string has at least one character before attempting to access index 0 and calling Substring.
| $displayName = $displayName.Substring(0,1).ToUpper() + $displayName.Substring(1) | |
| if (-not [string]::IsNullOrEmpty($displayName)) { | |
| $displayName = $displayName.Substring(0,1).ToUpper() + $displayName.Substring(1) | |
| } |
| state = "enabled" | ||
| conditions = [PSCustomObject]@{ | ||
| applications = [PSCustomObject]@{ | ||
| includeApplications = @("00000012-0000-0000-c000-000000000000") |
Copilot
AI
Jan 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this line has an extra space compared to other Mock statements in the file. The Mock keyword should align with other It block statements at 12 spaces indentation.
…cy in mock definitions
Fix https://github.com/microsoft/ztspecs/issues/53