tests/Test-Assessment.26889.ps1
|
<#
.SYNOPSIS Validates that diagnostic logging is enabled for Azure Front Door WAF. .DESCRIPTION This test evaluates diagnostic settings for Azure Front Door profiles to ensure WAF log categories are enabled with a valid destination configured (Log Analytics, Storage Account, or Event Hub). .NOTES Test ID: 26889 Category: Azure Network Security Required APIs: Azure Resource Graph (profiles, WAF policies), Azure Management REST API (diagnostic settings) #> function Test-Assessment-26889 { [ZtTest( Category = 'Azure Network Security', ImplementationCost = 'Low', MinimumLicense = ('Azure_FrontDoor_Standard', 'Azure_FrontDoor_Premium'), Pillar = 'Network', RiskLevel = 'High', SfiPillar = 'Monitor and detect cyberthreats', TenantType = ('Workforce'), TestId = 26889, Title = 'Diagnostic logging is enabled in Azure Front Door WAF', UserImpact = 'Low' )] [CmdletBinding()] param() # Required log categories for comprehensive Azure Front Door WAF monitoring $REQUIRED_LOG_CATEGORIES = @( 'FrontDoorAccessLog', 'FrontDoorWebApplicationFirewallLog', 'FrontDoorHealthProbeLog' ) # WAF log category to check for pass/fail criteria (per spec) $WAF_LOG_CATEGORY = 'FrontDoorWebApplicationFirewallLog' #region Data Collection Write-PSFMessage '🟦 Start' -Tag Test -Level VeryVerbose $activity = 'Evaluating Azure Front Door WAF diagnostic logging configuration' # Check if connected to Azure Write-ZtProgress -Activity $activity -Status 'Checking Azure connection' $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 NotSupported return } # Check Azure access token try { $accessToken = Get-AzAccessToken -AsSecureString -ErrorAction SilentlyContinue -WarningAction SilentlyContinue } catch { Write-PSFMessage $_.Exception.Message -Tag Test -Level Error } if (-not $accessToken) { Write-PSFMessage 'Azure authentication token not found.' -Tag Test -Level Warning Add-ZtTestResultDetail -SkippedBecause NotConnectedAzure return } # Q1, Q2, Q3: Query Azure Front Door profiles with WAF associations using Azure Resource Graph Write-ZtProgress -Activity $activity -Status 'Querying Azure Front Door profiles via Resource Graph' # ARG query to get Front Door Standard/Premium profiles with their associated WAF policies # Uses securityPolicyLinks from WAF policies to correlate with Front Door profiles $argQuery = @" resources | where type =~ 'microsoft.cdn/profiles' | where sku.name in~ ('Standard_AzureFrontDoor', 'Premium_AzureFrontDoor') | project FrontDoorId = tolower(id), FrontDoorName = name, SkuName = tostring(sku.name), subscriptionId | join kind=inner ( resources | where type =~ 'microsoft.network/frontdoorwebapplicationfirewallpolicies' | mv-expand securityPolicyLink = properties.securityPolicyLinks | extend LinkedProfileId = tostring(securityPolicyLink.id) | extend LinkedProfileIdLower = tolower(extract('(.+/providers/Microsoft.Cdn/profiles/[^/]+)', 1, LinkedProfileId)) | project WafPolicyName = name, LinkedProfileIdLower ) on `$left.FrontDoorId == `$right.LinkedProfileIdLower | join kind=leftouter ( resourcecontainers | where type =~ 'microsoft.resources/subscriptions' | project subscriptionId, SubscriptionName = name ) on subscriptionId | summarize WafPolicyNames = make_set(WafPolicyName) by FrontDoorId, FrontDoorName, SkuName, subscriptionId, SubscriptionName | project FrontDoorId, FrontDoorName, SkuName, subscriptionId, SubscriptionName, WafPolicyName = strcat_array(WafPolicyNames, ', ') "@ $profilesWithWaf = @() try { $profilesWithWaf = @(Invoke-ZtAzureResourceGraphRequest -Query $argQuery) Write-PSFMessage "ARG Query returned $($profilesWithWaf.Count) Azure Front Door profile(s) with WAF" -Tag Test -Level VeryVerbose } catch { Write-PSFMessage "Azure Resource Graph query failed: $($_.Exception.Message)" -Tag Test -Level Warning Add-ZtTestResultDetail -SkippedBecause NotSupported return } # Skip if no Azure Front Door profiles with WAF exist (per spec step 6) if ($profilesWithWaf.Count -eq 0) { Write-PSFMessage 'No Azure Front Door profiles with WAF found.' -Tag Test -Level VeryVerbose Add-ZtTestResultDetail -SkippedBecause NotApplicable return } # Q4: Get diagnostic settings for each Front Door resource Write-ZtProgress -Activity $activity -Status 'Querying diagnostic settings' $evaluationResults = @() # Iterate only over profiles with WAF (from ARG query above) foreach ($fdItem in $profilesWithWaf) { $frontDoorId = $fdItem.FrontDoorId $frontDoorName = $fdItem.FrontDoorName $wafPolicyName = $fdItem.WafPolicyName # Q4: Query diagnostic settings using Invoke-ZtAzureRequest $diagPath = $frontDoorId + '/providers/Microsoft.Insights/diagnosticSettings?api-version=2021-05-01-preview' $diagSettings = @() try { $diagSettings = @(Invoke-ZtAzureRequest -Path $diagPath) } catch { Write-PSFMessage "Error querying diagnostic settings for $frontDoorName : $_" -Level Warning } # Fallback: If WAF policy name not found via ARG, query security policies endpoint if (-not $wafPolicyName -or $wafPolicyName -eq 'None') { $securityPoliciesPath = $frontDoorId + '/securityPolicies?api-version=2024-02-01' try { $securityPolicies = @(Invoke-ZtAzureRequest -Path $securityPoliciesPath) # Get WAF policy references from security policies $wafPolicyIds = $securityPolicies | ForEach-Object { $_.properties.parameters.wafPolicy.id } | Where-Object { $_ } if ($wafPolicyIds) { # Extract WAF policy names and join if multiple $wafPolicyNames = $wafPolicyIds | ForEach-Object { ($_ -split '/')[-1] } $wafPolicyName = ($wafPolicyNames | Select-Object -Unique) -join ', ' } } catch { Write-PSFMessage "Error querying security policies for $frontDoorName : $_" -Level Warning } } # Evaluate diagnostic settings $hasValidDiagSetting = $false $destinationType = 'None' $enabledCategories = @() $diagSettingNames = @() foreach ($setting in $diagSettings) { $workspaceId = $setting.properties.workspaceId $storageAccountId = $setting.properties.storageAccountId $eventHubAuthRuleId = $setting.properties.eventHubAuthorizationRuleId # Check if destination is configured $hasDestination = $workspaceId -or $storageAccountId -or $eventHubAuthRuleId if ($hasDestination) { $hasValidDiagSetting = $true $diagSettingNames += $setting.name # Determine destination type $destTypes = @() if ($workspaceId) { $destTypes += 'Log Analytics' } if ($storageAccountId) { $destTypes += 'Storage' } if ($eventHubAuthRuleId) { $destTypes += 'Event Hub' } $destinationType = $destTypes -join ', ' # Collect all enabled log categories $logs = $setting.properties.logs foreach ($log in $logs) { if ($log.enabled) { if ($log.categoryGroup -eq 'allLogs' -or $log.categoryGroup -eq 'audit') { # categoryGroup covers all categories in the group $enabledCategories += $REQUIRED_LOG_CATEGORIES } elseif ($log.category) { $enabledCategories += $log.category } } } } } # Join diagnostic setting names after evaluating all settings $diagSettingName = if ($diagSettingNames.Count -gt 0) { ($diagSettingNames | Select-Object -Unique) -join ', ' } else { 'None' } # Check which required log categories are enabled and if WAF log is enabled for pass criteria $enabledCategories = $enabledCategories | Select-Object -Unique $missingRequiredCategories = $REQUIRED_LOG_CATEGORIES | Where-Object { $_ -notin $enabledCategories } $wafLogEnabled = $WAF_LOG_CATEGORY -in $enabledCategories $status = if ($hasValidDiagSetting -and $wafLogEnabled) { 'Pass' } else { 'Fail' } $evaluationResults += [PSCustomObject]@{ SubscriptionId = $fdItem.subscriptionId SubscriptionName = $fdItem.SubscriptionName FrontDoorName = $frontDoorName FrontDoorId = $frontDoorId Sku = $fdItem.SkuName WafPolicy = $wafPolicyName DiagnosticSettingCount = $diagSettings.Count DiagnosticSettingName = $diagSettingName DestinationType = $destinationType EnabledCategories = $enabledCategories -join ', ' MissingRequiredCategories = $missingRequiredCategories -join ', ' WafLogEnabled = $wafLogEnabled Status = $status } } #endregion Data Collection #region Assessment Logic $passedItems = $evaluationResults | Where-Object { $_.Status -eq 'Pass' } $failedItems = $evaluationResults | Where-Object { $_.Status -eq 'Fail' } $passed = ($failedItems.Count -eq 0) -and ($passedItems.Count -gt 0) if ($passed) { $testResultMarkdown = "✅ Diagnostic logging is enabled for Azure Front Door WAF with active log collection configured.`n`n%TestResult%" } else { $testResultMarkdown = "❌ Diagnostic logging is not enabled for Azure Front Door WAF, preventing security monitoring and threat detection at the edge.`n`n%TestResult%" } #endregion Assessment Logic #region Report Generation $mdInfo = "`n## [Azure Front Door WAF diagnostic logging status](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.Cdn%2Fprofiles)`n`n" # Front Door Status table if ($evaluationResults.Count -gt 0) { $tableRows = "" $formatTemplate = @' | Subscription | Profile name | SKU | WAF policy | Diagnostic settings count | Destination configured | Enabled log categories | Status | | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | {0} '@ foreach ($result in $evaluationResults) { $subscriptionLink = "[$(Get-SafeMarkdown $result.SubscriptionName)](https://portal.azure.com/#resource/subscriptions/$($result.SubscriptionId)/overview)" $profileLink = "[$(Get-SafeMarkdown $result.FrontDoorName)](https://portal.azure.com/#resource$($result.FrontDoorId)/diagnostics)" $diagCount = $result.DiagnosticSettingCount $destConfigured = if ($result.DestinationType -eq 'None') { 'No' } else { 'Yes' } $enabledCategories = if ($result.DiagnosticSettingCount -eq 0) { 'No diagnostic settings' } elseif ($result.EnabledCategories) { $result.EnabledCategories } else { 'None' } $statusText = if ($result.Status -eq 'Pass') { '✅ Pass' } else { '❌ Fail' } $tableRows += "| $subscriptionLink | $profileLink | $($result.Sku) | $(Get-SafeMarkdown $result.WafPolicy) | $diagCount | $destConfigured | $enabledCategories | $statusText |`n" } $mdInfo += $formatTemplate -f $tableRows } # Summary $mdInfo += "**Summary:**`n`n" $mdInfo += "- Total Azure Front Door profiles with WAF evaluated: $($evaluationResults.Count)`n" $mdInfo += "- Profiles with diagnostic logging enabled: $($passedItems.Count)`n" $mdInfo += "- Profiles without diagnostic logging: $($failedItems.Count)`n" # Replace the placeholder with detailed information $testResultMarkdown = $testResultMarkdown -replace '%TestResult%', $mdInfo #endregion Report Generation $params = @{ TestId = '26889' Title = 'Diagnostic logging is enabled in Azure Front Door WAF' Status = $passed Result = $testResultMarkdown } Add-ZtTestResultDetail @params } |