I'm trying to create custom WAF rules to allow traffic to specific URI's - this deployment is being done via Terraform.
The error I'm getting is:
Application Gateway Web Application Firewall Policy Name: "waf-policy-prod-uksouth"): unexpected status 400 (400 Bad Request) with error: ApplicationGatewayFirewallMatchValueNoCollection: Custom Rule 'AllowAmexPay' does not have a valid collection match variable 'RequestUri' which support selector in its condition in context 'properties.customRules[2].matchConditions[0].matchVariables[0]'.
│
│ with azurerm_web_application_firewall_policy.waf_policy,
│ on application_gw.tf line 224, in resource "azurerm_web_application_firewall_policy" "waf_policy":
│ 224: resource "azurerm_web_application_firewall_policy" "waf_policy" {
TFVars file:
custom_rules = [
{
name = "RecitePreferences"
priority = "70"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestCookies"
selector = "Recite.Preferences"
operator = "Any"
action = "Allow"
},
{
name = "CookieConsent"
priority = "71"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestCookies"
selector = "CookieConsent"
operator = "Any"
action = "Allow"
},
{
name = "AllowAmexPay"
priority = "80"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestUri"
selector = "/smart-card/amex-pay"
operator = "Contains"
action = "Allow"
},
{
name = "AllowAmexPayComplete"
priority = "81"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestUri"
selector = "/smart-card/amex-pay-complete"
operator = "Contains"
action = "Allow"
}
]
Variables.tf file:
variable "custom_rules" {
type = list(object({
name = string
priority = string
enabled = bool
rule_type = string
variable_name = string
operator = string
selector = string
action = string
}))
}
Application Gateway config:
resource "azurerm_web_application_firewall_policy" "waf_policy" {
name = "waf-policy-${var.general_environment}-${var.general_location}"
resource_group_name = azurerm_resource_group.prod.name
location = azurerm_resource_group.prod.location
policy_settings {
enabled = true
mode = "Detection"
request_body_check = false
file_upload_limit_in_mb = 100
max_request_body_size_in_kb = 128
}
managed_rules {
managed_rule_set {
type = "OWASP"
version = "3.2"
dynamic "rule_group_override" {
for_each = var.rule_group_override
content {
rule_group_name = rule_group_override.key
dynamic "rule" {
for_each = rule_group_override.value
content {
id = rule.value.id
enabled = rule.value.enabled
}
}
}
}
}
dynamic "exclusion" {
for_each = var.exclusion
content {
match_variable = exclusion.value["match_variable"]
selector = exclusion.value["selector"]
selector_match_operator = exclusion.value["selector_match_operator"]
}
}
managed_rule_set {
type = "Microsoft_BotManagerRuleSet"
version = "1.0"
}
}
dynamic "custom_rules" {
for_each = var.custom_rules
content {
name = custom_rules.value["name"]
enabled = custom_rules.value["enabled"]
priority = custom_rules.value["priority"]
rule_type = custom_rules.value["rule_type"]
action = custom_rules.value["action"]
match_conditions {
operator = custom_rules.value["operator"]
match_variables {
variable_name = custom_rules.value["variable_name"]
selector = custom_rules.value["selector"]
}
}
}
}
}
Just wondering if there's a different/better way of passing in he variables as these are currently erroring on Apply?
Azure WAF custom rules deployement using Terraform
The issue seems to with the way you define selector. It was wrongly used for RequestUri
. The structure which you mentioned forced everything to use only selector
.
The RequestUri always define using match_values
this is why the configuration you tried was facing a blocker. The structural configuration doesn't match the requirement.
I tried a configuration with all the necessary changes, and I was able to provision the requirement successfully.
Main.tf:
resource "azurerm_web_application_firewall_policy" "example" {
name = "waf-policy-uksouth"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
policy_settings {
enabled = true
mode = "Detection"
}
managed_rules {
managed_rule_set {
type = "OWASP"
version = "3.2"
}
}
dynamic "custom_rules" {
for_each = var.custom_rules
content {
name = custom_rules.value.name
priority = tonumber(custom_rules.value.priority)
enabled = custom_rules.value.enabled
rule_type = custom_rules.value.rule_type
action = custom_rules.value.action
match_conditions {
operator = custom_rules.value.operator
match_values = custom_rules.value.match_values
match_variables {
variable_name = custom_rules.value.variable_name
selector = lookup(custom_rules.value, "selector", null)
}
}
}
}
}
tfvars:
custom_rules = [
{
name = "AllowAmexPay"
priority = "80"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestUri"
operator = "Contains"
match_values = ["/smart-card/amex-pay"]
action = "Allow"
},
{
name = "AllowAmexPayComplete"
priority = "81"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestUri"
operator = "Contains"
match_values = ["/smart-card/amex-pay-complete"]
action = "Allow"
},
{
name = "RecitePreferences"
priority = "70"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestCookies"
operator = "Equal"
selector = "Recite.Preferences"
match_values = ["Enabled"]
action = "Allow"
},
{
name = "CookieConsent"
priority = "71"
enabled = true
rule_type = "MatchRule"
variable_name = "RequestCookies"
operator = "Equal"
selector = "CookieConsent"
match_values = ["Accepted"]
action = "Allow"
}
]
variable.tf:
variable "custom_rules" {
type = list(object({
name = string
priority = string
enabled = bool
rule_type = string
variable_name = string
operator = string
match_values = list(string) <-- Add this line
selector = optional(string)
action = string
}))
}
Deployment:
Refer: