0
votes

I have the following snippet inside of a resource block,

...
condition {
    dynamic "http_header" {
      for_each = var.http_headers
      content {
        http_header_name  = http_header.value.header_name[0]
        values            = [http_header.value.values[0]]
      }
    }
}
...

Variable is currently set to:

variable "http_headers" {
  type = map
  default = {}
}

I'd like to provide a single map and have Terraform set the values for them inside the content block, e.g.

http_headers =  {
    header_name = "foo"
    values      = "bar"
}

I thought this would work based on reading I've done so far, but I am getting errors similar to the following.

Error: Unsupported attribute

  on .terraform/main.tf line 138, in resource "aws_alb_listener_rule" "listener_rule":
 138:         values            = [http_header.value.values[0]]
    |----------------
    | http_header.value is "foo"

This value does not have any attributes.

FWIW I have been able to get this working if I wrap the http_headers variable in a list and iterate over that instead but I would like to simplify the configuration to only use a single map. Is this possible?

1

1 Answers

1
votes

Looks like you have a mix of map and list, if you want to loop over something it should be a list...

I prefer lists with clearly defined objects, that way the input is validated:

  type = list(object({
    num    = number
    protoc = string
    values = string
  }))

But you could also go with the simpler

  type = list(any)

Here is an example:

variable "http_headers" {
  type = list(object({
    num    = number
    values = string
  }))

  default = []
}

data "aws_vpc" "default" {
  default = true
}

resource "aws_network_acl" "network_acl" {
  vpc_id = data.aws_vpc.default.id

  dynamic "ingress" {
    for_each = var.http_headers
    content {
      rule_no    = ingress.value.num
      protocol   = ingress.value.protoc
      action     = "allow"
      cidr_block = ingress.value.values
      from_port  = 22
      to_port    = 22
    }
  }
}

Then we have the input in a json file (input.tfvars.json):

[
  {
    "num": 1,
    "protoc": "tcp",
    "values": "10.0.0.1/32"
  },
  {
    "num": 2,
    "protoc": "tcp",
    "values": "10.0.0.2/32"
  }
]

Last we can run a plan:
terraform plan -var-file="input.tfvars.json"


Terraform will perform the following actions:

  # aws_network_acl.network_acl will be created
  + resource "aws_network_acl" "network_acl" {
      + arn        = (known after apply)
      + egress     = (known after apply)
      + id         = (known after apply)
      + ingress    = [
          + {
              + action          = "allow"
              + cidr_block      = "10.0.0.1/32"
              + from_port       = 22
              ...
              + rule_no         = 1
              + to_port         = 22
            },
          + {
              + action          = "allow"
              + cidr_block      = "10.0.0.2/32"
              + from_port       = 22
              ...
              + rule_no         = 2
              + to_port         = 22
            },
        ]
      + owner_id   = (known after apply)
      + subnet_ids = (known after apply)
      + vpc_id     = "vpc-f99999999"
    }

Plan: 1 to add, 0 to change, 0 to destroy.