'How to access variables in a list / object in Terraform?

In Terraform, how can access the values from the variable below?

variable "egress_rules" {
  type = list(object({
    from_port = number
    to_port = number
    protocol = string
    cidr_blocks = list(string)
  }))
  default = [
    {
      from_port = 0
      to_port = 0
      protocol = "-1"
      cidr_blocks = ["0.0.0.0/0"]
    }
  ]
}

I tried:

resource "aws_security_group_rule" "egress" {
  security_group_id = aws_security_group.new.id
  type = "ingress"
  for_each = var.egress_rules
    from_port   = each.value.from_port
    to_port     = each.value.from_port
    protocol    = each.value.protocol
    cidr_blocks = each.value.cidr_blocks
}

But got this error:

Error: Invalid for_each argument

What is the correct way to reference this variable?



Solution 1:[1]

for_each will not work with a list of maps. You have to convert it to a map. This is commonly done through a for expression:

resource "aws_security_group_rule" "egress" {
  security_group_id = aws_security_group.new.id
  type = "ingress"
  for_each = { for idx, rule in var.egress_rules: idx => rule }
    from_port   = each.value.from_port
    to_port     = each.value.from_port
    protocol    = each.value.protocol
    cidr_blocks = each.value.cidr_blocks
}

Solution 2:[2]

to set multiple properties of a resource you could use a map of objects like this:

variable "egress_rules" {
  type = map(object({
    from_port = number
    to_port = number
    protocol = string
    cidr_blocks = list(string)
  }))
}

Your variable definition would be:

egress_rules = {
    {
      from_port = 0
      to_port = 0
      protocol = "-1"
      cidr_blocks = ["0.0.0.0/0"]
    }
}

The updated resource definition would be:

resource "aws_security_group_rule" "egress" {
  security_group_id = aws_security_group.new.id
  type = "egress"
  for_each = var.egress_rules
    from_port   = each.value["from_port"]
    to_port     = each.value["to_port"]
    protocol    = each.valuep["protocol"]
    cidr_blocks = each.value["cidr_blocks"]
}

Solution 3:[3]

Adding to above answer from @Marcin

If you want to use single object instead of list then you can directly access without for_each

Instead of declaring it as list of objects; use single object as shown below

variable "egress_rules" {
  type = object({
    from_port = number
    to_port = number
    protocol = string
    cidr_blocks = list(string)
  })
  default = ({
      from_port = 0
      to_port = 0
      protocol = "-1"
      cidr_blocks = ["0.0.0.0/0"]
    }}
}

Then access in your resource like

resource "aws_security_group_rule" "egress" {
  security_group_id = aws_security_group.new.id
  from_port   = var.egress_rules.from_port
  to_port     = var.egress_rules.from_port
  protocol    = var.egress_rules.protocol
  cidr_blocks = var.egress_rules.cidr_blocks
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Marcin
Solution 2
Solution 3