Cloud

Azure WAF: move a policy from Detection to Prevention without breaking traffic

A production runbook for qualifying an Azure WAF policy before switching to Prevention with KQL evidence, change scope, application validation, rollback window and an operable decision.

20 Jun 2026 azurewafapplication-gatewaykqlsecurityobservabilityrunbookrollbackproduction

Moving an Azure WAF policy from Detection to Prevention can look like a simple security toggle. In production, it is an application change. Requests that were only logged can start being blocked, partner integrations can disappear behind 403 responses, and support teams may discover too late that they cannot separate a real block from a false positive.

The use case is an application published behind Application Gateway WAF. The policy has been running in Detection for days or weeks. The team wants to enable Prevention without weakening managed rules, without adding broad exclusions and without turning the first hour into an incident. The runbook goal is to decide whether the switch is ready, how to watch it, what to roll back and which evidence to keep.

Treat the switch as a production change

A policy in Detection is not inactive. It produces an operational simulation of future blocks. The useful question is therefore not only “how many rules matched?”. The useful question is: which legitimate requests would be blocked if the policy actually enforced its decisions?

text waf-detection-to-prevention-scope.txt
Change scope
Affected Application Gateway and WAF policy
Published hostnames
Critical paths: login, payment, import, webhook, partner API, callback
Impacted environments
Managed ruleset version and active custom rules

Expected evidence
Top simulated blocks in Detection
Associated ruleId and message
URI, method, hostname, client IP and user agent
Volume by time bucket
Samples of legitimate and illegitimate requests
Decision per case: accept, fix application, targeted exclusion, custom rule, rollback

Switch conditions
Critical false positives handled
Monitoring ready
Support aware of expected symptoms
Rollback documented and tested

This intake prevents a common mistake: enabling Prevention because the global match count looks low while one business-critical path contains most requests that will be blocked.

Read Detection logs as a forecast

The first query should isolate what Prevention would block. Depending on ingestion, tables and fields may differ between AzureDiagnostics and dedicated Application Gateway tables. The principle stays the same: period, hostname, URI, action, ruleId, message and number of distinct clients.

kusto 01-waf-detection-candidates.kql
let Window = 7d;
AzureDiagnostics
| where TimeGenerated > ago(Window)
| where Category == "ApplicationGatewayFirewallLog"
| extend hostname = tostring(host_s)
| extend uri = tostring(requestUri_s)
| extend method = tostring(requestMethod_s)
| extend action = tostring(action_s)
| extend ruleId = tostring(ruleId_s)
| extend message = tostring(message_s)
| extend clientIp = tostring(clientIp_s)
| where action in ("Matched", "Detected", "Blocked")
| summarize hits=count(),
          clients=dcount(clientIp),
          firstSeen=min(TimeGenerated),
          lastSeen=max(TimeGenerated),
          sampleMessages=make_set(message, 3)
by hostname, uri, method, ruleId, action
| order by hits desc

A WAF hit is not automatically a false positive. A wave of suspicious requests on /wp-admin may be healthy to block. A SQLi rule on an internal import endpoint may instead reveal a legitimate payload that is poorly encoded, or an API that accepts risky fields too freely. Qualify by use case, not by volume only.

Build a decision matrix per path

Before the switch, every critical path needs a decision. The decision may be to do nothing, fix the application, add a targeted exclusion, strengthen a custom rule, or postpone Prevention if evidence is insufficient.

text waf-path-decision-matrix.txt
Path: /login
WAF signal: brute force, automation, atypical headers
Business impact: user authentication
Decision: Prevention possible if application errors and support stay normal
Validation: expected 403 rate on suspicious sources, not on legitimate cohorts
Rollback: return policy to Detection if legitimate users are blocked

Path: /api/import
WAF signal: ruleId on JSON or multipart content
Business impact: partner integration or critical batch
Decision: replay representative payloads before Prevention
Validation: WAF correlation plus application logs plus partner response
Rollback: Detection or targeted exclusion with expiration

Path: /webhook/provider
WAF signal: unusual user agent or signature
Business impact: asynchronous inbound events
Decision: verify source IPs, signatures and provider retries
Validation: no backlog, no event loss
Rollback: Detection if backlog or retries rise after switch

This matrix makes the change window shorter. The team does not debate WAF severity in the abstract. It decides path by path with a signal and a return path.

Prepare probes and correlation

On the switch day, application probes and KQL queries should already be ready. A probe that only checks the home page is not enough if the risks sit on login, import, webhook or partner API paths.

text waf-rollout-probes.txt
Before switch
Replay critical journeys from a network close to users
Verify representative payloads for sensitive endpoints
Add a correlation ID when the application supports it
Confirm that Application Gateway, WAF and application logs arrive

During switch
Watch 403, 502, latency and application errors
Compare affected paths with expected paths
Read WAF ruleId before adding an exception
Keep a support channel open for first reports

After switch
Capture before and after on the same time window
Identify remaining false positives
Remove useless temporary exceptions
Document the final decision

Correlation matters more than the raw number of 403s. More 403s on automated sources can be the expected result. A few 403s on a critical internal client can justify immediate rollback.

Watch Prevention before tickets arrive

After activation, the monitoring query should separate expected blocks from new blocks. It should also show whether one hostname or path suddenly concentrates the risk.

kusto 02-waf-prevention-watch.kql
let ChangeTime = datetime(2026-06-20T08:00:00Z);
let Window = 2h;
AzureDiagnostics
| where TimeGenerated between (ChangeTime .. ChangeTime + Window)
| where Category == "ApplicationGatewayFirewallLog"
| extend hostname = tostring(host_s)
| extend uri = tostring(requestUri_s)
| extend method = tostring(requestMethod_s)
| extend action = tostring(action_s)
| extend ruleId = tostring(ruleId_s)
| extend clientIp = tostring(clientIp_s)
| where action has_any ("Blocked", "Prevention")
| summarize blocked=count(),
          clients=dcount(clientIp),
          firstSeen=min(TimeGenerated),
          lastSeen=max(TimeGenerated),
          sampleClients=make_set(clientIp, 5)
by hostname, uri, method, ruleId
| order by blocked desc

Read this query with application logs. If WAF blocks a request, the application may never see it. Missing application logs are not proof that everything is fine. Conversely, rising application errors without WAF blocks likely point to another deployment problem.

Bound exceptions instead of opening broadly

If a false positive appears after the switch, the fastest response is not always the best one. Returning to Detection may be better than adding a broad exclusion that stays in place. An exclusion should target a ruleId, field, path and duration.

text waf-exception-control.txt
Before exception
RuleId identified
Path and hostname limited
Matched field understood: cookie, header, arg, body, multipart
Legitimate request sample retained
Bypass risk reviewed
Expiration or review planned

Prefer Detection rollback when
The false positive affects a broad critical path
The matched field is not understood
Several different ruleIds appear at once
The team cannot prove legitimate traffic
The required exception would be too global

Prefer targeted exclusion when
The legitimate payload is stable
The ruleId is unique or very limited
The path is constrained
The before and after test is reproducible
Security review accepts the compromise

The discipline is simple: if the team cannot explain exactly what is excluded, it is safer to roll back the mode than keep an opaque exception.

Decide rollout, hold or rollback

The change should end with an explicit decision. The worst state is a policy in Prevention with temporary exceptions, open support tickets and no planned review.

text waf-rollout-decision.txt
Decision: rollout confirmed
Critical probes pass
Blocks match expected paths and sources
No legitimate critical client is blocked
Remaining false positives have a fix or targeted exclusion
Logs can reconstruct the decision

Decision: hold under watch
Prevention stays active but a review is scheduled
Blocks are acceptable but not yet stable
Temporary exceptions have a removal date
Support knows which symptoms to escalate

Decision: rollback to Detection
Critical path blocked without a targeted fix
Backlog, business errors or partner calls fail
RuleId or matched field is not understood
Required exclusion would be too broad
Visibility is lost or logs are insufficient

Rollback should be clean: return the policy to Detection, keep logs from the window, annotate affected paths, then add missing payloads or scenarios to the matrix before trying again.

Conclusion

Enabling Azure WAF Prevention is not a checkbox. It is a production change that should start from Detection logs, go through a critical-path matrix, rely on realistic probes and keep immediate rollback available.

The right decision is not always to activate at any cost. It may be to confirm Prevention, hold under watch with targeted exceptions, or return to Detection when evidence is missing. What matters is that blocked traffic is explainable, legitimate traffic remains verified and every exception has a reason, a scope and an exit.