Problems using a Terraform for loop to do some transformation for use in for_each

Hi all,

I’m trying to create a new value so I can use for_each to create some alarms using CloudWatch. I’ve checked the documentation (HashiCorp Terraform 0.12 Preview: For and For-Each) where this example is listed:

output "instance_public_ip_addresses" {
  value = {
    for instance in aws_instance.example:
    instance.id => instance.public
    if instance.associate_public_ip_address
  }
}

but when trying to follow something similar I’m wondering what I’m doing wrong. I’ve tried different combinations but I’m just getting errors. For reference I’m using version 1.0.0. My error is below followed by my code:

Error:

Error: Unsupported attribute
  on vpn.tf line 3, in locals:
   3:     for vpn in aws_vpn_connection.vpn : vpn.tags_all.Name => {
This value does not have any attributes.

Terraform code:

resource "aws_vpn_connection" "vpn" {
  customer_gateway_id = aws_customer_gateway.gw.id
  vpn_gateway_id      = aws_vpn_gateway.gw.id
  type                = aws_customer_gateway.gw.type

  tags = {
    Name = "vpn-conn-1"
  }
}

locals {
  vpns = { for vpn in aws_vpn_connection.vpn : vpn.tags_all.Name => {
      vpn_id          = vpn.id
      tunnel1_address = vpn.tunnel1_address
      tunnel2_address = vpn.tunnel2_address
    }
  }
}

The output I’m trying to achieve:

vpns = {
    vpn-conn-1 = {
        vpn_id          = "<vpn_id>"
        tunnel1_address = "<pub_ip>"
        tunnel2_address = "<pub_ip>"
    }
}

I could do a workaround, but it’s manual and isn’t really preferred:

locals {
  vpns = {
    "${aws_vpn_connection.vpn.tags_all.Name}" = {
      vpn_id          = aws_vpn_connection.vpn.id
      tunnel1_address = aws_vpn_connection.vpn.tunnel1_address
      tunnel2_address = aws_vpn_connection.vpn.tunnel2_address
    }
  }
}

Any help would be greatly appreciated, not sure if I’m missing something very obvious. Is it because the resources themselves have not been created with for_each?

To expand further, can the for loop support multiple resources to iterate over? i.e:

resource "aws_vpn_connection" "vpn" {
  customer_gateway_id = aws_customer_gateway.gw.id
  vpn_gateway_id      = aws_vpn_gateway.gw.id
  type                = aws_customer_gateway.gw.type

  tags = {
    Name = "vpn-conn-1"
  }
}

resource "aws_vpn_connection" "vpn_2" {
  customer_gateway_id = aws_customer_gateway.gw.id
  vpn_gateway_id      = aws_vpn_gateway.gw.id
  type                = aws_customer_gateway.gw.type

  tags = {
    Name = "vpn-conn-2"
  }
}

locals {
  vpns = { for vpn in [aws_vpn_connection.vpn, aws_vpn_connection.vpn_2] : vpn.tags_all.Name => {
      vpn_id          = vpn.id
      tunnel1_address = vpn.tunnel1_address
      tunnel2_address = vpn.tunnel2_address
    }
  }
}

With the output looking like:

vpns = {
    vpn-conn-1 = {
        vpn_id          = "<vpn_id>"
        tunnel1_address = "<pub_ip>"
        tunnel2_address = "<pub_ip>"
    }
    vpn-conn-2 = {
        vpn_id          = "<vpn_id>"
        tunnel1_address = "<pub_ip>"
        tunnel2_address = "<pub_ip>"
    }
}

Thanks anyone taking time to read this.

Yes, this is exactly it. You can’t iterate over a single-instance, non-expanded resource: you must use for_each (or count) for multiple resources to do this.

Not directly. I think it probably makes more sense to iterate over an expanded resource, or over the same value you pass to for_each, to ensure that the output of your loop does continues to make sense as your configuration changes in the future.

Thanks for the reply @alisdair! I thought that might be the case, but I’m glad I got it confirmed :slight_smile: Thanks again for your help.

1 Like