Terraform: Support for nested maps in variables

Created on 28 May 2015  ·  82Comments  ·  Source: hashicorp/terraform

I'd love to be able to do this:

variable "amis" {
  default = {
    app1 = {
      us-west-2 = "ami-abc123"
    }
    app2 = {
      us-west-2 = "ami-cba321"
    }
  }
}

Currently, you'd get an error like Variable 'amis': must be string or mapping

config enhancement

Most helpful comment

I just verified that the example given in the original comment on this issue works in Terraform v0.12-alpha1:

variable "amis" {
  default = {
    app1 = {
      us-west-2 = "ami-abc123"
    }
    app2 = {
      us-west-2 = "ami-cba321"
    }
  }
}

output "amis" {
  value = var.amis
}

In addition, Terraform v0.12 introduces the idea of _object types_ which allow specification of variables that expect a specific set of attributes that may be of different types:

variable "regions" {
  type = map(map(object({
    ami = string
  })))

  default = {
    app1 = {
      us-west-2 = {
        ami = "ami-abc123"
      }
    }
    app2 = {
      us-west-2 = {
        ami = "ami-cba321"
      }
    }
  }
}

output "regions" {
  value = var.regions
}

Although there is still a remaining small issue to fix over in #19141, this feature request is now complete. Thanks for your patience, everyone!

All 82 comments

Interestingly, I can cheat a bit:

variable "amis" {
  default = {
    app.us-west-2 = "ami-abc123"
    app2.us-west-2 = "ami-cba321"
  }
}

+1

+1

+1, I didn't realize this was not possible.

+1

+1

+1

:+1: really need to be able to pass data types other than strings in the DSL.

+1

+1 really need this one

+1

+1

+1

+1

+1

+1

Being able to have a list as a value would also be very nice

+1

:+1:

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

pleaseeeeeeeeee

+1

+1

+1

+1

+1

+1

+1

+1

+1 any closer to this with 0.7 additional support for maps?

so we have lists and maps (+ maps of lists works?)

+1

+1

Adding my vote for this.

related: #3690 #8384 #5841 #8153

+1

+1

+1

+1

+1

+1

+1

+1
gosh! :)

+1

+1

+1

+1

👍

+1

+1

+1

+1

+1

+1

+1 This would be really helpful when it comes to tags.

+1

+1

+1

+1

What are people doing to work around this?

@tomstockton it's been two years, had to drop terraform originally and then brought it back in a lesser capacity

Here is what I would do as an alternative for this map of map issue:

variable amis {
    app1 = "app1_map"
    app2 = "app2_map"
}
app1_map = {
      us-west-2 = "ami-abc123"
}
app2_map = {
      us-west-2 = "ami-cba321"
    }
}

Here's what I did for multi-environment in one variables.tf file symlinked into each environment folder

variable "subnets" {
    type = "map"
    default = {
        dev             = "10.104.0.0/16"
        qa              = "10.103.0.0/16"
        stage           = "10.102.0.0/16"
        prod            = "10.101.0.0/16"
        app.dev         = "10.104.20.0/23"
        app.qa          = "10.103.20.0/23"
        app.stage       = "10.102.20.0/23"
        app.prod        = "10.101.20.0/23"
        app_1.dev       = "10.104.20.0/24"
        app_1.qa        = "10.103.20.0/24"
        app_1.stage     = "10.102.20.0/24"
        app_1.prod      = "10.101.20.0/24"
        app_2.dev       = "10.104.21.0/24"
        app_2.qa        = "10.103.21.0/24"
        app_2.stage     = "10.102.21.0/24"
        app_2.prod      = "10.101.21.0/24"
        public.dev      = "10.104.10.0/23"
        public.qa       = "10.103.10.0/23"
        public.stage    = "10.102.10.0/23"
        public.prod     = "10.101.10.0/23"
        public_1.dev    = "10.104.10.0/24"
        public_1.qa     = "10.103.10.0/24"
        public_1.stage  = "10.102.10.0/24"
        public_1.prod   = "10.101.10.0/24"
        public_2.dev    = "10.104.11.0/24"
        public_2.qa     = "10.103.11.0/24"
        public_2.stage  = "10.102.11.0/24"
        public_2.prod   = "10.101.11.0/24"
        private_1.dev   = "10.104.xx.0/24"
        private_1.qa    = "10.103.xx.0/24"
        private_1.stage = "10.102.xx.0/24"
        private_1.prod  = "10.101.xx.0/24"
        private_2.dev   = "10.104.xx.0/24"
        private_2.qa    = "10.103.xx.0/24"
        private_2.stage = "10.102.xx.0/24"
        private_2.prod  = "10.101.xx.0/24"
    }
}

and then call it like

module "vpc" {
  source = "../modules/aws_vpc/"

  # we call the environment 'variable' here for the vpc name

  name          = "vpc-${var.environment}"

  cidr                    = "${lookup(var.subnets, var.environment)}"
  private_subnets         = ["${lookup(var.subnets, "private_1.${var.environment}")}", "${lookup(var.subnets, "private_2.${var.environment}")}"]
  public_subnets          = ["${lookup(var.subnets, "public_1.${var.environment}")}", "${lookup(var.subnets, "public_2.${var.environment}")}"]
  app_subnets             = ["${lookup(var.subnets, "app_1.${var.environment}")}", "${lookup(var.subnets, "app_2.${var.environment}")}"]
  enable_dns_hostnames    = true
  enable_dns_support      = true
  enable_nat_gateway      = "true"
}

+1

+1

+1

I just verified that the example given in the original comment on this issue works in Terraform v0.12-alpha1:

variable "amis" {
  default = {
    app1 = {
      us-west-2 = "ami-abc123"
    }
    app2 = {
      us-west-2 = "ami-cba321"
    }
  }
}

output "amis" {
  value = var.amis
}

In addition, Terraform v0.12 introduces the idea of _object types_ which allow specification of variables that expect a specific set of attributes that may be of different types:

variable "regions" {
  type = map(map(object({
    ami = string
  })))

  default = {
    app1 = {
      us-west-2 = {
        ami = "ami-abc123"
      }
    }
    app2 = {
      us-west-2 = {
        ami = "ami-cba321"
      }
    }
  }
}

output "regions" {
  value = var.regions
}

Although there is still a remaining small issue to fix over in #19141, this feature request is now complete. Thanks for your patience, everyone!

Here's what I did for multi-environment in one variables.tf file symlinked into each environment folder

variable "subnets" {
    type = "map"
    default = {
        dev             = "10.104.0.0/16"
        qa              = "10.103.0.0/16"
        stage           = "10.102.0.0/16"
        prod            = "10.101.0.0/16"
        app.dev         = "10.104.20.0/23"
        app.qa          = "10.103.20.0/23"
        app.stage       = "10.102.20.0/23"
        app.prod        = "10.101.20.0/23"
        app_1.dev       = "10.104.20.0/24"
        app_1.qa        = "10.103.20.0/24"
        app_1.stage     = "10.102.20.0/24"
        app_1.prod      = "10.101.20.0/24"
        app_2.dev       = "10.104.21.0/24"
        app_2.qa        = "10.103.21.0/24"
        app_2.stage     = "10.102.21.0/24"
        app_2.prod      = "10.101.21.0/24"
        public.dev      = "10.104.10.0/23"
        public.qa       = "10.103.10.0/23"
        public.stage    = "10.102.10.0/23"
        public.prod     = "10.101.10.0/23"
        public_1.dev    = "10.104.10.0/24"
        public_1.qa     = "10.103.10.0/24"
        public_1.stage  = "10.102.10.0/24"
        public_1.prod   = "10.101.10.0/24"
        public_2.dev    = "10.104.11.0/24"
        public_2.qa     = "10.103.11.0/24"
        public_2.stage  = "10.102.11.0/24"
        public_2.prod   = "10.101.11.0/24"
        private_1.dev   = "10.104.xx.0/24"
        private_1.qa    = "10.103.xx.0/24"
        private_1.stage = "10.102.xx.0/24"
        private_1.prod  = "10.101.xx.0/24"
        private_2.dev   = "10.104.xx.0/24"
        private_2.qa    = "10.103.xx.0/24"
        private_2.stage = "10.102.xx.0/24"
        private_2.prod  = "10.101.xx.0/24"
    }
}

and then call it like

module "vpc" {
  source = "../modules/aws_vpc/"

  # we call the environment 'variable' here for the vpc name

  name          = "vpc-${var.environment}"

  cidr                    = "${lookup(var.subnets, var.environment)}"
  private_subnets         = ["${lookup(var.subnets, "private_1.${var.environment}")}", "${lookup(var.subnets, "private_2.${var.environment}")}"]
  public_subnets          = ["${lookup(var.subnets, "public_1.${var.environment}")}", "${lookup(var.subnets, "public_2.${var.environment}")}"]
  app_subnets             = ["${lookup(var.subnets, "app_1.${var.environment}")}", "${lookup(var.subnets, "app_2.${var.environment}")}"]
  enable_dns_hostnames    = true
  enable_dns_support      = true
  enable_nat_gateway      = "true"
}

FYI: Terraform v0.12 will complain about the ' . ' in the subnet variables (e.g app.dev) with this error:
Error: Ambiguous attribute key
I changed the ' . ' delimiter and works fine.

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