How to Read Azure WAF Logs Like an Analyst

How to Read Azure WAF Logs Like an Analyst

A practical triage order for Azure Application Gateway and Front Door WAF logs — the five questions an analyst asks before opening a single ticket.

Azure WAF logs are signal-rich and very, very noisy. The difference between drowning in them and pulling an incident out in fifteen minutes is the order in which you ask questions. This is the order I use — five questions, each with the KQL to answer it, applied in sequence before raising anything.

Assumes you have diagnostic settings enabled with resource-specific destination tables and logs landing in a Log Analytics workspace. The queries below target Application Gateway WAF — table AGWFirewallLogs for firewall events and AGWAccessLogs for the matching request records. If you’re still on legacy Azure diagnostics, the same data lives in AzureDiagnostics under categories ApplicationGatewayFirewallLog and ApplicationGatewayAccessLog, but field names get the classic _s / _d suffixes and you have to filter by category. Switch to resource-specific tables if you can — the schema is cleaner and queries are faster.

1. Is this one source, or many?

The first question is always shape. One attacker hammering your endpoint reads very differently from a botnet doing distributed reconnaissance.

AGWFirewallLogs
| where TimeGenerated > ago(1h)
| where Action == "Blocked"
| summarize Blocks = count() by ClientIp
| top 20 by Blocks

If the top one or two IPs account for 80%+ of blocks, you’re looking at a single noisy source — usually a scanner or someone fuzzing. If the distribution is flat across dozens of IPs, that’s a distributed pattern and worth more attention.

Don’t escalate yet. Keep going.

2. What is the WAF actually doing?

The next reality check: is your policy actually blocking, or are you in Detection mode and quietly logging everything?

AGWFirewallLogs
| where TimeGenerated > ago(24h)
| summarize Requests = count() by Action
| order by Requests desc

Action will typically be Blocked, Matched (rule matched but not blocked — Detection mode behavior), Detected, or Allowed. The mode lives on the WAF policy, but the action distribution tells you the truth: if you see thousands of Matched and zero Blocked, the policy is in Detection mode regardless of what the dashboard says.

This is where production WAF deployments fail quietly. A “we’re protected” policy that’s actually still in Detection from the original rollout, never flipped, is more common than anyone wants to admit. Settle that question before anything else.

3. Which rules are firing?

Now look at what the WAF is catching. The top rule IDs tell you the attack pattern.

AGWFirewallLogs
| where TimeGenerated > ago(24h)
| where Action == "Blocked"
| summarize Blocks = count() by RuleId, Message
| top 10 by Blocks

You’re looking for two things:

  • Concentration: if one RuleId (e.g. 942100 — SQL injection) dominates, you have a clear attack vector — and likely a clear false-positive candidate if the source IP matches a known legitimate caller.
  • Spread: if blocks are evenly distributed across many unrelated rules from a single client IP, that’s almost always a scanner working through a payload list. Lower priority, but worth noting.

For the OWASP Core Rule Set, the first three digits of the rule ID tell you the category (913 scanner, 920 protocol enforcement, 941 XSS, 942 SQLi, 949 blocking eval, etc.). Memorize the ranges you see most.

4. Which site / route is being targeted?

Group the same blocks by hostname and request URI to see what the attacker is actually after.

AGWFirewallLogs
| where TimeGenerated > ago(24h)
| where Action == "Blocked"
| summarize Blocks = count() by Hostname, RequestUri
| top 20 by Blocks

Targeted blocks on /wp-login.php when you don’t run WordPress are noise. Targeted blocks on your actual login endpoint are not. The signal-to-noise ratio jumps the moment you cross-reference hostname with a real endpoint someone cares about.

5. Did anything land?

The most important question — and the one most analysts skip. Just because the WAF blocked a thousand requests doesn’t mean the attacker didn’t also send one the WAF allowed that hit a real vulnerability.

Correlate the suspect client IP against the matching access log. Application Gateway emits a TransactionId on both the firewall log and the access log for the same request — that’s your join key:

let suspectIP = "203.0.113.45";
AGWFirewallLogs
| where TimeGenerated > ago(24h)
| where ClientIp == suspectIP
| project TimeGenerated, Action, RuleId, Message, RequestUri, TransactionId
| join kind=inner (
    AGWAccessLogs
    | where TimeGenerated > ago(24h)
    | where ClientIp == suspectIP
    | project TimeGenerated, HttpStatus, RequestUri, TransactionId
) on TransactionId
| where HttpStatus between (200 .. 299)
| order by TimeGenerated desc

Any 2xx response correlated by TransactionId is a request the WAF saw, decided about, and the backend served successfully. If the same client also has Blocked rows for similar payloads, the 2xx rows are where to look hardest — they’re what slipped through.

False-positive triage

When question 3 surfaces a noisy rule firing against a known-good client (a CI job, a partner integration, an internal service), the fix is rarely “disable the rule.” It’s usually one of:

  1. Add an exclusion scoped tightly: specific match variable (e.g. RequestHeaderNames), specific selector (x-myapp-token), specific rule. Never blanket-exclude a whole rule.
  2. Add a custom rule that allows the specific client IP / header signature before the managed rule set evaluates.
  3. Refactor the request if the caller is yours and the WAF is correctly catching a sketchy pattern (e.g. the app is sending SQL keywords in a query parameter because someone got lazy).

Document the reason in your exclusion’s display name. Future-you will not remember why Exclusion-027 exists.

When to escalate

Escalate when:

  • Question 1 shows distributed source pattern AND question 3 shows targeted rule pattern (coordinated effort)
  • Question 2 reveals the policy is in Detection mode when it shouldn’t be
  • Question 5 returns rows (the WAF let something through that the attacker also tried to block)
  • You see successful authentication or data-modifying requests from the suspect IP in adjacent logs

Everything else is noise to characterize, document, and move on from.

Closing

WAF logs reward discipline. Walk the five questions in order — source shape, actual action, rule concentration, target, what landed — and the noise filters itself out without you needing to tune queries for hours. The five-minute version of this checklist beats the perfect dashboard you’ll never build.

Questions? Requests? Suggestions?

We are looking forward to hearing from you!

Are you looking for
Consulting Services,
Support Plans or
Training & Workshops?