Azure Application Gateway with WAF blocks thousands of attacks. But most organisations have no visibility into what's being blocked.
Here's how to build a monitoring workbook that shows you what your WAF is actually doing.
The Executive Summary Tile
Start with the numbers that matter:
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| where TimeGenerated > ago(30d)
| summarize
TotalAttacksBlocked = count(),
UniqueAttackers = dcount(clientIp_s),
AttacksPerDay = count() / 30
This gives you:
- Total attacks blocked in the last 30 days
- Number of unique attacker IPs
- Average attacks per day
Put these in big number tiles at the top of your workbook.
Attack Trend Over Time
Show whether attacks are increasing or decreasing:
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| summarize Attacks = count() by bin(TimeGenerated, 1d)
| render timechart
Spikes in this chart warrant investigation.
Attack Type Distribution
What kinds of attacks are you seeing?
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| extend AttackType = case(
ruleId_s startswith "942", "SQL Injection",
ruleId_s startswith "941", "Cross-Site Scripting",
ruleId_s startswith "913", "Scanner Detection",
ruleId_s startswith "920", "Protocol Violation",
ruleId_s startswith "931", "Remote File Inclusion",
ruleId_s startswith "932", "Remote Command Execution",
"Other"
)
| summarize Count = count() by AttackType
| render piechart
The rule ID prefixes correspond to OWASP CRS categories. This tells you what threats you're facing.
Top Attackers
Who's hitting you the most?
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| summarize
Attacks = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by clientIp_s
| top 20 by Attacks desc
Persistent attackers might warrant IP blocking at the firewall level.
Scanner and Bot Detection
Find automated scanners in your access logs:
AzureDiagnostics
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayAccessLog"
| where userAgent_s contains "scan"
or userAgent_s contains "bot"
or userAgent_s contains "crawler"
or userAgent_s contains "nikto"
or userAgent_s contains "sqlmap"
or userAgent_s contains "nmap"
| summarize Count = count() by userAgent_s, clientIP_s
| order by Count desc
These might not all be blocked by WAF (some bots are legitimate), but it's good to know who's probing.
Real-Time Alert Status
A traffic light showing current threat level:
AzureDiagnostics
| where TimeGenerated > ago(1h)
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| summarize RecentAttacks = count()
| extend AlertLevel = case(
RecentAttacks > 1000, "CRITICAL",
RecentAttacks > 500, "HIGH",
RecentAttacks > 100, "ELEVATED",
"NORMAL"
)
| project AlertLevel, RecentAttacks
The X-Forwarded-For Problem
If your Application Gateway is behind Azure Firewall, you'll only see the firewall's internal IP as the client IP. The real client IP is in the X-Forwarded-For header.
For custom rules that need to block specific IPs:
resource "azurerm_web_application_firewall_policy" "this" {
custom_rules {
name = "BlockBadIPs"
priority = 1
rule_type = "MatchRule"
match_conditions {
match_variables {
variable_name = "RequestHeaders"
selector = "X-Forwarded-For"
}
operator = "IPMatch"
match_values = ["1.2.3.4", "5.6.7.8"]
}
action = "Block"
}
}
Use RequestHeaders with X-Forwarded-For selector instead of RemoteAddr.
Building the Workbook
- Go to Azure Monitor → Workbooks → New
- Add tiles using the queries above
- Set appropriate time ranges (30d for trends, 1h for alerts)
- Add parameters for time range and App Gateway selection
- Save and pin to a dashboard
Alerting
Don't just monitor - alert on anomalies:
AzureDiagnostics
| where TimeGenerated > ago(1h)
| where ResourceType == "APPLICATIONGATEWAYS"
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| summarize AttackCount = count()
| where AttackCount > 500
Create an alert rule that fires when this returns results.
Need help setting up WAF monitoring or tuning your rules? Get in touch - we help organisations get visibility into their security posture.