Skip to content

Commit 2ceb15c

Browse files
Add new tests
1 parent e8e1465 commit 2ceb15c

12 files changed

+1304
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
function Invoke-CippTestZTNA21883 {
2+
<#
3+
.SYNOPSIS
4+
Checks if workload identities are configured with risk-based policies
5+
6+
.DESCRIPTION
7+
Verifies that Conditional Access policies exist that:
8+
- Block authentication based on service principal risk
9+
- Are enabled
10+
- Target service principals
11+
12+
.FUNCTIONALITY
13+
Internal
14+
#>
15+
[CmdletBinding()]
16+
param(
17+
[Parameter(Mandatory = $true)]
18+
[string]$Tenant
19+
)
20+
21+
try {
22+
# Get Conditional Access policies from cache
23+
$Policies = New-CIPPDbRequest -TenantFilter $Tenant -Type 'ConditionalAccessPolicies'
24+
25+
if (-not $Policies) {
26+
Add-CippTestResult -TestId 'ZTNA21883' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Skipped' `
27+
-ResultMarkdown 'No Conditional Access policies found in cache.' `
28+
-Risk 'Medium' -Name 'Workload identities configured with risk-based policies' `
29+
-UserImpact 'High' -ImplementationEffort 'Low' `
30+
-Category 'Access control'
31+
return
32+
}
33+
34+
# Filter for policies that:
35+
# - Block authentication
36+
# - Include service principals
37+
# - Are enabled
38+
$MatchedPolicies = [System.Collections.Generic.List[object]]::new()
39+
foreach ($Policy in $Policies) {
40+
$blocksAuth = $false
41+
if ($Policy.grantControls.builtInControls) {
42+
foreach ($control in $Policy.grantControls.builtInControls) {
43+
if ($control -eq 'block') {
44+
$blocksAuth = $true
45+
break
46+
}
47+
}
48+
}
49+
50+
$includesSP = $false
51+
if ($Policy.conditions.clientApplications.includeServicePrincipals) {
52+
$includesSP = $true
53+
}
54+
55+
$isEnabled = $Policy.state -eq 'enabled'
56+
57+
if ($blocksAuth -and $includesSP -and $isEnabled) {
58+
$MatchedPolicies.Add($Policy)
59+
}
60+
}
61+
62+
# Determine pass/fail
63+
if ($MatchedPolicies.Count -ge 1) {
64+
$Status = 'Passed'
65+
$ResultMarkdown = "✅ **Pass**: Workload identities are protected by risk-based Conditional Access policies.`n`n"
66+
$ResultMarkdown += "## Matching policies`n`n"
67+
$ResultMarkdown += "| Policy name | State | Service principals | Grant controls |`n"
68+
$ResultMarkdown += "| :---------- | :---- | :----------------- | :------------- |`n"
69+
70+
foreach ($Policy in $MatchedPolicies) {
71+
$policyLink = "https://entra.microsoft.com/#view/Microsoft_AAD_ConditionalAccess/PolicyBlade/policyId/$($Policy.id)"
72+
$policyName = if ($Policy.displayName) { $Policy.displayName } else { 'Unnamed' }
73+
$spTargets = if ($Policy.conditions.clientApplications.includeServicePrincipals) {
74+
($Policy.conditions.clientApplications.includeServicePrincipals | Select-Object -First 3) -join ', '
75+
if ($Policy.conditions.clientApplications.includeServicePrincipals.Count -gt 3) {
76+
$spTargets += " (and $($Policy.conditions.clientApplications.includeServicePrincipals.Count - 3) more)"
77+
}
78+
$spTargets
79+
} else {
80+
'None'
81+
}
82+
$grants = if ($Policy.grantControls.builtInControls) {
83+
$Policy.grantControls.builtInControls -join ', '
84+
} else {
85+
'None'
86+
}
87+
$ResultMarkdown += "| [$policyName]($policyLink) | $($Policy.state) | $spTargets | $grants |`n"
88+
}
89+
} else {
90+
$Status = 'Failed'
91+
$ResultMarkdown = "❌ **Fail**: No Conditional Access policies found that protect workload identities with risk-based controls.`n`n"
92+
$ResultMarkdown += 'Workload identities should be protected by policies that block authentication when service principal risk is detected.'
93+
}
94+
95+
Add-CippTestResult -TestId 'ZTNA21883' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status $Status `
96+
-ResultMarkdown $ResultMarkdown `
97+
-Risk 'Medium' -Name 'Workload identities configured with risk-based policies' `
98+
-UserImpact 'High' -ImplementationEffort 'Low' `
99+
-Category 'Access control'
100+
101+
} catch {
102+
Add-CippTestResult -TestId 'ZTNA21883' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Failed' `
103+
-ResultMarkdown "❌ **Error**: $($_.Exception.Message)" `
104+
-Risk 'Medium' -Name 'Workload identities configured with risk-based policies' `
105+
-UserImpact 'High' -ImplementationEffort 'Low' `
106+
-Category 'Access control'
107+
Write-LogMessage -API 'ZeroTrustNetworkAccess' -tenant $Tenant -message "Test ZTNA21883 failed: $($_.Exception.Message)" -sev Error
108+
}
109+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
function Invoke-CippTestZTNA21889 {
2+
<#
3+
.SYNOPSIS
4+
Checks if organization has reduced password surface area by enabling multiple passwordless authentication methods
5+
6+
.DESCRIPTION
7+
Verifies that both FIDO2 Security Keys and Microsoft Authenticator are enabled with proper configuration
8+
to reduce reliance on passwords.
9+
10+
.FUNCTIONALITY
11+
Internal
12+
#>
13+
[CmdletBinding()]
14+
param(
15+
[Parameter(Mandatory = $true)]
16+
[string]$Tenant
17+
)
18+
19+
try {
20+
# Get authentication methods policy from cache
21+
$AuthMethodsPolicy = New-CIPPDbRequest -TenantFilter $Tenant -Type 'AuthenticationMethodsPolicy'
22+
23+
if (-not $AuthMethodsPolicy) {
24+
Add-CippTestResult -TestId 'ZTNA21889' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Skipped' `
25+
-ResultMarkdown 'Unable to retrieve authentication methods policy from cache.' `
26+
-Risk 'High' -Name 'Reduce the user-visible password surface area' `
27+
-UserImpact 'Medium' -ImplementationEffort 'Medium' `
28+
-Category 'Access control'
29+
return
30+
}
31+
32+
# Extract FIDO2 and Microsoft Authenticator configurations
33+
$Fido2Config = $null
34+
$AuthenticatorConfig = $null
35+
36+
if ($AuthMethodsPolicy.authenticationMethodConfigurations) {
37+
foreach ($config in $AuthMethodsPolicy.authenticationMethodConfigurations) {
38+
if ($config.id -eq 'Fido2') {
39+
$Fido2Config = $config
40+
}
41+
if ($config.id -eq 'MicrosoftAuthenticator') {
42+
$AuthenticatorConfig = $config
43+
}
44+
}
45+
}
46+
47+
# Check FIDO2 configuration
48+
$Fido2Enabled = $Fido2Config.state -eq 'enabled'
49+
$Fido2HasTargets = $Fido2Config.includeTargets -and $Fido2Config.includeTargets.Count -gt 0
50+
$Fido2Valid = $Fido2Enabled -and $Fido2HasTargets
51+
52+
# Check Microsoft Authenticator configuration
53+
$AuthEnabled = $AuthenticatorConfig.state -eq 'enabled'
54+
$AuthHasTargets = $AuthenticatorConfig.includeTargets -and $AuthenticatorConfig.includeTargets.Count -gt 0
55+
$AuthMode = $null
56+
if ($AuthenticatorConfig.includeTargets) {
57+
foreach ($target in $AuthenticatorConfig.includeTargets) {
58+
if ($target.authenticationMode) {
59+
$AuthMode = $target.authenticationMode
60+
break
61+
}
62+
}
63+
}
64+
65+
if ([string]::IsNullOrEmpty($AuthMode)) {
66+
$AuthMode = 'Not configured'
67+
$AuthModeValid = $false
68+
} else {
69+
$AuthModeValid = ($AuthMode -eq 'any') -or ($AuthMode -eq 'deviceBasedPush')
70+
}
71+
$AuthValid = $AuthEnabled -and $AuthHasTargets -and $AuthModeValid
72+
73+
# Determine pass/fail
74+
$Status = if ($Fido2Valid -and $AuthValid) { 'Passed' } else { 'Failed' }
75+
76+
# Build result message
77+
if ($Status -eq 'Passed') {
78+
$ResultMarkdown = "✅ **Pass**: Your organization has implemented multiple passwordless authentication methods reducing password exposure.`n`n"
79+
} else {
80+
$ResultMarkdown = "❌ **Fail**: Your organization relies heavily on password-based authentication, creating security vulnerabilities.`n`n"
81+
}
82+
83+
# Build detailed markdown table
84+
$ResultMarkdown += "## Passwordless authentication methods`n`n"
85+
$ResultMarkdown += "| Method | State | Include targets | Authentication mode | Status |`n"
86+
$ResultMarkdown += "| :----- | :---- | :-------------- | :------------------ | :----- |`n"
87+
88+
# FIDO2 row
89+
$Fido2State = if ($Fido2Enabled) { '✅ Enabled' } else { '❌ Disabled' }
90+
$Fido2TargetsDisplay = if ($Fido2Config.includeTargets -and $Fido2Config.includeTargets.Count -gt 0) {
91+
"$($Fido2Config.includeTargets.Count) target(s)"
92+
} else {
93+
'None'
94+
}
95+
$Fido2Status = if ($Fido2Valid) { '✅ Pass' } else { '❌ Fail' }
96+
$ResultMarkdown += "| FIDO2 Security Keys | $Fido2State | $Fido2TargetsDisplay | N/A | $Fido2Status |`n"
97+
98+
# Microsoft Authenticator row
99+
$AuthState = if ($AuthEnabled) { '✅ Enabled' } else { '❌ Disabled' }
100+
$AuthTargetsDisplay = if ($AuthenticatorConfig.includeTargets -and $AuthenticatorConfig.includeTargets.Count -gt 0) {
101+
"$($AuthenticatorConfig.includeTargets.Count) target(s)"
102+
} else {
103+
'None'
104+
}
105+
$AuthModeDisplay = if ($AuthModeValid) { "$AuthMode" } else { "$AuthMode" }
106+
$AuthStatus = if ($AuthValid) { '✅ Pass' } else { '❌ Fail' }
107+
$ResultMarkdown += "| Microsoft Authenticator | $AuthState | $AuthTargetsDisplay | $AuthModeDisplay | $AuthStatus |`n"
108+
109+
Add-CippTestResult -TestId 'ZTNA21889' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status $Status `
110+
-ResultMarkdown $ResultMarkdown `
111+
-Risk 'High' -Name 'Reduce the user-visible password surface area' `
112+
-UserImpact 'Medium' -ImplementationEffort 'Medium' `
113+
-Category 'Access control'
114+
115+
} catch {
116+
Add-CippTestResult -TestId 'ZTNA21889' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Failed' `
117+
-ResultMarkdown "❌ **Error**: $($_.Exception.Message)" `
118+
-Risk 'High' -Name 'Reduce the user-visible password surface area' `
119+
-UserImpact 'Medium' -ImplementationEffort 'Medium' `
120+
-Category 'Access control'
121+
Write-LogMessage -API 'ZeroTrustNetworkAccess' -tenant $Tenant -message "Test ZTNA21889 failed: $($_.Exception.Message)" -sev Error
122+
}
123+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
function Invoke-CippTestZTNA21892 {
2+
<#
3+
.SYNOPSIS
4+
Verifies that all sign-in activity is restricted to managed devices
5+
6+
.DESCRIPTION
7+
Checks for Conditional Access policies that:
8+
- Apply to all users
9+
- Apply to all applications
10+
- Require compliant or hybrid joined devices
11+
- Are enabled
12+
13+
.FUNCTIONALITY
14+
Internal
15+
#>
16+
[CmdletBinding()]
17+
param(
18+
[Parameter(Mandatory = $true)]
19+
[string]$Tenant
20+
)
21+
22+
try {
23+
# Get Conditional Access policies from cache
24+
$Policies = New-CIPPDbRequest -TenantFilter $Tenant -Type 'ConditionalAccessPolicies'
25+
26+
if (-not $Policies) {
27+
Add-CippTestResult -TestId 'ZTNA21892' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Skipped' `
28+
-ResultMarkdown 'No Conditional Access policies found in cache.' `
29+
-Risk 'High' -Name 'All sign-in activity comes from managed devices' `
30+
-UserImpact 'High' -ImplementationEffort 'High' `
31+
-Category 'Access control'
32+
return
33+
}
34+
35+
# Find policies that require managed devices for all users and apps
36+
$MatchingPolicies = [System.Collections.Generic.List[object]]::new()
37+
foreach ($Policy in $Policies) {
38+
# Check if applies to all users
39+
$appliesToAllUsers = $false
40+
if ($Policy.conditions.users.includeUsers) {
41+
foreach ($user in $Policy.conditions.users.includeUsers) {
42+
if ($user -eq 'All') {
43+
$appliesToAllUsers = $true
44+
break
45+
}
46+
}
47+
}
48+
49+
# Check if applies to all apps
50+
$appliesToAllApps = $false
51+
if ($Policy.conditions.applications.includeApplications) {
52+
foreach ($app in $Policy.conditions.applications.includeApplications) {
53+
if ($app -eq 'All') {
54+
$appliesToAllApps = $true
55+
break
56+
}
57+
}
58+
}
59+
60+
# Check if requires compliant or hybrid joined device
61+
$requiresCompliantDevice = $false
62+
$requiresHybridJoined = $false
63+
if ($Policy.grantControls.builtInControls) {
64+
foreach ($control in $Policy.grantControls.builtInControls) {
65+
if ($control -eq 'compliantDevice') {
66+
$requiresCompliantDevice = $true
67+
}
68+
if ($control -eq 'domainJoinedDevice') {
69+
$requiresHybridJoined = $true
70+
}
71+
}
72+
}
73+
74+
$isEnabled = $Policy.state -eq 'enabled'
75+
76+
# Policy matches if enabled, applies to all users/apps, and requires managed device
77+
if ($isEnabled -and $appliesToAllUsers -and $appliesToAllApps -and ($requiresCompliantDevice -or $requiresHybridJoined)) {
78+
$MatchingPolicies.Add([PSCustomObject]@{
79+
PolicyId = $Policy.id
80+
PolicyState = $Policy.state
81+
DisplayName = $Policy.displayName
82+
AllUsers = $appliesToAllUsers
83+
AllApps = $appliesToAllApps
84+
CompliantDevice = $requiresCompliantDevice
85+
HybridJoinedDevice = $requiresHybridJoined
86+
IsFullyCompliant = $isEnabled -and $appliesToAllUsers -and $appliesToAllApps -and ($requiresCompliantDevice -or $requiresHybridJoined)
87+
})
88+
}
89+
}
90+
91+
# Determine pass/fail
92+
if ($MatchingPolicies.Count -gt 0) {
93+
$Status = 'Passed'
94+
$ResultMarkdown = "✅ **Pass**: Conditional Access policies require managed devices for all sign-in activity.`n`n"
95+
$ResultMarkdown += "## Matching policies`n`n"
96+
$ResultMarkdown += "| Policy name | State | All users | All apps | Compliant device | Hybrid joined |`n"
97+
$ResultMarkdown += "| :---------- | :---- | :-------- | :------- | :--------------- | :------------ |`n"
98+
99+
foreach ($Policy in $MatchingPolicies) {
100+
$policyLink = "https://entra.microsoft.com/#view/Microsoft_AAD_ConditionalAccess/PolicyBlade/policyId/$($Policy.PolicyId)"
101+
$policyName = if ($Policy.DisplayName) { $Policy.DisplayName } else { 'Unnamed' }
102+
$allUsers = if ($Policy.AllUsers) { '' } else { '' }
103+
$allApps = if ($Policy.AllApps) { '' } else { '' }
104+
$compliant = if ($Policy.CompliantDevice) { '' } else { '' }
105+
$hybrid = if ($Policy.HybridJoinedDevice) { '' } else { '' }
106+
107+
$ResultMarkdown += "| [$policyName]($policyLink) | $($Policy.PolicyState) | $allUsers | $allApps | $compliant | $hybrid |`n"
108+
}
109+
} else {
110+
$Status = 'Failed'
111+
$ResultMarkdown = "❌ **Fail**: No Conditional Access policies found that require managed devices for all sign-in activity.`n`n"
112+
$ResultMarkdown += 'Organizations should enforce that all sign-ins come from managed devices (compliant or hybrid Azure AD joined) to ensure security controls are applied.'
113+
}
114+
115+
Add-CippTestResult -TestId 'ZTNA21892' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status $Status `
116+
-ResultMarkdown $ResultMarkdown `
117+
-Risk 'High' -Name 'All sign-in activity comes from managed devices' `
118+
-UserImpact 'High' -ImplementationEffort 'High' `
119+
-Category 'Access control'
120+
121+
} catch {
122+
Add-CippTestResult -TestId 'ZTNA21892' -TenantFilter $Tenant -TestType 'ZeroTrustNetworkAccess' -Status 'Failed' `
123+
-ResultMarkdown "❌ **Error**: $($_.Exception.Message)" `
124+
-Risk 'High' -Name 'All sign-in activity comes from managed devices' `
125+
-UserImpact 'High' -ImplementationEffort 'High' `
126+
-Category 'Access control'
127+
Write-LogMessage -API 'ZeroTrustNetworkAccess' -tenant $Tenant -message "Test ZTNA21892 failed: $($_.Exception.Message)" -sev Error
128+
}
129+
}

0 commit comments

Comments
 (0)