If Conditionals with for_each

Have troubles to make if condition with for_each

locals {
  services = {
    api = {
      task_definition = "api.json"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
    auth = {
      task_definition = "auth.json"
      service_discovery_enabled = true
    }
    post = {
      task_definition = "post.json"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
  }
}

Try to run resource only for service where application_loadbalancer = true

resource "aws_cloudwatch_dashboard" "this" {
  for_each = [for s in var.services : s if lookup(s, "application_loadbalancer", false)]

  dashboard_name = "${var.name}-${terraform.workspace}-${each.key}-metrics-dashboard"

  dashboard_body = data.template_file.metric_dashboard[each.key].rendered
}

unsuccessfully:

Error: Invalid for_each argument

  on ../../../terraform/main.tf line 77, in resource "aws_cloudwatch_dashboard" "this":
1178:   for_each = [for s in var.services : s if lookup(s, "application_loadbalancer", false)]

The given "for_each" argument value is unsuitable: the "for_each" argument
must be a map, or set of strings, and you have provided a value of type tuple.

Any ideas on how to resolve it?

1 Like

I’m not sure why the right side of the for_each is treated as a tuple rather than a list of maps. While wrapping it in toset() doesn’t solve the issue, I find the error message illuminating:

The given “for_each” argument value is unsuitable: “for_each” supports maps
and sets of strings, but you have provided a set containing type object.

In particular, “for_each” supports maps and sets of strings. [for s in var.services : s if lookup(s, “application_loadbalancer”, false)] appears to attempt to construct a list/set of maps/objects. If the goal is to pass the entire object, use { for … } to create a map of objects. If only the keys are desired, they can be extracted with keys(). The example below demonstrates both techniques.

locals {
  services = {
    api = {
      task_definition = "api.json"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
    auth = {
      task_definition = "auth.json"
      service_discovery_enabled = true
    }
    post = {
      task_definition = "post.json"
      service_discovery_enabled = true
      application_loadbalancer = true
    }
  }
}

provider "null" { }

resource "null_resource" "this" {
# This doesn't work
# for_each = [ for s in local.services : s if lookup(s, "application_loadbalancer", false) ]

# These do
# for_each = toset([ for s in keys(local.services): s if lookup(local.services[s], "application_loadbalancer", false) ])
# for_each = { for s,v in local.services: s => v if lookup(v, "application_loadbalancer", false) }

  provisioner "local-exec" {
    command = "echo selected: ${each.key}"
  }
}
1 Like

Thanks, that helps, but i have another problem with dynamic block that also contains for_each:

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]
  }
}

I got:
Error: Invalid index

  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.