Skip to content

Commit fc0c110

Browse files
authored
Merge pull request #304728 from halkazwini/waf-crs
CRS DRS upgrade
2 parents 827aed7 + eded03b commit fc0c110

File tree

2 files changed

+311
-0
lines changed

2 files changed

+311
-0
lines changed
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
---
2+
title: Upgrade CRS or DRS Ruleset Version
3+
titleSuffix: Azure Web Application Firewall
4+
description: Learn how to upgrade CRS or DRS ruleset version on Application Gateway Web Application Firewall.
5+
author: halkazwini
6+
ms.author: halkazwini
7+
ms.service: azure-web-application-firewall
8+
ms.topic: how-to
9+
ms.date: 08/28/2025
10+
---
11+
12+
# Upgrade CRS or DRS ruleset version
13+
14+
The Azure-managed **Default Rule Set (DRS)** in Azure Application Gateway Web Application Firewall (WAF) protects web applications against common vulnerabilities and exploits, including the OWASP top 10 attack types. The default rule set also incorporates the Microsoft Threat Intelligence Collection rules. We recommend always running the **latest ruleset version**, which includes the most recent security updates, rule enhancements, and fixes.
15+
16+
The Azure-managed Default Rule Set (DRS) is the latest generation of rulesets in Azure WAF, replacing all previous Core Rule Set (CRS) versions. Among DRS releases, always use the highest available version (for example, DRS 2.2 when released) to ensure you have the most up-to-date protections.
17+
18+
This article provides PowerShell examples for upgrading your Azure WAF policy to DRS 2.1. While the examples reference DRS 2.1 specifically, you should always upgrade to the latest available DRS version to ensure maximum protection.
19+
20+
> [!NOTE]
21+
> PowerShell snippets are examples only. Replace all placeholders with values from your environment.
22+
23+
## Prerequisites
24+
25+
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F).
26+
27+
- An existing Azure WAF policy with a Core Rule Set (CRS) or Default Rule Set (DRS) applied. If you don't have a WAF policy yet, see [Create Web Application Firewall policies for Application Gateway](create-waf-policy-ag.md).
28+
29+
- Latest version of [Azure PowerShell installed locally](/powershell/azure/install-azure-powershell). This article requires the [Az.Network Module](/powershell/module/az.network).
30+
31+
## Key considerations when upgrading
32+
33+
When upgrading your Azure WAF ruleset version, make sure to:
34+
35+
- **Preserve existing customizations**: carry over your rule action overrides, rule status (enabled/disabled) overrides, and exclusions.
36+
37+
- **Validate new rules safely**: ensure newly added rules are initially set to **log mode**, so you can monitor their impact and fine-tune them before enabling blocking.
38+
39+
## Prepare your environment and variables
40+
41+
1. Set context of your selected subscription, resource group, and Azure WAF policy.
42+
43+
```powershell
44+
Import-Module Az.Network
45+
Set-AzContext -SubscriptionId "<subscription_id>"
46+
$resourceGroupName = "<resource_group>"
47+
$wafPolicyName = "<policy_name>"
48+
```
49+
1. Get the WAF policy object and retrieve its definitions.
50+
51+
```powershell
52+
$wafPolicy = Get-AzApplicationGatewayFirewallPolicy `
53+
-Name $wafPolicyName `
54+
-ResourceGroupName $resourceGroupName
55+
$currentExclusions = $wafPolicy.ManagedRules.Exclusions
56+
$currentManagedRuleset = $wafPolicy.ManagedRules.ManagedRuleSets
57+
| Where-Object { $_.RuleSetType -eq "OWASP" }
58+
$currentVersion = $currentManagedRuleset.RuleSetVersion
59+
```
60+
61+
## Preserve existing customizations
62+
63+
1. Don't copy overrides or exclusions that apply to rules removed in **DRS 2.1**. The following function checks if a rule has been removed:
64+
65+
```powershell
66+
function Test-RuleIsRemovedFromDRS21 {
67+
param (
68+
[string]$RuleId,
69+
[string]$CurrentRulesetVersion
70+
)
71+
$removedRulesByCrsVersion = @{
72+
"3.0" = @( "200004", "913100", "913101", "913102", "913110", "913120", "920130", "920140", "920250", "921100", "800100", "800110", "800111", "800112", "800113" )
73+
"3.1" = @( "200004", "913100", "913101", "913102", "913110", "913120", "920130", "920140", "920250", "800100", "800110", "800111", "800112", "800113", "800114" )
74+
"3.2" = @( "200004", "913100", "913101", "913102", "913110", "913120", "920250", "800100", "800110", "800111", "800112", "800113", "800114" )
75+
}
76+
# If the version isn't known, assume rule has not been removed
77+
if (-not $removedRulesByCrsVersion.ContainsKey($CurrentRulesetVersion)) {
78+
return $false
79+
}
80+
return $removedRulesByCrsVersion[$CurrentRulesetVersion] -contains $RuleId }
81+
```
82+
83+
1. When creating new override objects, use the **DRS 2.1 group names**. The following function maps legacy CRS group names to DRS 2.1
84+
groups:
85+
86+
```powershell
87+
function Get-DrsRuleGroupName {
88+
param (
89+
[Parameter(Mandatory = $true)]
90+
[string]$SourceGroupName )
91+
$groupMap = @{
92+
"REQUEST-930-APPLICATION-ATTACK-LFI" = "LFI"
93+
"REQUEST-931-APPLICATION-ATTACK-RFI" = "RFI"
94+
"REQUEST-932-APPLICATION-ATTACK-RCE" = "RCE"
95+
"REQUEST-933-APPLICATION-ATTACK-PHP" = "PHP"
96+
"REQUEST-941-APPLICATION-ATTACK-XSS" = "XSS"
97+
"REQUEST-942-APPLICATION-ATTACK-SQLI" = "SQLI"
98+
"REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION" = "FIX"
99+
"REQUEST-944-APPLICATION-ATTACK-JAVA" = "JAVA"
100+
"REQUEST-921-PROTOCOL-ATTACK" = "PROTOCOL-ATTACK"
101+
"REQUEST-911-METHOD-ENFORCEMENT" = "METHOD-ENFORCEMENT"
102+
"REQUEST-920-PROTOCOL-ENFORCEMENT" = "PROTOCOL-ENFORCEMENT"
103+
"REQUEST-913-SCANNER-DETECTION" = $null # No direct mapping
104+
"Known-CVEs" = "MS-ThreatIntel-CVEs"
105+
"General" = "General"
106+
}
107+
if ($groupMap.ContainsKey($SourceGroupName)) {
108+
return $groupMap[$SourceGroupName]
109+
} else {
110+
return $SourceGroupName # No known mapping
111+
}
112+
}
113+
```
114+
115+
1. Use the following PowerShell code to define the rules’ overrides, duplicating overrides from existing ruleset version:
116+
117+
```powershell
118+
$groupOverrides = @()
119+
foreach ($group in $currentManagedRuleset.RuleGroupOverrides) {
120+
$mappedGroupName = Get-DrsRuleGroupName $group.RuleGroupName
121+
foreach ($existingRule in $group.Rules) {
122+
if (-not (Test-RuleIsRemovedFromDRS21 $existingRule.RuleId $currentVersion))
123+
{
124+
`$existingGroup = $groupOverrides |
125+
Where-Object { $_.RuleGroupName -eq $mappedGroupName }
126+
if ($existingGroup) {
127+
if (-not ($existingGroup.Rules |
128+
Where-Object { $_.RuleId -eq $existingRule.RuleId })) {
129+
$existingGroup.Rules.Add($existingRule) } }
130+
else {
131+
$newGroup = New-AzApplicationGatewayFirewallPolicyManagedRuleGroupOverride ` -RuleGroupName $mappedGroupName ` -Rule @($existingRule) $groupOverrides += $newGroup } } } }
132+
133+
```
134+
135+
1. Use the following PowerShell code to duplicate your existing exclusions and apply them on DRS 2.1:
136+
137+
```powershell
138+
# Create new exclusion objects
139+
$newRuleSetExclusions = @()
140+
141+
if ($currentExclusions -ne $null -and $currentExclusions.Count -gt 0)
142+
{
143+
foreach ($exclusion in $currentExclusions) {
144+
$newExclusion = New-AzApplicationGatewayFirewallPolicyExclusion `
145+
-MatchVariable $exclusion.MatchVariable `
146+
-SelectorMatchOperator $exclusion.SelectorMatchOperator `
147+
-Selector $exclusion.Selector
148+
149+
# Migrate scopes: RuleSet, RuleGroup, or individual Rules
150+
if ($exclusion.ExclusionManagedRuleSets) {
151+
foreach ($scope in $exclusion.ExclusionManagedRuleSets) {
152+
# Create RuleGroup objects from existing RuleGroups
153+
$ruleGroups = @()
154+
foreach ($group in $scope.RuleGroups) {
155+
$drsGroupName = Get-DrsRuleGroupName $group.RuleGroupName
156+
if ($drsGroupName)
157+
{
158+
$exclusionRules = @()
159+
foreach ($rule in $group.Rules)
160+
{
161+
if (-not (Test-RuleIsRemovedFromDRS21 $rule.RuleId "3.2"))
162+
{
163+
$exclusionRules += New-AzApplicationGatewayFirewallPolicyExclusionManagedRule `
164+
-RuleId $rule.RuleId
165+
}
166+
}
167+
if ($exclusionRules -ne $null -and $exclusionRules.Count -gt 0)
168+
{
169+
$ruleGroups += New-AzApplicationGatewayFirewallPolicyExclusionManagedRuleGroup `
170+
-Name $drsGroupName `
171+
-Rule $exclusionRules
172+
} else {
173+
$ruleGroups += New-AzApplicationGatewayFirewallPolicyExclusionManagedRuleGroup `
174+
-Name $drsGroupName
175+
}
176+
}
177+
}
178+
179+
# Create the ManagedRuleSet scope object with the updated RuleGroups
180+
if ($ruleGroups.Count -gt 0) {
181+
$newRuleSetScope = New-AzApplicationGatewayFirewallPolicyExclusionManagedRuleSet `
182+
-Type "Microsoft_DefaultRuleSet" `
183+
-Version "2.1" `
184+
-RuleGroup $ruleGroups
185+
}
186+
187+
# Add to the new exclusion object
188+
$newExclusion.ExclusionManagedRuleSets += $newRuleSetScope
189+
}
190+
}
191+
192+
if (-not $newExclusion.ExclusionManagedRuleSets)
193+
{
194+
$newExclusion.ExclusionManagedRuleSets = @()
195+
}
196+
197+
$newRuleSetExclusions += $newExclusion
198+
}
199+
}
200+
```
201+
202+
## Validate new rules safely
203+
204+
When you upgrade, new DRS 2.1 rules are active by default. If your WAF is in ***Prevention*** mode, set new rules to ***log*** mode first. The *log* mode allows you to review logs before enabling blocking.
205+
206+
1. The following PowerShell definitions are for rules introduced in DRS 2.1 compared to each CRS version:
207+
208+
```powershell
209+
# Added in DRS 2.1 compared to CRS 3.0
210+
$rulesAddedInThisVersionByGroup = @{
211+
"General" = @("200002", "200003")
212+
"PROTOCOL-ENFORCEMENT" = @("920121", "920171", "920181", "920341", "920470", "920480", "920500")
213+
"PROTOCOL-ATTACK" = @("921190", "921200")
214+
"RCE" = @("932180")
215+
"PHP" = @("933200", "933210")
216+
"NODEJS" = @("934100")
217+
"XSS" = @("941101", "941360", "941370", "941380")
218+
"SQLI" = @("942361", "942470", "942480", "942500", "942510")
219+
"JAVA" = @("944100", "944110", "944120", "944130", "944200", "944210", "944240", "944250")
220+
"MS-ThreatIntel-WebShells" = @("99005002", "99005003", "99005004", "99005005", "99005006")
221+
"MS-ThreatIntel-AppSec" = @("99030001", "99030002")
222+
"MS-ThreatIntel-SQLI" = @("99031001", "99031002", "99031003", "99031004")
223+
"MS-ThreatIntel-CVEs" = @( "99001001","99001002","99001003","99001004","99001005","99001006", "99001007","99001008","99001009","99001010","99001011","99001012", "99001013","99001014","99001015","99001016","99001017" )
224+
}
225+
```
226+
227+
```powershell
228+
# Added in DRS 2.1 compared to CRS 3.1
229+
$rulesAddedInThisVersionByGroup = @{
230+
"General" = @("200002", "200003")
231+
"PROTOCOL-ENFORCEMENT" = @("920181", "920500")
232+
"PROTOCOL-ATTACK" = @("921190", "921200")
233+
"PHP" = @("933200", "933210")
234+
"NODEJS" = @("934100")
235+
"XSS" = @("941360", "941370", "941380")
236+
"SQLI" = @("942500", "942510")
237+
"MS-ThreatIntel-WebShells" = @("99005002", "99005003", "99005004", "99005005", "99005006")
238+
"MS-ThreatIntel-AppSec" = @("99030001", "99030002")
239+
"MS-ThreatIntel-SQLI" = @("99031001", "99031002", "99031003", "99031004") "MS-ThreatIntel-CVEs" = @( "99001001","99001002","99001003","99001004","99001005","99001006", "99001007","99001008","99001009","99001010","99001011","99001012", "99001013","99001014","99001015","99001016","99001017" )
240+
}
241+
```
242+
243+
```powershell
244+
# Added in DRS 2.1 compared to CRS 3.2
245+
$rulesAddedInThisVersionByGroup = @{
246+
"General" = @("200002", "200003")
247+
"PROTOCOL-ENFORCEMENT" = @("920181", "920500")
248+
"PROTOCOL-ATTACK" = @("921190", "921200")
249+
"PHP" = @("933200", "933210")
250+
"NODEJS" = @("934100")
251+
"XSS" = @("941360", "941370", "941380")
252+
"SQLI" = @("942100", "942500", "942510")
253+
"MS-ThreatIntel-WebShells" = @("99005002", "99005003", "99005004", "99005005", "99005006")
254+
"MS-ThreatIntel-AppSec" = @("99030001", "99030002")
255+
"MS-ThreatIntel-SQLI" = @("99031001", "99031002", "99031003", "99031004")
256+
"MS-ThreatIntel-CVEs" = @( "99001001","99001002","99001003","99001004","99001005","99001006", "99001007","99001008","99001009","99001010","99001011","99001012", "99001013","99001014","99001015","99001016","99001017" )
257+
}
258+
259+
```
260+
261+
1. Use the following PowerShell code to add new rule overrides to the existing `$groupOverrides` object defined previously:
262+
263+
```powershell
264+
foreach ($groupName in $rulesAddedInDRS21.Keys) {
265+
$ruleOverrides = @()
266+
foreach ($ruleId in $rulesAddedInDRS21[$groupName]) {
267+
$alreadyExists = $existingOverrides |
268+
Where-Object { $_.RuleId -eq $ruleId }
269+
if (-not $alreadyExists) {
270+
$ruleOverrides += New-AzApplicationGatewayFirewallPolicyManagedRuleOverride `
271+
-RuleId $ruleId `
272+
-Action "Log" `
273+
-State "Enabled"
274+
}
275+
} # Only create group override if we added rules to it
276+
if ($ruleOverrides.Count -gt 0) {
277+
$groupOverrides += New-AzApplicationGatewayFirewallPolicyManagedRuleGroupOverride `
278+
-RuleGroupName $groupName `
279+
-Rule $ruleOverrides }
280+
}
281+
```
282+
283+
## Apply customizations and upgrade
284+
285+
Define your updated Azure WAF policy object, incorporating the duplicated and updated rule overrides and exclusions:
286+
287+
```powershell
288+
$managedRuleSet = New-AzApplicationGatewayFirewallPolicyManagedRuleSet `
289+
-RuleSetType "Microsoft_DefaultRuleSet" `
290+
-RuleSetVersion "2.1" `
291+
-RuleGroupOverride $groupOverrides
292+
for ($i = 0; $i -lt $wafPolicy.ManagedRules.ManagedRuleSets.Count; $i++) {
293+
if ($wafPolicy.ManagedRules.ManagedRuleSets[$i].RuleSetType -eq "OWASP") {
294+
$wafPolicy.ManagedRules.ManagedRuleSets[$i] = $managedRuleSet
295+
break
296+
}
297+
}
298+
# Assign to policy
299+
if ($newRuleSetExclusions) {
300+
$wafPolicy.ManagedRules.Exclusions = $currentExclusions + $newRuleSetExclusions
301+
}
302+
# Apply the updated WAF policy
303+
Set-AzApplicationGatewayFirewallPolicy -InputObject $wafPolicy
304+
```
305+
306+
## Related content
307+
308+
- [Web Application Firewall DRS and CRS rule groups and rules](/azure/web-application-firewall/ag/application-gateway-crs-rulegroups-rules)
309+
- [Customize Web Application Firewall rules using the Azure portal](/azure/web-application-firewall/ag/application-gateway-customize-waf-rules-portal)

articles/web-application-firewall/toc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ items:
7676
href: ./ag/application-gateway-crs-rulegroups-rules.md
7777
- name: Exclusion lists
7878
href: ./ag/application-gateway-waf-configuration.md
79+
- name: Upgrade ruleset version
80+
href: ./ag/upgrade-ruleset-version.md
7981
- name: Customize managed rules
8082
items:
8183
- name: Azure portal

0 commit comments

Comments
 (0)