Dynamically generate part of json

I have the following:

variable "list" {
  type        = list(string)
  default     = ["value_1", "value_2", "value_3", "value_4"]
}

I need to build a ‘task-definitions’ with the following part:

...
    "environment": [
      {
        "name": "TERM",
        "value": "xterm"
      },
      {
        "name": "PROJECT",
        "value": "project"
      },
      {
        "name": "ENVNAME",
        "value": "${env_name}"
      },
...
      {
        "name": "VARIABLE_1",
        "value": "value_1"
      },
      {
        "name": "VARIABLE_2",
        "value": "value_2"
      },
      {
        "name": "VARIABLE_3",
        "value": "value_3"
      },
      {
        "name": "VARIABLE_4",
        "value": "value_4"
      },
...

How to achieve it?
I tried the following:

locals {
  task_def_doc = jsonencode(
    [
      {
        environment = [
          {
            name  = "TERM",
            value = "xterm"
          },
          {
            name  = "PROJECT",
            value = "project"
          },
          {
            name  = "ENVNAME",
            value = "dev"
          },
...
          {
            for i in var.list :
            name     = "VARIABLE_"
            value    = i
          },
...
}

but it gives me an error:

Error: Invalid 'for' expression

  on locals.tf line 55, in locals:
  53:           {
  54:             for i in var.list :
  55:             name     = "VARIABLE_"
  56:             value    = i
  57:           },

Key expression is required when building an object.


Error: Invalid 'for' expression

  on locals.tf line 55, in locals:
  53:           {
  54:             for i in var.list :
  55:             name     = "VARIABLE_"

Extra characters after the end of the 'for' expression.

Hi @DenisBY,

You can use the concat function to concatenate multiple lists together into a single list, which I think would help get the result you wanted here by allowing you to mix a statically-written list with a dynamically-generated list:

locals {
  example = {
    environment = concat(
      [
        {
          name  = "TERM"
          value = "xterm"
        },
        {
          name  = "PROJECT"
          value = "project"
        },
        {
          name  = "ENVNAME"
          value = "dev"
        },
      ],
      [
        for i, v in var.list : {
          name  = "VARIABLE_${i}"
          value = v
        }
      ],
    )
  }
}

Given that these are key/value pairs serialized as a list of objects, I might choose to build the initial value of this as a map of strings and then convert the result to the list of objects separately, to make the structure easier to read and to work with:

locals = {
  task_environment = tomap(merge(
    {
      TERM    = "xterm"
      PROJECT = "project"
      ENVNAME = "dev"
    },
    { for i, v in var.list : "VARIABLE_${i}" => v }
    },
  ))

  example = {
    environment = [
      for name, value in local.task_environment : {
        name  = name
        value = value
      }
    ]
  }
}

Here I used merge instead of concat, which merges multiple maps into a single map while potentially overriding conflicting keys, and thus guarantees no duplicate names in the resulting array of environment objects once you serialize to JSON.

1 Like

Thank you very much! It worked!