Search code examples
terraformterraform-provider-awsterraform-modulesterraform-aws-modules

Terraform using outputs from multiple occurrences of a module created using for each


I have a problem that occurred to me after changing my code when it changed the number of "instances" of a sub-module from one to a dynamic number (using for each). The sub-module is not of my authorship, I use ready-made code from the registry, its initialization looks like this, among other things:

module "container_definition_sidecar" {

  source = "cloudposse/ecs-container-definition/aws"
  version = "v0.46.0"
  
  for_each = var.sidecars
  container_name = each.value.container_name
  container_image = each.value.container_image
  ...

Why does I write sub-module? Because I already use the above fragment in my own module called simply "ECS", which is initialized like this:


module "ecs-service" {
  source  = "./ecs-service"

  environment           = "test"
  awslogs_group         = "/ecs/fargate-task-definition"
  awslogs_stream_prefix = "ecs"

  container_name        = "my_container"
  container_image       = "nginx:latest"
  ...

  sidecars = {
    first_sidecar = {
        container_name  = "logzio-log-router"
        container_image = "12345.dkr.ecr.us-east-2.amazonaws.com/aws-for-fluent-bit:latest"
       }
    second_sidecar = {...}

   }

Now, where is the problem? Where, using jsonencode, I need to get the output, which according to the documentation is called "json_map_object" for each called to life module.container_definition_sidecar

resource "aws_ecs_task_definition" "task_definition" {
  family                   = var.family_name
  network_mode             = "awsvpc"
  requires_compatibilities = [ "FARGATE" ]

  container_definitions    = jsonencode([module.container_definition_sidecar[*].json_map_object])

When I try use [*] I receive such error:

 Error: Unsupported attribute
│
│   on ecs-service/main.tf line 111, in resource "aws_ecs_task_definition" "task_definition":
│  111:   container_definitions    = jsonencode([module.container_definition_sidecar.*.json_map_object])
│
│ This object does not have an attribute named "json_map_object".

And the only situation in which the code passes is when I manually type e.g.:

  container_definitions    = jsonencode([module.container_definition_sidecar["first_sidecar"].json_map_object, module.container_definition_sidecar["second_sidecar"].json_map_object])

However, of course, I don't want to manually provide keys ["first_sidecar"], ["second_sidecar"] and etc. in my function. But don't know how to handle that dynamically

I'll just add that from where the jsonencode is executed I don't have access to the references of the ecs-service module, so I can't go through it and extract the sidecar call keys.


Solution

  • Ok, I solved my own issue using by making this code - writing because maybe someone will find it useful:

      container_definitions = jsonencode([for key in range(length(var.sidecars)): module.container_definition_sidecar[keys(var.sidecars)[key]].json_map_object])
    

    That is, I make a FOR loop for as many times as the number of keys in the map object. Then I use the built-in keys() function in which I point to the map, and the numeric value of the key I want to get (not the name of the key, but the value as in the index). Thanks to the for loop, the construction of this is done dynamically, as many times as there are nodes in the map object.