Skip to content

Commit 98f0778

Browse files
committed
fix: correct field references in identity and device tests
Fix multiple bugs in CIPP test functions where incorrect field names or IDs were used when querying role data and policies: - Replace `$GA.id` with `$GA.roleTemplateId` when matching PIM role assignments (CIS_1_1_2, CIS_1_1_3) - Replace `$Role.templateId` with `$Role.roleTemplateId` when filtering roles and policies (ZTNA21813, 21816, 21818, 21820) - Fix app protection policy tests to read from correct data type and distinguish between missing data vs. missing platform-specific policy (ZTNA24548, ZTNA24549) - Replace `IsPermanent` for permanent GA detection instead of `AssignmentType -eq 'Permanent'` (ZTNA21835) - Use correct field names from Get-CippDbRoleMembers: `id` instead of `principalId`, `displayName` instead of `principalDisplayName` (ZTNA21835, ZTNA21836) - Fix role management policy lookup to use `roleDefinitionId` instead of nonexistent `effectiveRules.target.targetObjects.id` (ZTNA21819, ZTNA21820) - Clean up Entra URL construction (ZTNA21836) These changes fix tests that were silently returning false negatives due to incorrect field references.
1 parent ba8232e commit 98f0778

12 files changed

Lines changed: 93 additions & 34 deletions

Modules/CIPPTests/Public/Tests/CIS/Identity/Invoke-CippTestCIS_1_1_2.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ function Invoke-CippTestCIS_1_1_2 {
3333
}
3434
}
3535

36+
# roleDefinitionId carries the role's TEMPLATE id, not the directoryRole instance id, so
37+
# comparing it to $GA.id never matched and no PIM-assigned admin was ever counted here —
38+
# only the direct members above.
3639
foreach ($Assignment in @($RoleAssignmentScheduleInstances)) {
37-
if ($Assignment.roleDefinitionId -eq $GA.id -and $Assignment.assignmentType -eq 'Assigned' -and $null -eq $Assignment.endDateTime -and $Assignment.principalId) {
40+
if ($Assignment.roleDefinitionId -eq $GA.roleTemplateId -and $Assignment.assignmentType -eq 'Assigned' -and $null -eq $Assignment.endDateTime -and $Assignment.principalId) {
3841
[void]$GAUserIds.Add([string]$Assignment.principalId)
3942
}
4043
}

Modules/CIPPTests/Public/Tests/CIS/Identity/Invoke-CippTestCIS_1_1_3.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ function Invoke-CippTestCIS_1_1_3 {
2828
}
2929
}
3030

31+
# roleDefinitionId carries the role's TEMPLATE id, not the directoryRole instance id, so
32+
# comparing it to $GA.id never matched and no PIM-assigned admin was ever counted here —
33+
# only the direct members above.
3134
foreach ($Assignment in @($RoleAssignmentScheduleInstances)) {
32-
if ($Assignment.roleDefinitionId -eq $GA.id -and $Assignment.assignmentType -eq 'Assigned' -and $null -eq $Assignment.endDateTime -and $Assignment.principalId) {
35+
if ($Assignment.roleDefinitionId -eq $GA.roleTemplateId -and $Assignment.assignmentType -eq 'Assigned' -and $null -eq $Assignment.endDateTime -and $Assignment.principalId) {
3336
[void]$GAMembers.Add([string]$Assignment.principalId)
3437
}
3538
}

Modules/CIPPTests/Public/Tests/ZTNA/Devices/Invoke-CippTestZTNA24548.ps1

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,33 @@ function Invoke-CippTestZTNA24548 {
99
#Tested - Device
1010

1111
try {
12-
$IosPolicies = Get-CIPPTestData -TenantFilter $Tenant -Type 'IntuneIosAppProtectionPolicies'
12+
# App protection policies for every platform live under one type; URLName carries the
13+
# Graph resource they came from and is the platform discriminator. This previously read
14+
# 'IntuneIosAppProtectionPolicies', a type no collector writes, so the test always skipped.
15+
$AllPolicies = @(Get-CIPPTestData -TenantFilter $Tenant -Type 'IntuneAppProtectionManagedAppPolicies')
1316

14-
if (-not $IosPolicies) {
17+
# Only skip when the type itself is absent (no Intune licence, or collection has not run).
18+
if ($AllPolicies.Count -eq 0) {
1519
Add-CippTestResult -TenantFilter $Tenant -TestId $TestId -TestType 'Devices' -Status 'Skipped' -ResultMarkdown 'No data found in database. This may be due to missing required licenses or data collection not yet completed.' -Risk 'High' -Name 'Data on iOS/iPadOS is protected by app protection policies' -UserImpact 'Low' -ImplementationEffort 'Low' -Category 'Tenant'
1620
return
1721
}
1822

23+
$IosPolicies = @($AllPolicies | Where-Object { $_.URLName -eq 'iosManagedAppProtection' })
24+
25+
# Data exists but no iOS policy at all — that is a genuine failure of this control, not a
26+
# missing-data skip.
27+
if ($IosPolicies.Count -eq 0) {
28+
Add-CippTestResult -TenantFilter $Tenant -TestId $TestId -TestType 'Devices' -Status 'Failed' -ResultMarkdown "❌ No iOS/iPadOS app protection policy exists in this tenant.`n`nApp protection policies were found for other platforms, so Intune data is being collected — there is simply no iOS policy." -Risk 'High' -Name 'Data on iOS/iPadOS is protected by app protection policies' -UserImpact 'Low' -ImplementationEffort 'Low' -Category 'Tenant'
29+
return
30+
}
31+
1932
$AssignedPolicies = @($IosPolicies | Where-Object { $_.assignments -and $_.assignments.Count -gt 0 })
2033
$Passed = $AssignedPolicies.Count -gt 0
2134

2235
if ($Passed) {
2336
$ResultMarkdown = [System.Text.StringBuilder]::new("✅ At least one iOS app protection policy exists and is assigned.`n`n")
2437
} else {
25-
$ResultMarkdown = [System.Text.StringBuilder]::new("No iOS app protection policy exists or none are assigned.`n`n")
38+
$ResultMarkdown = [System.Text.StringBuilder]::new("❌ iOS app protection policies exist but none are assigned.`n`n")
2639
}
2740

2841
$null = $ResultMarkdown.Append("## iOS App Protection Policies`n`n")

Modules/CIPPTests/Public/Tests/ZTNA/Devices/Invoke-CippTestZTNA24549.ps1

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,33 @@ function Invoke-CippTestZTNA24549 {
99
#Tested - Device
1010

1111
try {
12-
$AndroidPolicies = Get-CIPPTestData -TenantFilter $Tenant -Type 'IntuneAndroidAppProtectionPolicies'
12+
# App protection policies for every platform live under one type; URLName carries the
13+
# Graph resource they came from and is the platform discriminator. This previously read
14+
# 'IntuneAndroidAppProtectionPolicies', a type no collector writes, so the test always skipped.
15+
$AllPolicies = @(Get-CIPPTestData -TenantFilter $Tenant -Type 'IntuneAppProtectionManagedAppPolicies')
1316

14-
if (-not $AndroidPolicies) {
17+
# Only skip when the type itself is absent (no Intune licence, or collection has not run).
18+
if ($AllPolicies.Count -eq 0) {
1519
Add-CippTestResult -TenantFilter $Tenant -TestId $TestId -TestType 'Devices' -Status 'Skipped' -ResultMarkdown 'No data found in database. This may be due to missing required licenses or data collection not yet completed.' -Risk 'High' -Name 'Data on Android is protected by app protection policies' -UserImpact 'Low' -ImplementationEffort 'Low' -Category 'Tenant'
1620
return
1721
}
1822

23+
$AndroidPolicies = @($AllPolicies | Where-Object { $_.URLName -eq 'androidManagedAppProtection' })
24+
25+
# Data exists but no Android policy at all — that is a genuine failure of this control, not
26+
# a missing-data skip.
27+
if ($AndroidPolicies.Count -eq 0) {
28+
Add-CippTestResult -TenantFilter $Tenant -TestId $TestId -TestType 'Devices' -Status 'Failed' -ResultMarkdown "❌ No Android app protection policy exists in this tenant.`n`nApp protection policies were found for other platforms, so Intune data is being collected — there is simply no Android policy." -Risk 'High' -Name 'Data on Android is protected by app protection policies' -UserImpact 'Low' -ImplementationEffort 'Low' -Category 'Tenant'
29+
return
30+
}
31+
1932
$AssignedPolicies = @($AndroidPolicies | Where-Object { $_.assignments -and $_.assignments.Count -gt 0 })
2033
$Passed = $AssignedPolicies.Count -gt 0
2134

2235
if ($Passed) {
2336
$ResultMarkdown = [System.Text.StringBuilder]::new("✅ At least one Android app protection policy exists and is assigned.`n`n")
2437
} else {
25-
$ResultMarkdown = [System.Text.StringBuilder]::new("No Android app protection policy exists or none are assigned.`n`n")
38+
$ResultMarkdown = [System.Text.StringBuilder]::new("❌ Android app protection policies exist but none are assigned.`n`n")
2639
}
2740

2841
$null = $ResultMarkdown.Append("## Android App Protection Policies`n`n")

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21813.ps1

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ function Invoke-CippTestZTNA21813 {
2424
$UserRoleMap = @{}
2525

2626
foreach ($Role in $PrivilegedRoles) {
27+
# 'roleTemplateId', not 'templateId' — the Roles cache has no templateId field at all
28+
# (description, displayName, id, memberCount, members, roleTemplateId), so both filters
29+
# below compared against $null and matched nothing.
2730
$ActiveAssignments = $RoleAssignmentScheduleInstances | Where-Object {
28-
$_.roleDefinitionId -eq $Role.templateId -and $_.assignmentType -eq 'Assigned'
31+
$_.roleDefinitionId -eq $Role.roleTemplateId -and $_.assignmentType -eq 'Assigned'
2932
}
3033
$EligibleAssignments = $RoleEligibilitySchedules | Where-Object {
31-
$_.roleDefinitionId -eq $Role.templateId
34+
$_.roleDefinitionId -eq $Role.roleTemplateId
3235
}
3336

3437
$AllAssignments = @($ActiveAssignments) + @($EligibleAssignments)
@@ -38,7 +41,9 @@ function Invoke-CippTestZTNA21813 {
3841
if (-not $User) { continue }
3942

4043
$UserId = $User.id
41-
$IsGARole = $Role.templateId -eq $GlobalAdminRoleId
44+
# roleTemplateId — $Role.templateId does not exist, so this was always false and
45+
# no user was ever classed as a Global Administrator.
46+
$IsGARole = $Role.roleTemplateId -eq $GlobalAdminRoleId
4247

4348
if ($IsGARole) {
4449
$AllGAUsers[$UserId] = $User

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21816.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ function Invoke-CippTestZTNA21816 {
5555
}
5656

5757
foreach ($Role in $PrivilegedRoles) {
58-
if ($Role.templateId -eq $GlobalAdminRoleId) { continue }
58+
# roleTemplateId — $Role.templateId does not exist, so this guard never fired and the
59+
# Global Administrator role was processed here despite being handled separately below.
60+
if ($Role.roleTemplateId -eq $GlobalAdminRoleId) { continue }
5961

6062
$RoleMembers = Get-CippDbRoleMembers -TenantFilter $Tenant -RoleTemplateId $Role.RoletemplateId
6163

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21818.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ function Invoke-CippTestZTNA21818 {
6868
$ExitLoop = $false
6969

7070
foreach ($Role in $PrivilegedRoles) {
71+
# roleDefinitionId carries the role's TEMPLATE id — matching it against $Role.id (the
72+
# directoryRole instance id) never succeeded.
7173
$Policy = $RoleManagementPolicies | Where-Object {
72-
$_.scopeId -eq '/' -and $_.scopeType -eq 'DirectoryRole' -and $_.roleDefinitionId -eq $Role.id
74+
$_.scopeId -eq '/' -and $_.scopeType -eq 'DirectoryRole' -and $_.roleDefinitionId -eq $Role.roleTemplateId
7375
} | Select-Object -First 1
7476

7577
if (-not $Policy) { continue }

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21819.ps1

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ function Invoke-CippTestZTNA21819 {
1717
return
1818
}
1919

20-
# Get role management policy for Global Admin
20+
# Get role management policy for Global Admin.
21+
# This previously matched on effectiveRules.target.targetObjects.id — a property that does
22+
# not exist on this response (target is {caller, operations, level, inheritableSettings,
23+
# enforcedSettings}), so the policy was never found. roleDefinitionId carries the role's
24+
# TEMPLATE id, which is the correct join.
2125
$RoleManagementPolicies = Get-CIPPTestData -TenantFilter $Tenant -Type 'RoleManagementPolicies'
2226
$GlobalAdminPolicy = $RoleManagementPolicies | Where-Object {
23-
$_.scopeId -eq '/' -and $_.scopeType -eq 'DirectoryRole' -and $_.effectiveRules.target.targetObjects.id -contains $GlobalAdminRole.id
24-
}
27+
$_.scopeId -eq '/' -and $_.scopeType -eq 'DirectoryRole' -and $_.roleDefinitionId -eq $GlobalAdminRole.roleTemplateId
28+
} | Select-Object -First 1
2529

2630
$Passed = 'Failed'
2731
$IsDefaultRecipientsEnabled = 'N/A'

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21820.ps1

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,24 @@ function Invoke-CippTestZTNA21820 {
1919
# Get all role management policies
2020
$RoleManagementPolicies = Get-CIPPTestData -TenantFilter $Tenant -Type 'RoleManagementPolicies'
2121

22-
# Build hashtable for quick policy lookup by role ID
22+
# Build hashtable for quick policy lookup by role template ID.
23+
# This previously keyed on effectiveRules.target.targetObjects.id — a property that does
24+
# not exist on this response (target is {caller, operations, level, inheritableSettings,
25+
# enforcedSettings}), so the table was always empty and no policy was ever found.
26+
# roleDefinitionId carries the role's TEMPLATE id, which is the correct key.
2327
$PolicyByRoleId = @{}
2428
foreach ($Policy in $RoleManagementPolicies) {
25-
if ($Policy.scopeId -eq '/' -and $Policy.scopeType -eq 'DirectoryRole') {
26-
foreach ($RoleId in $Policy.effectiveRules.target.targetObjects.id) {
27-
if ($RoleId) {
28-
$PolicyByRoleId[$RoleId] = $Policy
29-
}
30-
}
29+
if ($Policy.scopeId -eq '/' -and $Policy.scopeType -eq 'DirectoryRole' -and $Policy.roleDefinitionId) {
30+
$PolicyByRoleId[$Policy.roleDefinitionId] = $Policy
3131
}
3232
}
3333

3434
$RolesWithIssues = [System.Collections.Generic.List[object]]::new()
3535
$Passed = 'Passed'
3636

3737
foreach ($Role in $PrivilegedRoles) {
38-
$Policy = $PolicyByRoleId[$Role.id]
38+
# Template id, not the directoryRole instance id — see the note above.
39+
$Policy = $PolicyByRoleId[$Role.roleTemplateId]
3940

4041
if (-not $Policy) {
4142
$RolesWithIssues.Add(@{

Modules/CIPPTests/Public/Tests/ZTNA/Identity/Invoke-CippTestZTNA21835.ps1

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ function Invoke-CippTestZTNA21835 {
1717
return
1818
}
1919

20-
# Get permanent Global Administrator members
20+
# Get permanent Global Administrator members.
21+
# This previously filtered on AssignmentType -eq 'Permanent', a value Get-CippDbRoleMembers
22+
# never emits (it returns Active/Eligible/Direct), so the set was always empty and the test
23+
# silently reported no permanent GAs on every tenant. Permanence is IsPermanent.
2124
$PermanentGAMembers = Get-CippDbRoleMembers -TenantFilter $Tenant -RoleTemplateId '62e90394-69f5-4237-9190-012177145e10' | Where-Object {
22-
$_.AssignmentType -eq 'Permanent' -and $_.'@odata.type' -eq '#microsoft.graph.user'
25+
$_.IsPermanent -and $_.'@odata.type' -eq '#microsoft.graph.user'
2326
}
2427

2528
# Get Users data to check sync status
@@ -28,7 +31,8 @@ function Invoke-CippTestZTNA21835 {
2831
$EmergencyAccountCandidates = [System.Collections.Generic.List[object]]::new()
2932

3033
foreach ($Member in $PermanentGAMembers) {
31-
$User = $Users | Where-Object { $_.id -eq $Member.principalId }
34+
# Get-CippDbRoleMembers returns the principal object id as 'id', not 'principalId'.
35+
$User = $Users | Where-Object { $_.id -eq $Member.id }
3236

3337
# Only process cloud-only accounts
3438
if ($User -and $User.onPremisesSyncEnabled -ne $true) {
@@ -165,7 +169,9 @@ function Invoke-CippTestZTNA21835 {
165169

166170
$UserSummary = [System.Collections.Generic.List[object]]::new()
167171
foreach ($Member in $PermanentGAMembers) {
168-
$User = $Users | Where-Object { $_.id -eq $Member.principalId }
172+
# 'id', not 'principalId' — see the note above; this lookup silently matched
173+
# nothing and every row was skipped by the -not $User guard below.
174+
$User = $Users | Where-Object { $_.id -eq $Member.id }
169175
if (-not $User) { continue }
170176

171177
$PortalLink = "https://entra.microsoft.com/#view/Microsoft_AAD_UsersAndTenants/UserProfileMenuBlade/~/overview/userId/$($User.id)"

0 commit comments

Comments
 (0)