Search code examples
amazon-web-servicesterraformterraform-provider-aws

Terraform Nested Loop in same resource


I'm currently facing an issue in my Terraform configuration, specifically with the "Invalid for_each argument" error. I have a configuration that involves creating AWS Load Balancer target groups and attachments. I'm using a local.c variable with keys derived from resource attributes, and it seems that Terraform is unable to determine the full set of keys during the planning phase.

As I am a new to Terraform. Please help me with this problem. What will be the better solution? This is my main.tf an variables.tf

resource "aws_lb_target_group" "tg" {
  # count    = length(var.target-port)
  # name     = "${var.targetgroup-name}-${var.target-port[count.index]}"
  # port     = var.target-port[count.index]
  for_each = toset(var.target-port)
  name     = "${var.targetgroup-name}-${each.value}"
  port     = each.value
  protocol = "HTTP"
  vpc_id   = "vpc-571f3830"
}
locals {
  a = {for anr in aws_lb_target_group.tg : anr.arn => anr}
  
}
locals {
  b = {for p in setproduct(var.target, var.target-port):
                "${p[0]}-${p[1]}" => p}
                
}

locals {
  c = {for c in setproduct(local.a, local.b):
                "${c[0]}-${c[1]}" => c}
                
}

resource "aws_lb_target_group_attachment" "tg-attach" { 
  for_each         = local.c
  target_group_arn = each.value[0]
  target_id        = each.value[1]
  port             = each.value[2]
  depends_on = [ aws_lb_target_group.tg]
}

variables.tf

variable "target-port" {
    type = set(string)
    default = [ "81" , "82" , "83" , "84" , "85" ]
  
}

variable "listenerport" {
    type = set(string)
    default = [ "81" , "82" , "83" , "84" , "85" ]
}

variable "target" {
      type = set(string)
      default = ["i-08990bdd2df0" , "i-0d3b9802d7ac"]
}

variable "targetgroup-name" {
    default = "terraform-alb-tg"
}

The error I faced was

Error: Invalid for_each argument
│
│   on main.tf line 47, in resource "aws_lb_target_group_attachment" "tg-attach":
│   47:   for_each         = local.c
│     ├────────────────
│     │ local.c will be known only after apply
│
│ The "for_each" map includes keys derived from resource attributes that cannot be determined until apply, and so     
│ Terraform cannot determine the full set of keys that will identify the instances of this resource.
│
│ When working with unknown values in for_each, it's better to define the map keys statically in your configuration   
│ and place apply-time results only in the map values.
│
│ Alternatively, you could use the -target planning option to first apply only the resources that the for_each value  
│ depends on, and then apply a second time to fully converge.

The result I need is simple. I want to create Targetgroups with these ports and attachment will be these 2 ec2 instances id.


Solution

  • The error message has more details giving clues on how to solve the problem too..

    • -target option helps you create independent resources in the first go followed by dependent resources. Doesn't scale well with CI/CD as you can't expect all the resources you are going to create with two-pronged approach.
    • Another way as said in the error message is When working with unknown values in for_each, it's better to define the map keys statically in your configuration │ and place apply-time results only in the map values.

    The 2nd one is what I use heavily. But you have to redesign your tf configuration files accordingly with deterministic names (not using random ones).

    In your case, rather relying directly on aws_lb_target_group.tg output you predict the output would be something like a static arn:aws:elasticloadbalancing:us-west-2:187416307283:targetgroup/app-front-end/20cfe21448b66314. With this, you could construct local variables & use in dependent resources with depends_on meta-argument (it is a must otherwise you hit race conditions).

    If you would like to know more about this constraint, read this comment.