Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I iterate through a map variable in terraform

Im trying to iterate through a variable type map and i'm not sure how to

This is what i have so far

In my main.tf:

resource "aws_route_53_record" "proxy_dns" {
  count = "${length(var.account_name)}"
  zone_id = "${infrastructure.zone_id}"
  name = "proxy-${element(split(",", var.account_name), count.index)}-dns
  type = CNAME
  ttl = 60
  records = ["{records.dns_name}"]
}

And in my variables.tf

variable "account_name" {
  type = "map"
  default = {
    "account1" = "accountA"
    "account2" = "accountB"
  }
}

I want to be able to create multiple resources with the different account names

like image 488
CaptainRaasClaat Avatar asked Aug 14 '19 23:08

CaptainRaasClaat


People also ask

How do you make a loop in Terraform?

Using the count meta-argument The count meta-argument is the simplest of the looping constructs within Terraform. By either directly assigning a whole number or using the length function on a list or map variable, Terraform creates this number of resources based on the resource block it is assigned to.

What is MAP Variable Terraform?

terraform-map-variable.tfA variable can have a map type assigned explicitly, or it can be implicitly declared as a map by specifying a default value that is a map. The above demonstrates both. Then, replace the aws_instance with the following: resource "aws_instance" "example" { ami = var.

Can We use if condition in Terraform?

This conditional expression can be used to programmatically assign resource parameters and variables based on an expression being evaluated. However, this feature is also very useful for other scenarios as well.


2 Answers

If you are using Terraform 0.12.6 or later then you can use for_each instead of count to produce one instance for each element in your map:

resource "aws_route53_record" "proxy_dns" {
  for_each = var.account_name

  zone_id = infrastructure.zone_id
  name    = "proxy-${each.value}-dns"
  # ... etc ...
}

The primary advantage of for_each over count is that Terraform will identify the instances by the key in the map, so you'll get instances like aws_route53_record.proxy_dns["account1"] instead of aws_route53_record.proxy_dns[0], and so you can add and remove elements from your map in future with Terraform knowing which specific instance belongs to each element.

each.key and each.value in the resource type arguments replace count.index when for_each is used. They evaluate to the key and value of the current map element, respectively.

like image 183
Martin Atkins Avatar answered Oct 09 '22 22:10

Martin Atkins


You can use a combination of map, keys function,index function, and count. This terraform creates 3 acls with various rules.

  • The names of the acl's are determined by the keys.
  • The number of acl's is determined by the count of the keys.
  • The index of each rule (priority) is determined by the index function
  • The name of each rule is from the CONTAINS_WORD or CONTAINS property in the map

=>

variable "acls" {
  type = map(any)
  default = {

    "acl1" = {
      "CONTAINS_WORD" = ["api","aaa", "bbb", "ccc"]
      "CONTAINS" = ["xxx","yyy"]
    }

    "acl2" = {
      "CONTAINS_WORD" = [ "url1,"url2","url3"]
      "CONTAINS" = ["url4"]
    }

    "acl3" = {
      "CONTAINS_WORD" = ["xxx"]
      "CONTAINS" = []
    } 
  }
}

resource "aws_wafv2_web_acl" "acl" {
  name  = keys(var.acls)[count.index]
  scope = "REGIONAL"
  count = length(keys(var.acls))

  default_action {
    block {}
  }

  dynamic "rule" {

    for_each = toset(var.acls[keys(var.acls)[count.index]].CONTAINS_WORD)

    content {
      name     =  rule.key
      priority = index(var.acls[keys(var.acls)[count.index]].CONTAINS_WORD, rule.key)

      action {
        allow {}
      }

      statement {
        
        #https://docs.aws.amazon.com/waf/latest/APIReference/API_ByteMatchStatement.html
        byte_match_statement  {

          positional_constraint = "CONTAINS_WORD"
          search_string         = lower(rule.key)

          field_to_match {
            uri_path {}
          }

          text_transformation {
            priority = 0
            type     = "LOWERCASE"
          }
        }
      } 

      visibility_config {
        cloudwatch_metrics_enabled = true
        metric_name                = "waf-${keys(var.acls)[count.index]}-${rule.key}"
        sampled_requests_enabled   = true
      }
    }
  }

  dynamic "rule" {

    for_each = toset(var.acls[keys(var.acls)[count.index]].CONTAINS)

    content {
      name     = replace(rule.key, ".", "_")
      priority = index(var.acls[keys(var.acls)[count.index]].CONTAINS, rule.key) + length(var.acls[keys(var.acls)[count.index]].CONTAINS_WORD)

      action {
        allow {}
      }

      statement {
        
        #https://docs.aws.amazon.com/waf/latest/APIReference/API_ByteMatchStatement.html
        byte_match_statement  {

          positional_constraint = "CONTAINS"
          search_string         = lower(rule.key)

          field_to_match {
            uri_path {}
          }

          text_transformation {
            priority = 0
            type     = "LOWERCASE"
          }
        }
      } 

      visibility_config {
        cloudwatch_metrics_enabled = true
        metric_name                = "waf-${keys(var.acls)[count.index]}-${replace(rule.key, ".", "_")}"
        sampled_requests_enabled   = true
      }
    }
  }


  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "waf-${keys(var.acls)[count.index]}"
    sampled_requests_enabled   = true
  }
}
like image 1
jlo-gmail Avatar answered Oct 09 '22 22:10

jlo-gmail