Dynamic block with for_each resource

Hi, I have some troubles with dynamic blocks that used in for_each resource:


locals {
  services = {
    api = {
      container_port = "4000"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
    auth = {
      container_port = "4001"
      service_discovery_enabled = true
    }
    post = {
      container_port = "4003"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
  }
}


resource "aws_ecs_service" "this" {
  for_each = local.services

  name          = each.key
  cluster       = aws_ecs_cluster.this.name
  desired_count = lookup(each.value, "replicas", "1")
  launch_type   = "FARGATE"

  task_definition = "${aws_ecs_task_definition.this[each.key].family}:${max(
    aws_ecs_task_definition.this[each.key].revision, data.aws_ecs_task_definition.this[each.key].revision
  )}"

  deployment_minimum_healthy_percent = 100
  deployment_maximum_percent         = 200

  network_configuration {
    security_groups = [
      aws_security_group.services[each.key].id,
      aws_security_group.services_dynamic[each.key].id
    ]

    subnets          = var.vpc_create_nat ? local.vpc_private_subnets_ids : local.vpc_public_subnets_ids
    assign_public_ip = ! var.vpc_create_nat
  }

  dynamic "load_balancer" {
  for_each = { for s,v in local.services: s => v if lookup(v, "application_loadbalancer", false) }

    content {
      target_group_arn = aws_lb_target_group.this[each.key].arn
      container_name   = local.services[each.key]
      container_port   = local.services[each.key].container_port
    }
  }

  depends_on = [aws_lb_target_group.this, aws_lb_listener.this, aws_ecs_task_definition.this]

  lifecycle {
    ignore_changes = [desired_count]
  }
}

and I can’t figure out how to make it works:
on …/…/…/terraform/tf-aws-fargate/main.tf line 55, in resource “aws_ecs_service” “this”:
55: target_group_arn = aws_lb_target_group.this[each.key].arn
|----------------
| aws_lb_target_group.this is object with 1 attribute “api”
| each.key is “auth”

The given key does not identify an element in this collection value.

Also if I hardcoded:

  dynamic "load_balancer" {
  for_each = { for s,v in local.services: s => v if lookup(v, "application_loadbalancer", false) }

    content {
      target_group_arn = "arn:aws:elasticloadbalancing:eu-west-2:xxxxx:targetgroup/test-api-lb-tg/xxxx"
      container_name   = "api"
      container_port   = "4000"
    }
  }

I got installed lb it into all services:

  # aws_ecs_service.this["auth"] will be created
  + resource "aws_ecs_service" "this" {
      + cluster                            = "1111"
      + task_definition                    = "auth"

      + load_balancer {
          + container_name   = "api"
          + container_port   = 4000
          + target_group_arn = "arn:aws:elasticloadbalancing:xxxx:targetgroup/test-api-lb-tg/xxxx"
        }

please help =(

Hi @Alexvianet,

Inside a dynamic block, the default name for the iterator object for that block is the block type name, so you’d use load_balancer.key instead of each.key to access the key from your inner repetition:

  dynamic "load_balancer" {
    for_each = { for s,v in local.services: s => v if lookup(v, "application_loadbalancer", false) }

    content {
      target_group_arn = aws_lb_target_group.this[load_balancer.key].arn
      container_name   = load_balancer.key
      container_port   = local.services[load_balancer.key].container_port
    }
  }

Since the value of the repetition map is already the corresponding element from local.services, we can simplify this further by using load_balancer.value rather than looking it up again in local.services:

  dynamic "load_balancer" {
    for_each = { for s,v in local.services: s => v if lookup(v, "application_loadbalancer", false) }

    content {
      target_group_arn = aws_lb_target_group.this[load_balancer.key].arn
      container_name   = load_balancer.key
      container_port   = load_balancer.value.container_port
    }
  }

That’s helps, thank you very much!!!