1
votes

Say that I need to provision a large number of vpc subnets in terraform. Each subnet has a cidr, a name and a availability zone. So in other config management tools I'd do something like:

[  
   {  
      "name":"subnet1",
      "cidr":"10.0.0.1/24",
      "az":"us-west-1a"
   },
   {  
      "name":"subnet2",
      "cidr":"10.0.0.2/24",
      "az":"us-west-1b"
   }
]

And then iterate over that array.

Terraform doesn't have a notion of arrays/objects as far as I can see. So, for arrays of single attributes I would just use a list item:

subnets: ["10.0.0.1/24","10.0.0.2/24"]

But that doesn't allow me to name or place the subnets where I want. I know that I can also use multiple lists in Terraform, something like:

subnet_names: ["subnet1", "subnet2"]
subnets: ["10.0.0.1/24","10.0.0.2/24"]
subnet_az: ["us-west-1a", "us-west-1b"]

But that strikes me as messy and counter-intuitive. The last option I see is to mash everything togehter into an ugly list of strings, and then split them apart in Terraform:

things: ["subnet1__10.0.0.1/24__us-west-1a","subnet2__10.0.0.2/24__us-west-2a"]

But thats just ugly.

How can I deal with array/object-type of repeats in Terraform? For now I've just explicitly defined all my things, which caused a simple vpc definition to be 300 lines long :-(

1
Found how to do it here: blog.gruntwork.io/… - Matt Schuchard
The Terraform docs have a simple example of count that is close to what you're looking for. terraform.io/docs/configuration/… - logicaldiagram
imho that still doesn't allow me full control over my configuration using objects. - Trondh

1 Answers

1
votes

As you've seen, at present Terraform doesn't support lists of structured data like you're trying to create here.

Having multiple flat lists of strings as you showed in your question is one common solution to this problem. It works, but as you've seen it's somewhat counter-intuitive to keep track of which values belong together that way.

An alternative approach that is likely to produce a more readable and maintainable result is to factor your aws_subnet resource out into a module that takes care of the elements that are always the same for all subnets. Then you can instantiate the module once per subnet, providing only the values that vary:

module "subnet1" {
  source = "./subnet"

  name = "subnet1"
  cidr = "10.0.0.1/24"
  az   = "us-west-1a"
}

module "subnet2" {
  source = "./subnet"

  name = "subnet2"
  cidr = "10.0.0.2/24"
  az   = "us-west-1b"
}

In many cases there's some sort of systematic relationship between AZs and CIDR blocks. If that's true for you then you can also use your module to encode these numbering rules. For example, in your subnet module:

variable "region_network_numbers" {
  default = {
    "us-west-1" = 0
    "us-east-1" = 1
    "us-west-2" = 2
  }
}

variable "az_network_numbers" {
  default = {
    a = 1
    b = 2
  }
}

variable "base_cidr_block" {
  default = "10.0.0.0/8"
}

variable "az" {
}

data "aws_availability_zone" "selected" {
  name = "${var.az}"
}

resource "aws_subnet" "main" {
  cidr_block = "${cidrsubnet(cidrsubnet(var.base_cidr_block, 8, var.region_network_numbers[data.aws_availability_zone.selected.region]), 4, var.az_network_numbers[data.aws_availability_zone.selected.name_suffix])}"
  # ...
}

With this it's sufficient to provide just the az argument to the module, with the cidr and name produced systematically from the AZ name. This is the same general idea as shown in the example for the aws_availability_zone data source, and there's a more complete, elaborate example of this in the Terraform repository itself.