Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

map list of maps to a list of selected field values in terraform

Tags:

terraform

If resources use a count parameter to specify multi resources in terraform there is a simple syntax for providing a list/array of dedicated fields for the resource instances.

for example

aws_subnet.foo.*.id

Since quite a number of versions it is possible to declare variables with a complex structure, for example lists of maps.

variable "data" {
  type = "list"
  default = [
    {
      id = "1"
      ...
    },
    {
      id = "10"
      ...
    }
  ]
}

I'm looking for a possibility to do the same for varaibles I can do for multi resources: a projection of an array to an array of field values of the array elements.

Unfortunately

var.data.*.id

does not work as for resources. Is there any possibility to do this?

like image 355
Uwe Krüger Avatar asked May 10 '17 13:05

Uwe Krüger


People also ask

What is MAP Variable terraform?

terraform-map-variable.tfA variable can have a map type assigned explicitly, or it can be implicitly declared as a map by specifying a default value that is a map. The above demonstrates both. Then, replace the aws_instance with the following: resource "aws_instance" "example" { ami = var.

What is map of string in terraform?

Maps are a collection of string keys and string values. These can be useful for selecting values based on predefined parameters such as the server configuration by the monthly price.


2 Answers

UPDATE

Massive fancy features have been added into terraform since Terraform 0.12 was released, e.g., list comprehension, with which the solution is super easy.

locals {
  ids = [for d in var.data: d.id]
  #ids = [for d in var.data: d["id"]]  #same
}

# Then you could get the elements this way,
#     local.ids[0]

Solution before terraform 0.12

template_file can help you out.

data "template_file" "data_id" {
  count = "${length(var.data)}"
  template = "${lookup(var.data[count.index], "id")}"
}

Then you get a list "${data.template_file.data_id.*.rendered}", whose elements are value of "id".

You can get its element by index like this

"${data.template_file.data_id.*.rendered[0]}"

or through function element()

"${element(data.template_file.data_id.*.rendered, 0)}"
like image 159
Bruce Avatar answered Sep 17 '22 13:09

Bruce


A potentially simpler answer is to use the zipmap function.

Starting with an environment variable map compatible with ECS template definitions:

locals {
  shared_env = [
    {
      name  = "DB_CHECK_NAME"
      value = "postgres"
    },
    {
      name  = "DB_CONNECT_TIMEOUT"
      value = "5"
    },
    {
      name  = "DB_DOCKER_HOST_PORT"
      value = "35432"
    },
    {
      name  = "DB_DOCKER_HOST"
      value = "localhost"
    },
    {
      name  = "DB_HOST"
      value = "my-db-host"
    },
    {
      name  = "DB_NAME"
      value = "my-db-name"
    },
    {
      name  = "DB_PASSWORD"
      value = "XXXXXXXX"
    },
    {
      name  = "DB_PORT"
      value = "5432"
    },
    {
      name  = "DB_QUERY_TIMEOUT"
      value = "30"
    },
    {
      name  = "DB_UPGRADE_TIMEOUT"
      value = "300"
    },
    {
      name  = "DB_USER"
      value = "root"
    },
    {
      name  = "REDIS_DOCKER_HOST_PORT"
      value = "6380"
    },
    {
      name  = "REDIS_HOST"
      value = "my-redis"
    },
    {
      name  = "REDIS_PORT"
      value = "6379"
    },
    {
      name  = "SCHEMA_SCRIPTS_PATH"
      value = "db-scripts"
    },
    {
      name  = "USE_LOCAL"
      value = "false"
    }
  ]
}

In the same folder launch terraform console for testing built-in functions. You may need to terraform init if you haven't already.

terraform console

Inside the console type:

zipmap([for m in local.shared_env: m.name], [for m in local.shared_env: m.value])

Observe the output of each list-item-map being a name-value-pair of a single map:

{
  "DB_CHECK_NAME" = "postgres"
  "DB_CONNECT_TIMEOUT" = "5"
  "DB_DOCKER_HOST" = "localhost"
  "DB_DOCKER_HOST_PORT" = "35432"
  "DB_HOST" = "my-db-host"
  "DB_NAME" = "my-db-name"
  "DB_PASSWORD" = "XXXXXXXX"
  "DB_PORT" = "5432"
  "DB_QUERY_TIMEOUT" = "30"
  "DB_UPGRADE_TIMEOUT" = "300"
  "DB_USER" = "root"
  "REDIS_DOCKER_HOST_PORT" = "6380"
  "REDIS_HOST" = "my-redis"
  "REDIS_PORT" = "6379"
  "SCHEMA_SCRIPTS_PATH" = "db-scripts"
  "USE_LOCAL" = "false"
}
like image 21
Gio Avatar answered Sep 19 '22 13:09

Gio