tests/Test-Assessment.25539.ps1
|
<#
.SYNOPSIS Validates Intrusion Detection is Enabled in Deny Mode on Azure Firewall. .DESCRIPTION This test validates that Azure Firewall Policies have Intrusion Detection enabled in Deny mode. Checks all firewall policies in the subscription and reports their intrusion detection status. .NOTES Test ID: 25539 Category: Azure Network Security Required API: Azure Firewall Policies #> function Test-Assessment-25539 { [ZtTest( Category = 'Azure Network Security', ImplementationCost = 'Low', MinimumLicense = ('Azure_Firewall_Premium'), Pillar = 'Network', RiskLevel = 'High', SfiPillar = 'Protect networks', TenantType = ('Workforce','External'), TestId = 25539, Title = 'IDPS Inspection is Enabled in Deny Mode on Azure Firewall', UserImpact = 'Low' )] [CmdletBinding()] param() Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose #region Data Collection $activity = 'Azure Firewall Intrusion Detection' Write-ZtProgress ` -Activity $activity ` -Status 'Checking Azure connection' # Check if connected to Azure $azContext = Get-AzContext -ErrorAction SilentlyContinue if (-not $azContext) { Write-PSFMessage 'Not connected to Azure.' -Level Warning Add-ZtTestResultDetail -SkippedBecause NotConnectedAzure return } # Check the supported environment Write-ZtProgress -Activity $activity -Status 'Checking Azure environment' if ($azContext.Environment.Name -ne 'AzureCloud') { Write-PSFMessage 'This test is only applicable to the AzureCloud environment.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } Write-ZtProgress -Activity $activity -Status 'Enumerating Firewall Policies' # Query subscriptions using REST API $resourceManagerUrl = $azContext.Environment.ResourceManagerUrl.TrimEnd('/') $subscriptionsUri = "$resourceManagerUrl/subscriptions?api-version=2025-03-01" try { $subscriptionsResponse = Invoke-AzRestMethod -Method GET -Uri $subscriptionsUri -ErrorAction Stop if ($subscriptionsResponse.StatusCode -eq 403) { Write-PSFMessage 'The signed in user does not have access to check subscriptions.' -Tag Firewall -Level Warning Add-ZtTestResultDetail -SkippedBecause NoAzureAccess return } if ($subscriptionsResponse.StatusCode -ge 400) { Write-PSFMessage "Subscriptions request failed with status code $($subscriptionsResponse.StatusCode)" -Tag Firewall -Level Warning Add-ZtTestResultDetail -SkippedBecause NoAzureAccess return } $subscriptionsContent = $subscriptionsResponse.Content $subscriptions = ($subscriptionsContent | ConvertFrom-Json).value } catch { Write-PSFMessage "Unable to enumerate subscriptions: $($_.Exception.Message)" -Tag Firewall -Level Warning Add-ZtTestResultDetail -SkippedBecause NoAzureAccess return } $results = @() foreach ($sub in $subscriptions) { # Switch subscription context try { Set-AzContext -SubscriptionId $sub.subscriptionId -ErrorAction Stop | Out-Null } catch { Write-PSFMessage "Unable to switch to subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning continue } # Query Azure Firewall Policies try { $policiesUri = "$resourceManagerUrl/subscriptions/$($sub.subscriptionId)/providers/Microsoft.Network/firewallPolicies?api-version=2025-03-01" Write-ZtProgress -Activity $activity -Status "Enumerating policies in subscription $($sub.displayName)" $policyResponse = Invoke-AzRestMethod -Method GET -Uri $policiesUri -ErrorAction Stop if ($policyResponse.StatusCode -eq 403) { Write-PSFMessage "Access denied to firewall policies in subscription $($sub.displayName): Insufficient permissions" -Tag Firewall -Level Warning continue } if ($policyResponse.StatusCode -ge 400) { Write-PSFMessage "Firewall policies request failed with status code $($policyResponse.StatusCode)" -Tag Firewall -Level Warning continue } $policyResponseContent = $policyResponse.Content if (-not $policyResponseContent) { Write-PSFMessage "No response content for policies in subscription $($sub.displayName)" -Tag Firewall -Level Warning continue } $policies = ($policyResponseContent | ConvertFrom-Json).value } catch { Write-PSFMessage "Unable to enumerate firewall policies in subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning continue } if (-not $policies) { continue } # Get individual firewall policy details $detailedPolicies = @() foreach ($policyResource in $policies) { try { $detailUri = "$resourceManagerUrl$($policyResource.id)?api-version=2025-03-01" $detailResponse = Invoke-AzRestMethod -Method GET -Uri $detailUri -ErrorAction Stop if ($detailResponse.StatusCode -eq 403) { Write-PSFMessage "Access denied to firewall policy details in subscription $($sub.displayName): Insufficient permissions" -Tag Firewall -Level Warning continue } if ($detailResponse.StatusCode -ge 400) { Write-PSFMessage "Firewall policy details request failed with status code $($detailResponse.StatusCode)" -Tag Firewall -Level Warning continue } $detailResponseContent = $detailResponse.Content if (-not $detailResponseContent) { Write-PSFMessage "No response content for policy $($policyResource.name) in subscription $($sub.displayName)" -Tag Firewall -Level Warning continue } $detailedPolicy = $detailResponseContent | ConvertFrom-Json $detailedPolicies += $detailedPolicy } catch { Write-PSFMessage "Unable to get detailed policy information for $($policyResource.name) in subscription $($sub.displayName): $($_.Exception.Message)" -Tag Firewall -Level Warning } } # Check intrusion detection mode for each firewall policy foreach ($policyResource in $detailedPolicies) { # Skip if policy is missing required properties if (-not $policyResource -or -not $policyResource.name -or -not $policyResource.Id -or -not $policyResource.properties) { Write-PSFMessage "Firewall policy is missing required properties. Skipping." -Tag Firewall -Level Verbose continue } # Skip if SKU tier is not Premium if ($policyResource.properties.sku.tier -ne 'Premium') { Write-PSFMessage "Firewall policy '$($policyResource.name)' does not have Premium SKU. Skipping." -Tag Firewall -Level Verbose continue } # Get intrusion detection mode - if not configured, it's disabled by default (FAIL) $idMode = if ($policyResource.properties.intrusionDetection) { $policyResource.properties.intrusionDetection.mode } else { 'Off' } # Map intrusion detection mode to user-friendly display values $detectionModeDisplay = switch ($idMode) { 'Deny' { 'Alert and Deny' } 'Alert' { 'Alert Only' } 'Off' { 'Disabled' } } $subContext = Get-AzContext $results += [PSCustomObject]@{ PolicyName = $policyResource.Name SubscriptionName = $subContext.Subscription.Name SubscriptionId = $subContext.Subscription.Id IntrusionDetectionMode = $detectionModeDisplay PolicyID = $policyResource.Id Passed = $idMode -eq 'Deny' } } } #endregion Data Collection #region Assessment Logic # If no Premium firewall policies found, skip the test if ($results.Count -eq 0) { Write-PSFMessage 'No Azure Firewall Premium policies found to evaluate.' -Tag Firewall -Level Verbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } $failedPolicies = @($results | Where-Object { -not $_.Passed }) $passed = $failedPolicies.Count -eq 0 if ($passed) { $testResultMarkdown = "Intrusion Detection System (IDPS) inspection is set to Deny for Azure Firewall policies.`n`n%TestResult%" } else { $testResultMarkdown = "Intrusion Detection System (IDPS) inspection is not set to Deny for Azure Firewall policies.`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $reportTitle = "Firewall policies" $tableRows = "" $mdInfo = "" if ($results.Count -gt 0) { # Create a here-string with format placeholders {0}, {1}, etc. $formatTemplate = @' ## {0} | Policy name | Subscription name | Result | | :--- | :--- | :--- | {1} '@ foreach ($item in $results | Sort-Object PolicyName) { $policyLink = "https://portal.azure.com/#resource$($item.PolicyID)" $subLink = "https://portal.azure.com/#resource/subscriptions/$($item.SubscriptionId)" $policyMd = "[$(Get-SafeMarkdown -Text $item.PolicyName)]($policyLink)" $subMd = "[$(Get-SafeMarkdown -Text $item.SubscriptionName)]($subLink)" $icon = if ($item.Passed) { '✅' } else { '❌' } $resultText = "$icon $($item.IntrusionDetectionMode)" $tableRows += "| $policyMd | $subMd | $resultText |`n" } # Format the template by replacing placeholders with values $mdInfo = $formatTemplate -f $reportTitle, $tableRows } # Replace the placeholder with the detailed information $testResultMarkdown = $testResultMarkdown -replace "%TestResult%", $mdInfo #endregion Report Generation $params = @{ TestId = '25539' Status = $passed Result = $testResultMarkdown } Add-ZtTestResultDetail @params } |