Loop over a tuple in map

Hello

I hope someone can help me with this one i cant figure out how to do it:

I have my data that look like this

sa_list = {
  "app_1-test" = {
    "consumers" = [
      "topic-1",
      "topic-2",
      "topic-3",
    ]
    "groupId" = "app_1-group"
    "name" = "app_1"
  }
  "app_2-test" = {
    "consumers" = [
      "topic-1",
      "topic-2",
      "topic-3",
    ]
    "groupId" = "app_2-group"
    "name" = "app_2"
  }
}

I can also manage to have my data lookin like this

test = {
  "app_1-test" = {
    "consumers" = {
      "0" = "topic-1"
      "1" = "topic-2"
      "2" = "topic-3"
    }
    "groupId" = "app_1-group"
    "name" = "app_1"
  }
  "app_2-test" = {
    "consumers" = {
      "0" = "topic-1"
      "1" = "topic-2"
      "2" = "topic-3"
    }
    "groupId" = "app_2-group"
    "name" = "app_2"
  }
}

I want to iterate with a for_each to create a resource

resource "confluent_kafka_acl" "aws-app-consumer-read-on-topic" {
  for_each = var.sa_list
  kafka_cluster {
    id = confluent_kafka_cluster.clusters["aws"].id
  }
  resource_type = "TOPIC"
  resource_name = confluent_kafka_topic.topics_aws[each.value.consumers].topic_name
  pattern_type  = "LITERAL"
  principal     = "User:${confluent_service_account.aws-app-consumer[each.key].id}"
  host          = "*"
  operation     = "READ"
  permission    = "ALLOW"
  rest_endpoint = confluent_kafka_cluster.clusters["aws"].rest_endpoint
  credentials {
    key    = confluent_api_key.cluster-api-key["aws"].id
    secret = confluent_api_key.cluster-api-key["aws"].secret
  }
}

But i end up with this fail

each.value.consumers is tuple with 3 elements

I know it’s not possible but i’m trying to do something like this in my resource:

for_each = var.sa_list
count = count(each.value.consumers)
...
resource_name = confluent_kafka_topic.topics_aws[each.value.consumers[count.index].topic_name

The fundamental requirement of for_each is that you need to construct one collection that contains string keys for all of the resource instances you want to create.

So in your example, you need a map with keys like:

"app_1-test topic-1"
"app_1-test topic-2"
"app_1-test topic-3"
"app_2-test topic-1"
"app_2-test topic-2"
"app_2-test topic-3"

I picked space as the separator character, because it’s an easily readable separator for humans, that can usually be relied upon not to appear in the data values themselves.

The values of the map need to be all the data you need to refer to via each.value when defining the resources.

So, you might do something like:

  for_each = { for item in flatten(
    [for app_key, app_info in var.sa_list : 
      [for consumer in app_info.consumers :
        {
          app_key  = app_key
          app_info = app_info
          consumer = consumer
        }
    ]]) :
    "${item.app_key} ${item.consumer}" => item
  }

and then you can write things like each.value.app_key, each.value.app_info.groupId, each.value.consumer.

Yes, this syntax is neither easy to read nor easy to write, but you have to work with the expressiveness that the language allows you. :-/

Note: Do not be tempted to try to use count/index style keys for things like this. If you did, and then later went on to remove topic-2 for example, Terraform would either delete the confluent_kafka_acl for both topic-2 and topic-3, and then re-add the one for topic-3 … or it would delete the confluent_kafka_acl for topic-3 whilst modifying the existing one for topic-2 to refer to topic-3 instead. You don’t want to be using count/index style resource addressing for anything where undesirable effects could occur, if an item in the middle of the list is removed/added, and all the ones after it get moved to different indexes.