tests/functions/Test-MtCaExclusionForDirectorySyncAccount.Tests.ps1
|
Describe 'Test-MtCaExclusionForDirectorySyncAccount' { BeforeAll { Mock -ModuleName Maester Get-MtLicenseInformation { return 'P1' } Mock -ModuleName Maester Add-MtTestResultDetail {} # Role template IDs returned by Get-MtRoleInfo $script:DirSyncRoleId = 'd29b2b05-8046-44ba-8758-1e26182fcf32' $script:OnPremRoleId = 'a92aed5d-d78a-4d16-b381-09adb37eb3b0' # Sample sync account member IDs $script:syncUserId1 = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' $script:syncUserId2 = 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb' $script:syncSpId = 'cccccccc-cccc-cccc-cccc-cccccccccccc' # Member objects as returned by Get-MtRoleMember $script:syncUser1 = [PSCustomObject]@{ id = $script:syncUserId1 '@odata.type' = '#microsoft.graph.user' } $script:syncUser2 = [PSCustomObject]@{ id = $script:syncUserId2 '@odata.type' = '#microsoft.graph.user' } $script:syncServicePrincipal = [PSCustomObject]@{ id = $script:syncSpId '@odata.type' = '#microsoft.graph.servicePrincipal' } # Helper: build a minimal enabled CA policy object function New-CaPolicy { param( [string] $Id = 'policy1', [string] $DisplayName = 'Test Policy', [string[]] $IncludeApplications = @('All'), [string[]] $IncludeUsers = @('All'), [string[]] $IncludeRoles = @(), [string[]] $ExcludeUsers = @(), [string[]] $ExcludeRoles = @(), [string] $IncludeGuestsOrExternalUsers = $null, [string[]] $ClientAppTypes = @('all'), [string[]] $BuiltInControls = @() ) [PSCustomObject]@{ id = $Id displayName = $DisplayName state = 'enabled' conditions = [PSCustomObject]@{ applications = [PSCustomObject]@{ includeApplications = $IncludeApplications } users = [PSCustomObject]@{ includeUsers = $IncludeUsers includeRoles = $IncludeRoles excludeUsers = $ExcludeUsers excludeRoles = $ExcludeRoles includeGroups = @() includeGuestsOrExternalUsers = $IncludeGuestsOrExternalUsers } clientAppTypes = $ClientAppTypes } grantControls = [PSCustomObject]@{ builtInControls = $BuiltInControls } } } # Default mock: Get-MtRoleInfo returns the correct GUIDs by role name Mock -ModuleName Maester Get-MtRoleInfo { param($RoleName) switch ($RoleName) { 'DirectorySynchronizationAccounts' { return $script:DirSyncRoleId } 'OnPremisesDirectorySyncAccount' { return $script:OnPremRoleId } } } } Context 'No sync account members exist in the tenant' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $null } } It 'Should return true and mark test as not applicable' { Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @() } Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Service principal-only members (no user members)' { BeforeEach { # Only a service principal is in the role — no users to exclude Mock -ModuleName Maester Get-MtRoleMember { return $script:syncServicePrincipal } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because service principals cannot be excluded from CA policies' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy does not target all applications' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -IncludeApplications @('00000002-0000-0ff1-ce00-000000000000') -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because policy is not scoped to all apps' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy only targets guests (no internal users)' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -IncludeUsers @() -IncludeGuestsOrExternalUsers 'b2bCollaborationGuest' -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because policy does not scope internal users' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy only blocks legacy authentication' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ClientAppTypes @('exchangeActiveSync', 'other') -BuiltInControls @('block') -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because policy only blocks legacy auth which sync does not use' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy targets all users and sync account is excluded by DirectorySynchronizationAccounts role' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeRoles @($script:DirSyncRoleId)) } } It 'Should return true' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy targets all users and sync account is excluded by OnPremisesDirectorySyncAccount role' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeRoles @($script:OnPremRoleId)) } } It 'Should return true' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy targets all users and all sync user members are individually excluded' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return @($script:syncUser1, $script:syncUser2) } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeUsers @($script:syncUserId1, $script:syncUserId2)) } } It 'Should return true' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy targets all users and sync account is NOT excluded' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return false' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeFalse } } Context 'Policy targets all users and sync accounts are only partially excluded by user IDs' { BeforeEach { # Two user members but only one is excluded Mock -ModuleName Maester Get-MtRoleMember { return @($script:syncUser1, $script:syncUser2) } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeUsers @($script:syncUserId1)) } } It 'Should return false because not all user members are excluded' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeFalse } } Context 'Policy targets all users and sync accounts are only partially excluded (service principal skipped, one user excluded)' { BeforeEach { # One user + one service principal; only the user is excluded Mock -ModuleName Maester Get-MtRoleMember { return @($script:syncUser1, $script:syncServicePrincipal) } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @(New-CaPolicy -ExcludeUsers @($script:syncUserId1)) } } It 'Should return true because the only user member is excluded and service principals are not subject to CA' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy explicitly includes a sync account by user ID (policy designed for sync accounts)' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { # Policy specifically includes the sync account ID — must not be required to also exclude it return @(New-CaPolicy -IncludeUsers @($script:syncUserId1) -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because the policy is scoped specifically to the sync account' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Policy explicitly includes a sync role (policy designed for sync accounts)' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { # Policy scoped to the DirectorySynchronizationAccounts role — not an "all users" policy return @(New-CaPolicy -IncludeUsers @() -IncludeRoles @($script:DirSyncRoleId) -ExcludeUsers @() -ExcludeRoles @()) } } It 'Should return true because the policy is scoped specifically to the sync role' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Multiple policies — one passes, one fails' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { return @( New-CaPolicy -Id 'policy-pass' -DisplayName 'Policy With Exclusion' -ExcludeRoles @($script:DirSyncRoleId), New-CaPolicy -Id 'policy-fail' -DisplayName 'Policy Without Exclusion' -ExcludeUsers @() -ExcludeRoles @() ) } } It 'Should return false because at least one policy does not exclude sync accounts' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeFalse } } Context 'Multiple policies — all pass (each excluded by a different sync role)' { BeforeEach { Mock -ModuleName Maester Get-MtRoleMember { return $script:syncUser1 } Mock -ModuleName Maester Get-MtConditionalAccessPolicy { # Hardcode GUIDs to avoid $script: variable resolution inside -ModuleName mock context $p1 = New-CaPolicy -Id 'policy1' -DisplayName 'Excluded By DirSync Role' -ExcludeRoles @('d29b2b05-8046-44ba-8758-1e26182fcf32') $p2 = New-CaPolicy -Id 'policy2' -DisplayName 'Excluded By OnPrem Role' -ExcludeRoles @('a92aed5d-d78a-4d16-b381-09adb37eb3b0') return @($p1, $p2) } } It 'Should return true because all policies exclude sync accounts' { Test-MtCaExclusionForDirectorySyncAccount | Should -BeTrue } } Context 'Free Entra ID license' { It 'Should return null and skip the test' { Mock -ModuleName Maester Get-MtLicenseInformation { return 'Free' } Test-MtCaExclusionForDirectorySyncAccount | Should -BeNull } } } |