Skip to content

Commit b2c9693

Browse files
komalp2025Copilot
andauthored
Network 25539 IDPS Inspection is Enabled in Deny Mode on Azure Firewall (#862)
* add 25539 * remove backticks * updating comma Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * update propperty case --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent a95cfd9 commit b2c9693

2 files changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Azure Firewall Premium offers signature-based IDPS to quickly detect attacks by identifying specific patterns, such as byte sequences in network traffic or known malicious instruction sequences used by malware. These IDPS signatures apply to both application and network-level traffic (Layers 3-7). They are fully managed and continuously updated. IDPS can be applied to inbound, spoke-to-spoke (East-West), and outbound traffic, including traffic to/from an on-premises network.
2+
3+
This check verifies that the Intrusion Detection and Prevention System (IDPS) is enabled in “Alert and deny” mode in the Azure Firewall policy configuration. The check will fail if Intrusion Detection and Prevention System (IDPS) is either Disabled (Off) or if it is configured in “Alert” only mode, in the firewall policy attached to the firewall.
4+
5+
If this check does not pass, it means that the Intrusion Detection and Prevention System (IDPS) is not analyzing, detecting, and actively blocking malicious patterns in legitimate looking traffic.
6+
7+
**Remediation action**
8+
9+
- Please check the IDPS section of this article for guidance on how to enable Intrusion Detection and Prevention System (IDPS) in “Alert and Deny” mode in the Azure Firewall Policy.
10+
11+
- [Azure Firewall Premium features implementation guide | Microsoft Learn](https://learn.microsoft.com/en-us/azure/firewall/premium-features)
12+
<!--- Results --->
13+
%TestResult%
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
<#
2+
.SYNOPSIS
3+
Validates Intrusion Detection is Enabled in Deny Mode on Azure Firewall.
4+
.DESCRIPTION
5+
This test validates that Azure Firewall Policies have Intrusion Detection enabled in Deny mode.
6+
Checks all firewall policies in the subscription and reports their intrusion detection status.
7+
.NOTES
8+
Test ID: 25539
9+
Category: Azure Network Security
10+
Required API: Azure Firewall Policies
11+
#>
12+
13+
function Test-Assessment-25539 {
14+
[ZtTest(
15+
Category = 'Azure Network Security',
16+
ImplementationCost = 'Low',
17+
MinimumLicense = ('Azure_Firewall_Premium'),
18+
Pillar = 'Network',
19+
RiskLevel = 'High',
20+
SfiPillar = 'Protect networks',
21+
TenantType = ('Workforce','External'),
22+
TestId = 25539,
23+
Title = 'IDPS Inspection is Enabled in Deny Mode on Azure Firewall',
24+
UserImpact = 'Low'
25+
)]
26+
[CmdletBinding()]
27+
param()
28+
29+
Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose
30+
31+
#region Data Collection
32+
$activity = 'Azure Firewall Intrusion Detection'
33+
Write-ZtProgress `
34+
-Activity $activity `
35+
-Status 'Checking Azure connection'
36+
37+
# Check if connected to Azure
38+
$azContext = Get-AzContext -ErrorAction SilentlyContinue
39+
if (-not $azContext) {
40+
Write-PSFMessage 'Not connected to Azure.' -Level Warning
41+
Add-ZtTestResultDetail -SkippedBecause NotConnectedAzure
42+
return
43+
}
44+
45+
# Check the supported environment
46+
Write-ZtProgress -Activity $activity -Status 'Checking Azure environment'
47+
if ($azContext.Environment.Name -ne 'AzureCloud') {
48+
Write-PSFMessage 'This test is only applicable to the AzureCloud environment.' -Tag Test -Level VeryVerbose
49+
Add-ZtTestResultDetail -SkippedBecause NotApplicable
50+
return
51+
}
52+
53+
Write-ZtProgress -Activity $activity -Status 'Enumerating Firewall Policies'
54+
55+
# Query subscriptions using REST API
56+
$resourceManagerUrl = $azContext.Environment.ResourceManagerUrl.TrimEnd('/')
57+
$subscriptionsUri = "$resourceManagerUrl/subscriptions?api-version=2025-03-01"
58+
59+
try {
60+
$subscriptionsResponse = Invoke-AzRestMethod -Method GET -Uri $subscriptionsUri -ErrorAction Stop
61+
62+
if ($subscriptionsResponse.StatusCode -eq 403) {
63+
Write-PSFMessage 'The signed in user does not have access to check subscriptions.' -Tag Firewall -Level Warning
64+
Add-ZtTestResultDetail -SkippedBecause NoAzureAccess
65+
return
66+
}
67+
68+
if ($subscriptionsResponse.StatusCode -ge 400) {
69+
Write-PSFMessage "Subscriptions request failed with status code $($subscriptionsResponse.StatusCode)" -Tag Firewall -Level Warning
70+
Add-ZtTestResultDetail -SkippedBecause NoAzureAccess
71+
return
72+
}
73+
74+
$subscriptionsContent = $subscriptionsResponse.Content
75+
$subscriptions = ($subscriptionsContent | ConvertFrom-Json).value
76+
}
77+
catch {
78+
Write-PSFMessage "Unable to enumerate subscriptions: $($_.Exception.Message)" -Tag Firewall -Level Warning
79+
Add-ZtTestResultDetail -SkippedBecause NoAzureAccess
80+
return
81+
}
82+
83+
$results = @()
84+
85+
foreach ($sub in $subscriptions) {
86+
87+
# Switch subscription context
88+
try {
89+
Set-AzContext -SubscriptionId $sub.subscriptionId -ErrorAction Stop | Out-Null
90+
}
91+
catch {
92+
Write-PSFMessage "Unable to switch to subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning
93+
continue
94+
}
95+
96+
# Query Azure Firewall Policies
97+
try {
98+
$policiesUri = "$resourceManagerUrl/subscriptions/$($sub.subscriptionId)/providers/Microsoft.Network/firewallPolicies?api-version=2025-03-01"
99+
Write-ZtProgress -Activity $activity -Status "Enumerating policies in subscription $($sub.displayName)"
100+
101+
$policyResponse = Invoke-AzRestMethod -Method GET -Uri $policiesUri -ErrorAction Stop
102+
103+
if ($policyResponse.StatusCode -eq 403) {
104+
Write-PSFMessage "Access denied to firewall policies in subscription $($sub.displayName): Insufficient permissions" -Tag Firewall -Level Warning
105+
continue
106+
}
107+
108+
if ($policyResponse.StatusCode -ge 400) {
109+
Write-PSFMessage "Firewall policies request failed with status code $($policyResponse.StatusCode)" -Tag Firewall -Level Warning
110+
continue
111+
}
112+
113+
$policyResponseContent = $policyResponse.Content
114+
if (-not $policyResponseContent) {
115+
Write-PSFMessage "No response content for policies in subscription $($sub.displayName)" -Tag Firewall -Level Warning
116+
continue
117+
}
118+
119+
$policies = ($policyResponseContent | ConvertFrom-Json).value
120+
}
121+
catch {
122+
Write-PSFMessage "Unable to enumerate firewall policies in subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning
123+
continue
124+
}
125+
126+
if (-not $policies) { continue }
127+
128+
# Get individual firewall policy details
129+
$detailedPolicies = @()
130+
foreach ($policyResource in $policies) {
131+
try {
132+
$detailUri = "$resourceManagerUrl$($policyResource.id)?api-version=2025-03-01"
133+
$detailResponse = Invoke-AzRestMethod -Method GET -Uri $detailUri -ErrorAction Stop
134+
135+
if ($detailResponse.StatusCode -eq 403) {
136+
Write-PSFMessage "Access denied to firewall policy details in subscription $($sub.displayName): Insufficient permissions" -Tag Firewall -Level Warning
137+
continue
138+
}
139+
140+
if ($detailResponse.StatusCode -ge 400) {
141+
Write-PSFMessage "Firewall policy details request failed with status code $($detailResponse.StatusCode)" -Tag Firewall -Level Warning
142+
continue
143+
}
144+
145+
$detailResponseContent = $detailResponse.Content
146+
if (-not $detailResponseContent) {
147+
Write-PSFMessage "No response content for policy $($policyResource.name) in subscription $($sub.displayName)" -Tag Firewall -Level Warning
148+
continue
149+
}
150+
151+
$detailedPolicy = $detailResponseContent | ConvertFrom-Json
152+
$detailedPolicies += $detailedPolicy
153+
}
154+
catch {
155+
Write-PSFMessage "Unable to get detailed policy information for $($policyResource.name) in subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning
156+
}
157+
}
158+
159+
# Check intrusion detection mode for each firewall policy
160+
foreach ($policyResource in $detailedPolicies) {
161+
162+
# Skip if policy is missing required properties
163+
if (-not $policyResource -or -not $policyResource.name -or -not $policyResource.Id -or -not $policyResource.properties) {
164+
Write-PSFMessage "Firewall policy is missing required properties. Skipping." -Tag Firewall -Level Verbose
165+
continue
166+
}
167+
168+
# Skip if SKU tier is not Premium
169+
if ($policyResource.properties.sku.tier -ne 'Premium') {
170+
Write-PSFMessage "Firewall policy '$($policyResource.name)' does not have Premium SKU. Skipping." -Tag Firewall -Level Verbose
171+
continue
172+
}
173+
174+
# Get intrusion detection mode - if not configured, it's disabled by default (FAIL)
175+
$idMode = if ($policyResource.properties.intrusionDetection) {
176+
$policyResource.properties.intrusionDetection.mode
177+
} else {
178+
'Off'
179+
}
180+
# Map intrusion detection mode to user-friendly display values
181+
$detectionModeDisplay = switch ($idMode) {
182+
'Deny' { 'Alert and Deny' }
183+
'Alert' { 'Alert Only' }
184+
'Off' { 'Disabled' }
185+
}
186+
187+
$subContext = Get-AzContext
188+
189+
$results += [PSCustomObject]@{
190+
PolicyName = $policyResource.Name
191+
SubscriptionName = $subContext.Subscription.Name
192+
SubscriptionId = $subContext.Subscription.Id
193+
IntrusionDetectionMode = $detectionModeDisplay
194+
PolicyID = $policyResource.Id
195+
Passed = $idMode -eq 'Deny'
196+
}
197+
}
198+
}
199+
#endregion Data Collection
200+
201+
#region Assessment Logic
202+
203+
# If no Premium firewall policies found, skip the test
204+
if ($results.Count -eq 0) {
205+
Write-PSFMessage 'No Azure Firewall Premium policies found to evaluate.' -Tag Firewall -Level Verbose
206+
Add-ZtTestResultDetail -SkippedBecause NotApplicable
207+
return
208+
}
209+
210+
$failedPolicies = @($results | Where-Object { -not $_.Passed })
211+
$passed = $failedPolicies.Count -eq 0
212+
213+
if ($passed) {
214+
$testResultMarkdown = "Intrusion Detection System (IDPS) inspection is set to Deny for Azure Firewall policies.`n`n%TestResult%"
215+
}
216+
else {
217+
$testResultMarkdown = "Intrusion Detection System (IDPS) inspection is not set to Deny for Azure Firewall policies.`n`n%TestResult%"
218+
}
219+
#endregion Assessment Logic
220+
221+
#region Report Generation
222+
$reportTitle = "Firewall policies"
223+
$tableRows = ""
224+
$mdInfo = ""
225+
226+
if ($results.Count -gt 0) {
227+
# Create a here-string with format placeholders {0}, {1}, etc.
228+
$formatTemplate = @'
229+
230+
## {0}
231+
232+
| Policy name | Subscription name | Result |
233+
| :--- | :--- | :--- |
234+
{1}
235+
236+
'@
237+
238+
foreach ($item in $results | Sort-Object PolicyName) {
239+
$policyLink = "https://portal.azure.com/#resource$($item.PolicyID)"
240+
$subLink = "https://portal.azure.com/#resource/subscriptions/$($item.SubscriptionId)"
241+
$policyMd = "[$(Get-SafeMarkdown -Text $item.PolicyName)]($policyLink)"
242+
$subMd = "[$(Get-SafeMarkdown -Text $item.SubscriptionName)]($subLink)"
243+
$icon = if ($item.Passed) { '' } else { '' }
244+
$resultText = "$icon $($item.IntrusionDetectionMode)"
245+
$tableRows += "| $policyMd | $subMd | $resultText |`n"
246+
}
247+
248+
# Format the template by replacing placeholders with values
249+
$mdInfo = $formatTemplate -f $reportTitle, $tableRows
250+
}
251+
252+
# Replace the placeholder with the detailed information
253+
$testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo
254+
#endregion Report Generation
255+
256+
$params = @{
257+
TestId = '25539'
258+
Status = $passed
259+
Result = $testResultMarkdown
260+
}
261+
262+
Add-ZtTestResultDetail @params
263+
}

0 commit comments

Comments
 (0)