tests/Test-Assessment.27024.ps1

<#
.SYNOPSIS
    Validates that the HTTP DDoS protection rule set is enabled in Azure Front Door WAF.
 
.DESCRIPTION
    This test queries Azure Resource Graph for Azure Front Door Premium WAF policies that are attached to an Azure Front Door and evaluates whether the Microsoft_HTTPDDoSRuleSet is configured and has at least one of its rules enabled.
 
.NOTES
    Test ID: 27024
    Category: Azure Network Security
    Required API: Azure Resource Graph - FrontDoorWebApplicationFirewallPolicies
#>


function Test-Assessment-27024 {
    [ZtTest(
        Category = 'Azure Network Security',
        ImplementationCost = 'Low',
        MinimumLicense = ('Consumption-based: Azure WAF on Azure Front Door Premium'),
        Service = ('Azure'),
        Pillar = 'Network',
        RiskLevel = 'High',
        SfiPillar = 'Protect networks',
        TenantType = ('Workforce'),
        TestId = 27024,
        Title = 'HTTP DDoS protection rule set is enabled in Azure Front Door WAF',
        UserImpact = 'Low'
    )]
    [CmdletBinding()]
    param()

    #region Data Collection
    Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose

    $activity = 'Evaluating Azure Front Door WAF HTTP DDoS protection configuration'
    Write-ZtProgress -Activity $activity -Status 'Querying Azure Front Door WAF policies'

    # Q1: Query attached Premium Azure Front Door WAF policies and their managed rule sets.
    $argQuery = @"
resources
| where type =~ 'microsoft.network/frontdoorwebapplicationfirewallpolicies'
| where tostring(sku.name) =~ 'Premium_AzureFrontDoor'
| where array_length(properties.frontendEndpointLinks) > 0 or array_length(properties.securityPolicyLinks) > 0
| join kind=leftouter (
    resourcecontainers
    | where type =~ 'microsoft.resources/subscriptions'
    | project subscriptionId, SubscriptionName = name
) on subscriptionId
| project
    PolicyName = name,
    PolicyId = id,
    SubscriptionName,
    SubscriptionId = subscriptionId,
    EnabledState = tostring(properties.policySettings.enabledState),
    WafMode = tostring(properties.policySettings.mode),
    ManagedRuleSets = properties.managedRules.managedRuleSets
"@


    $rawPolicies = @()
    try {
        $rawPolicies = @(Invoke-ZtAzureResourceGraphRequest -Query $argQuery | Where-Object { $null -ne $_ })
        Write-PSFMessage "ARG query returned $($rawPolicies.Count) Azure Front Door Premium WAF policy(ies)." -Tag Test -Level VeryVerbose
    }
    catch {
        Write-PSFMessage "Azure Resource Graph query failed: $($_.Exception.Message)" -Tag Test -Level Warning
        Add-ZtTestResultDetail -SkippedBecause NotSupported
        return
    }
    #endregion Data Collection

    #region Assessment Logic
    if ($rawPolicies.Count -eq 0) {
        Write-PSFMessage 'No attached Azure Front Door Premium WAF policies found.' -Tag Test -Level Verbose
        Add-ZtTestResultDetail -SkippedBecause NotApplicable -Result 'No attached Azure Front Door Premium WAF policies found.'
        return
    }

    $policies = foreach ($policy in $rawPolicies) {
        $httpDdosRuleSet = @($policy.ManagedRuleSets | Where-Object { $_.ruleSetType -eq 'Microsoft_HTTPDDoSRuleSet' } | Select-Object -First 1)
        $hasHttpDdosRuleSet = $httpDdosRuleSet.Count -gt 0
        $ruleOverrides = @()

        if ($hasHttpDdosRuleSet) {
            # Unlisted rules retain their default enabled state, so both documented rules must be explicitly disabled.
            foreach ($ruleGroupOverride in @($httpDdosRuleSet[0].ruleGroupOverrides)) {
                $ruleOverrides += @(@($ruleGroupOverride.rules) | Where-Object { $null -ne $_ })
            }
        }

        $disabledRuleIds = @($ruleOverrides | Where-Object {
            $_.ruleId -in @('500100', '500110') -and $_.enabledState -eq 'Disabled'
        } | Select-Object -ExpandProperty ruleId -Unique)
        $bothHttpDdosRulesDisabled = $disabledRuleIds.Count -eq 2

        $httpDdosRulesetState = if (-not $hasHttpDdosRuleSet) {
            'Not Configured'
        }
        elseif ($bothHttpDdosRulesDisabled) {
            'Disabled'
        }
        else {
            'Enabled'
        }

        $isCompliant = $policy.EnabledState -eq 'Enabled' -and
            $policy.WafMode -eq 'Prevention' -and
            $httpDdosRulesetState -eq 'Enabled'

        [PSCustomObject]@{
            PolicyName               = $policy.PolicyName
            PolicyId                 = $policy.PolicyId
            SubscriptionName         = $policy.SubscriptionName
            SubscriptionId           = $policy.SubscriptionId
            EnabledState             = $policy.EnabledState
            WafMode                  = $policy.WafMode
            HttpDdosRulesetState     = $httpDdosRulesetState
            HttpDdosRulesetVersion   = if ($hasHttpDdosRuleSet) { $httpDdosRuleSet[0].ruleSetVersion } else { $null }
            IsCompliant              = $isCompliant
        }
    }

    $failedItems = @($policies | Where-Object { -not $_.IsCompliant })
    $passed = $failedItems.Count -eq 0

    if ($passed) {
        $testResultMarkdown = "✅ All Azure Front Door Premium WAF policies attached to Azure Front Door are enabled, running in Prevention mode, and have the HTTP DDoS ruleset (Microsoft_HTTPDDoSRuleSet) with at least one rule enabled.`n`n%TestResult%"
    }
    else {
        $testResultMarkdown = "❌ One or more Azure Front Door Premium WAF policies attached to Azure Front Door are disabled, running in Detection mode, do not have the HTTP DDoS ruleset configured, or have all HTTP DDoS ruleset rules disabled, leaving applications vulnerable to volumetric HTTP-based attacks at the edge.`n`n%TestResult%"
    }
    #endregion Assessment Logic

    #region Report Generation
    $portalWafBrowseLink = 'https://portal.azure.com/#browse/Microsoft.Network%2FfrontdoorWebApplicationFirewallPolicies'
    $portalResourceBaseLink = 'https://portal.azure.com/#resource'
    $portalSubscriptionBaseLink = 'https://portal.azure.com/#resource/subscriptions'
    $reportTitle = 'Azure Front Door WAF policies'

    $tableRows = ''
    foreach ($policy in ($policies | Sort-Object SubscriptionName, PolicyName)) {
        $policyLink = "[$(Get-SafeMarkdown $policy.PolicyName)]($portalResourceBaseLink$($policy.PolicyId))"
        $subscriptionLink = "[$(Get-SafeMarkdown $policy.SubscriptionName)]($portalSubscriptionBaseLink/$($policy.SubscriptionId)/overview)"
        $enabledStateDisplay = if ($policy.EnabledState -eq 'Enabled') { '✅ Enabled' } else { '❌ Disabled' }
        $wafModeDisplay = if ($policy.WafMode -eq 'Prevention') { '✅ Prevention' } else { "❌ $($policy.WafMode)" }
        $httpDdosRulesetDisplay = if ($policy.HttpDdosRulesetState -eq 'Enabled') { '✅ Enabled' } elseif ($policy.HttpDdosRulesetState -eq 'Disabled') { '❌ Disabled' } else { '❌ Not Configured' }
        $rulesetVersionDisplay = if ($policy.HttpDdosRulesetVersion) { $policy.HttpDdosRulesetVersion } else { 'N/A' }
        $statusDisplay = if ($policy.IsCompliant) { '✅ Pass' } else { '❌ Fail' }

        $tableRows += "| $policyLink | $subscriptionLink | $enabledStateDisplay | $wafModeDisplay | $httpDdosRulesetDisplay | $rulesetVersionDisplay | $statusDisplay |`n"
    }

    $formatTemplate = @'
## [{0}]({1})
 
| Policy name | Subscription name | Enabled state | WAF mode | HTTP DDoS ruleset | Ruleset version | Status |
| :---------- | :---------------- | :------------ | :------- | :---------------- | :-------------- | :----- |
{2}
'@


    $mdInfo = $formatTemplate -f $reportTitle, $portalWafBrowseLink, $tableRows
    $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo
    #endregion Report Generation

    $params = @{
        TestId = '27024'
        Title  = 'HTTP DDoS protection rule set is enabled in Azure Front Door WAF'
        Status = $passed
        Result = $testResultMarkdown
    }

    Add-ZtTestResultDetail @params
}