Terraform: Unable to express list of maps as a literal in variables

Created on 12 Jun 2016  ·  15Comments  ·  Source: hashicorp/terraform

Perhaps this one will be a wontfix for 0.7.0, but I wanted to get it recorded.

Terraform Version

master / 2127466280172a7cd0aa1d863cb932aa0107c986

Affected Resource(s)

core

Terraform Configuration Files

variable "sgtags" {
  type = "list"
  default = [
    {zone = "a"},
    {zone = "b"},
    {zone = "c"},
  ]
}

resource "aws_security_group" {
  count = 3
  tags = "${var.sgtags[count.index]}"
}

Steps to Reproduce

  1. terraform apply

    Expected Behavior

3 SGs each with a zone tag.

Actual Behavior

Error loading config: Error parsing /path/to/main.tf: At 4:5: unexpected token while parsing list: LBRACE
bug core

Most helpful comment

For anyone else trying to figure out how to access an attribute of a specific map, try:

"${lookup(var.var_name[index], "attribute_name")}"

Replacing var_name, index and attribute_name as appropriate

All 15 comments

Worth noting that a list of maps _can_ be expressed in both module and resource declarations by repeating blocks.

Examples:

module "foo" {
  source = "./foo"
  listofmaps {
    foo = 1
  }
  listofmaps {
    bar = 2
  }
}
resource "aws_instance" "foo" {
  ebs_block_device {
    device = "/dev/xvda"
    # ...
  }
  ebs_block_device {
    # ...
    device = "/dev/xvdb"
  }
}

Related a bit to #7030

Also duplicate of #7032?

Have you verified that setting tags from a list of maps variable like this works?
I tried to do something similar with aws_elastic_beanstalk_environment.setting with no luck: https://groups.google.com/d/msg/terraform-tool/-42XvH2bbRk/6qrJ8K9hAwAJ

I got an error when I tried: aws_elastic_beanstalk_environment.myEnv: gob: type not registered for interface: map[string]ast.Variable

This is marked as closed, but I can't seem to figure out what the actual syntax is. This seems to produce only a single map:

variable "list_of_maps" {
    default {
        id = "747381f4"
        key_permissions = ["get"]
        secret_permissions = ["get"]
    }
    default {
        id = "c411a1534b6e"
        key_permissions = ["get"]
        secret_permissions = ["get"]
    }
}

Syntax is:

```
variable "list_of_maps" {
type = "list"
default = [
{
field = "value"
},
{
field = "value"
}
]
}

@phinze in your example, how would you declare listofmaps in the module? Also, what happens if you only declare one listofmaps? I looked at your hcl PR and it wasn't 100% clear.

My hope is I can do this:

```hcl
variable "listofmaps" { type = "list" }

module "foo" {
# This would end up as ${var.listofmaps.0}
listofmaps {
bar = "baz"
}
}

@joestump yep! the variable inside the module will just be "list" as that's the top-level type

For anyone else trying to figure out how to access an attribute of a specific map, try:

"${lookup(var.var_name[index], "attribute_name")}"

Replacing var_name, index and attribute_name as appropriate

@phinze I'm unable to get your example working. Using Terraform 0.10.2:

0:0 /tmp/a $ tree
.
├── a.tf
└── b
    └── b.tf

1 directory, 2 files
0:0 /tmp/a $ cat a.tf
provider "aws" {
  region = "us-west-2"
}

module "b" {
  source = "b/"

  list_of_maps {
    name = "name1"
  }

  list_of_maps {
    name = "name2"
  }

  list_of_maps {
    name = "name3"
  }
}
0:0 /tmp/a $ cat b/b.tf
variable "list_of_maps" {
  type = "list"
}

resource "aws_ecs_cluster" "c" {
  count = "${length(var.list_of_maps)}"
  name  = "${lookup(element(var.list_of_maps, count.index), "name")}"
}
0:0 /tmp/a $ terraform plan
1 error(s) occurred:

* module.b.var.list_of_maps: variable list_of_maps in module b should be type list, got map

Yet if I change list_of_maps to be map, I get

Error asking for user input: 1 error(s) occurred:

* module.b.var.list_of_maps: variable list_of_maps in module b should be type map, got list

Hi @ericlagergren - that looks correct from here! I'd recommend posting your repro as a fresh issue and referencing back to this one.

the above is not a solution when you want to concatenate a list of maps to another (the one is declared inline and the other from a variable) maybe iterating though the provided would...

I think this is still an issue. I really don't see a way of generating a list of maps from a list.
For example:

locals {
  ports = [22, 80, 443]
}

resource "digitalocean_firewall" "host" {
  name        = "some_name"
  droplet_ids = ["${digitalocean_droplet.host.*.id}"]
  inbound_rule = [
    {
      protocol   = "tcp"
      port_range = "${local.open_ports.*}"
    }
  ]
}

I don't see how currently this is possible.

Could it work if instead of llimited HCL templates we would use a json template?

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings