Issue with for_each loop - egress.value is object with 3 attributes

Hello everyone,

I’m trying to create security group and rules with for_each. Getting the below error while passing the list(string) cidr_blocks value. Any help is much appreciated

Code:

################################
# Create Security Group & Rules #
################################

resource "aws_security_group" "app_sg" {
  for_each = var.create_sg ? toset(var.sg_name) : []
  name     = each.key
  vpc_id   = var.vpc_id

  dynamic "egress" {
    for_each = var.egress_rules != {} ? toset(values(var.egress_rules)) : []
    content {
      from_port       = egress.value["from_port"]
      to_port         = egress.value["to_port"]
      protocol        = egress.value["protocol"]
      cidr_blocks     = egress.value["cidr_blocks"]
      security_groups = var.security_groups
      self            = var.egress_self_sg
    }
  }

  tags = merge(
    var.common-tags,
    {
      "Name" = "${each.key}-${lower(var.environment)}"
    }
  )
}

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

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

With the above code, I’m getting the below error

╷
│ Error: Unsupported attribute
│
│   on ../../modules/security/main.tf line 28, in resource "aws_security_group" "app_sg":
│   28:       cidr_blocks     = egress.value["cidr_blocks"]
│     ├────────────────
│     │ egress.value is object with 3 attributes
│
│ This object does not have an attribute named "cidr_blocks".
╵

Pass the var.egress_rules as is. No need to get the list of values or check if it’s empty. You’re also missing the iterator. By default, it is each, but since you’ve got a dynamic within a higher level for_each I’d recommend creating an interator. The egress dynamic becomes

  dynamic "egress" {
    for_each = var.egress_rules
    iterator = egress
    content {
      from_port       = egress.value["from_port"]
      to_port         = egress.value["to_port"]
      protocol        = egress.value["protocol"]
      cidr_blocks     = egress.value["cidr_blocks"]
      security_groups = var.security_groups
      self            = var.egress_self_sg
    }
  }

You can also use these as egress.value.from_port, etc.

In the loop, egress.key is the key for the var.egress_rule should you need it.

See the docs for more details.

Hope this helps!

@dbadrak Thanks for your response. But I got a different error now

│ Error: Invalid index
│
│   on ../../modules/security/main.tf line 30, in resource "aws_security_group" "app_sg":
│   30:       cidr_blocks     = egress.value["cidr_blocks"]
│     ├────────────────
│     │ egress.value is object with 3 attributes
│
│ The given key does not identify an element in this collection value.

Are you using the default egress_rules variable? It’s behaving like the cidr_blocks is missing.

What does this show?

% terraform console
> var.egress_rules

Please use like this:

{
description = egress.value.description
from_port = egress.value.fromport
to_port = egress.value.toport
protocol = egress.value.protocol
cidr_blocks = egress.value.cidrblocks
self = egress.value.self
security_groups = egress.value.security_groups

}

I’m running into this same issue, but with different values. It seems like one of the attributes are just being left out.

What I’ve noticed (Terraform 1.1.3) is if the attribute is not defined explicitly in the variable definition, then it gets left out? This makes having psuedo-optional values tough, but there is at least a new experimental feature that allows to configure a variable as optional.