Here is my situation, I am trying to deploy a legacy Azure Frontdoor instance to the new version of AFD and do it via Terraform.
Here is an excerpt from my locals block for the Firewall policy:
firewall_policy = {
policy = {
name = "policy"
sku_name = "AzureSKU"
mode = "prevention"
managed_rules_list = [
{
type = "Microsoft_DefaultRuleSet"
version = "1.1"
action = "Block"
exclusion = [
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string1"
},
{
match_variable = "RequestBodyPostArgNames"
operator = "StartsWith"
selector = "string2"
},
{
match_variable = "RequestCookieNames"
operator = "EqualsAny"
selector = "string3"
},
{
match_variable = "RequestBodyPostArgNames"
operator = "Contains"
selector = "string4"
}
]
override = [
{
rule_group_name = "RFI"
rule = [
{
rule_id = "931130"
action = "Block"
exclusion = [
{
match_variable = "RequestBodyPostArgNames"
operator = "Equals"
selector = "string1"
},
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string2"
},
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string3"
},
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string4"
}
]
}
]
},
{
rule_group_name = "PHP"
rule = [
{
rule_id = "933100"
enabled = false
action = "Block"
},
{
rule_id = "933110"
enabled = false
action = "Block"
},
{
rule_id = "933120"
enabled = false
action = "Block"
}
]
}
]
}
]
}
}
As you can see, there are lots of nested objects.
In my main.tf file, I call the following:
module "Azure_FW_Policy_module" {
for_each = local.firewall_policy
source = "./frontdoorFirewallPolicy"
cdn_frontdoor_firewall_policy_name = each.value.name
resource_group_name = var.resource_group_name
sku_name = each.value.sku_name
mode = each.value.mode
managed_rules_list = each.value.managed_rules_list
managed_rules_exclusion_list = each.value.managed_rules_list.exclusion
managed_rules_overide_list = each.value.managed_rules_list.override
managed_rules_overide_rule_list = each.value.managed_rules_list.override.rule
managed_rules_overide_rule_exclusion_list = each.value.managed_rules_list.override.rule.exclusion
managed_rules_overide_rule_group_exclusion_list = each.value.managed_rules_list.override.exclusion
custom_rules_list = each.value.custom_rules_list
tags = var.tags
}
The Module itself has a bunch of dynamic blocks in it, to accomodate the various configurations and looks like this:
resource "azurerm_cdn_frontdoor_firewall_policy" "cdn_frontdoor_firewall_policy" {
name = var.cdn_frontdoor_firewall_policy_name
resource_group_name = var.resource_group_name
sku_name = var.sku_name
enabled = var.enabled
mode = var.mode
custom_block_response_status_code = var.custom_block_response_status_code
custom_block_response_body = var.custom_block_response_body
request_body_check_enabled = var.request_body_check_enabled
tags = var.tags
dynamic "managed_rule" {
for_each = toset(var.managed_rules_list)
content {
type = managed_rule.value["type"]
version = managed_rule.value["version"]
action = managed_rule.value["action"]
dynamic "exclusion" {
for_each = toset(var.managed_rules_exclusion_list)
content {
match_variable = exclusion.value["match_variable"]
operator = exclusion.value["operator"]
selector = exclusion.value["selector"]
}
}
dynamic "override" {
for_each = toset(var.managed_rules_overide_list)
content {
rule_group_name = override.value["rule_group_name"]
dynamic "rule" {
for_each = toset(var.managed_rules_overide_rule_list)
content {
rule_id = rule.value["rule_id"]
action = rule.value["action"]
dynamic "exclusion" {
for_each = toset(var.managed_rules_overide_rule_exclusion_list)
content {
match_variable = exclusion.value["match_variable"]
operator = exclusion.value["operator"]
selector = exclusion.value["selector"]
}
}
}
}
dynamic "exclusion" {
for_each = toset(var.managed_rules_overide_rule_group_exclusion_list)
content {
match_variable = exclusion.value["match_variable"]
operator = exclusion.value["operator"]
selector = exclusion.value["selector"]
}
}
}
}
}
}
dynamic "custom_rule" {
for_each = toset(var.custom_rules_list)
content {
name = custom_rule.value["name"]
enabled = custom_rule.value["enabled"]
priority = custom_rule.value["priority"]
type = custom_rule.value["type"]
action = custom_rule.value["action"]
rate_limit_duration_in_minutes = custom_rule.value["rate_limit_duration_in_minutes"]
rate_limit_threshold = custom_rule.value["rate_limit_threshold"]
match_condition {
match_variable = custom_rule.value["match_variable"]
operator = custom_rule.value["operator"]
negation_condition = custom_rule.value["negation_condition"]
match_values = custom_rule.value["match_values"]
}
}
}
}
My issue is that when I run this - I get the following error:
"each.value.managed_rules_list is tuple with 1 element This value does not have any attributes."
Based on my reading, I believe that this is because I need to use the flatten command in Terraform on the firewall_policy element in order to pass this to the module:
However - despite reading the above, I am not sure exactly how I need to call flatten to account for all the nested objects and then how I can pass them to my module, as I have not used this before.
Terraform using flatten with nested objects while provisioning forntdoor firewall policy.
In your main.tf
, you're trying to fetch the attributes like each.value.managed_rules_list.exclusion
, which assumes that managed_rules_list
is a map. However, since managed_rules_list
is defined as a list Terraform treats it as a tuple, and direct attribute access without specifying an index isn't valid.
To resolve this, you should ensure that you're fetching the elements of your lists correctly. This can be done using Terraform's flatten
function. Which can help manage deeply nested structures by converting nested lists into a single flat list, making them easier to iterate over.
Demo configuration:
main.tf:
locals {
firewall_policy = {
name = "afdwafvkpolicy"
sku_name = "Premium_AzureFrontDoor"
mode = "Prevention"
managed_rules = [
{
type = "Microsoft_DefaultRuleSet"
version = "1.1"
action = "Block"
exclusions = [
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string1"
},
{
match_variable = "RequestBodyPostArgNames"
operator = "StartsWith"
selector = "string2"
}
]
overrides = [
{
rule_group_name = "RFI"
rules = [
{
rule_id = "931130"
action = "Block"
exclusions = [
{
match_variable = "QueryStringArgNames"
operator = "Contains"
selector = "string3"
}
]
}
]
},
{
rule_group_name = "PHP"
rules = [
{
rule_id = "933100"
action = "Block"
enabled = false
},
{
rule_id = "933110"
action = "Block"
enabled = false
}
]
}
]
}
]
custom_rules = []
}
}
module "afd_waf" {
source = "./frontdoorFirewallPolicy"
firewall_policy_config = local.firewall_policy
resource_group_name = var.resource_group_name
}
frontdoorFirewallPolicy/main.tf
resource "azurerm_cdn_frontdoor_firewall_policy" "waf" {
name = var.firewall_policy_config.name
resource_group_name = var.resource_group_name
sku_name = var.firewall_policy_config.sku_name
mode = var.firewall_policy_config.mode
dynamic "managed_rule" {
for_each = var.firewall_policy_config.managed_rules
content {
type = managed_rule.value.type
version = managed_rule.value.version
action = managed_rule.value.action
dynamic "exclusion" {
for_each = lookup(managed_rule.value, "exclusions", [])
content {
match_variable = exclusion.value.match_variable
operator = exclusion.value.operator
selector = exclusion.value.selector
}
}
dynamic "override" {
for_each = lookup(managed_rule.value, "overrides", [])
content {
rule_group_name = override.value.rule_group_name
dynamic "rule" {
for_each = lookup(override.value, "rules", [])
content {
rule_id = rule.value.rule_id
action = rule.value.action
enabled = lookup(rule.value, "enabled", true)
dynamic "exclusion" {
for_each = lookup(rule.value, "exclusions", [])
content {
match_variable = exclusion.value.match_variable
operator = exclusion.value.operator
selector = exclusion.value.selector
}
}
}
}
}
}
}
}
}
Deployment:
Refer:
https://learn.microsoft.com/en-us/azure/frontdoor/create-front-door-terraform
https://library.tf/modules/T-Systems-MMS/cdn/azurerm/latest
https://developer.hashicorp.com/terraform/language/functions/flatten