0

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:

Like here

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.

1
  • Rewrite the local.firewall_policy to directly pass structured nested objects as a whole to the module, and within the module use nested dynamic blocks to iterate avoiding .exclusion or .override outside the module. Flattening is unnecessary if structured properly @TheDemonLord
    – Vinay B
    CommentedApr 8 at 7:51

1 Answer 1

1

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:

enter image description here

enter image description here

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

2
  • 1
    You are an absolute legend - thanks for that - worked like a charm :)CommentedApr 8 at 21:59
  • Glad to know it works for you @TheDemonLord
    – Vinay B
    CommentedApr 9 at 3:23

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.