Availability zones / mappings with lists

4,118 views
Skip to first unread message

Gareth Adams

unread,
Aug 3, 2015, 6:46:18 AM8/3/15
to Terraform
Hi guys,

In a CloudFormation template, I can use the built-in function "GetAZs", which AWS expands to a list of the availability zones in the region I'm deploying the template in.

I understand that because Terraform builds its own dependency tree and can be used across services, that there's no simple drop-in replacement for this function, which I'm ok with if I can find an alternative.

It seems like the mapping syntax described in the documentation[1] doesn't allow for lists as mapping values. When I try and output the plan for a terraform file containing the script below (unrelated parts removed), I get the error "Variable 'aws_availability_zones': must be string or mapping" (Terraform v0.6.1).

Is what I'm trying to do possible at all?

    variable "aws_region" {
        default = "eu-west-1"
    }

    variable "aws_availability_zones" {
      default = {
        eu-west-1 = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
        us-east-1 = ["eu-west-1b", "eu-west-1c", "eu-west-1d", "eu-west-1e"]
      }
    }

    resource "aws_elb" "elasticsearch" {
      availability_zones = ["${lookup(var.aws_availability_zones, var.aws_region)}"]
    }

Thanks,
Gareth

Gareth Adams

unread,
Aug 3, 2015, 9:14:28 AM8/3/15
to Terraform
After a big more searching, I found an example of this kind of setup in the Github examples repo[1]. It requires that the variables are always strings, which then get split using ${split(",",lookup(var.variable_name))}. This makes sense because variables passed in on the command line will always be simple strings.

ja...@fpcomplete.com

unread,
Aug 4, 2015, 6:49:25 PM8/4/15
to Terraform


On Monday, August 3, 2015 at 6:46:18 AM UTC-4, Gareth Adams wrote:
Hi guys,

In a CloudFormation template, I can use the built-in function "GetAZs", which AWS expands to a list of the availability zones in the region I'm deploying the template in.


As a more general / practical question, how often does the list of active AZ per region change? For example, if we hardcode something like this in our modules, would we be better off with something automatic that would lookup the current list?


Thanks!

Matt Peter

unread,
Aug 7, 2015, 12:05:00 AM8/7/15
to Terraform
Here's some snippets of what I use to create blocks of subnet's that cross AZ's. This pushes these concepts about as far as I've seen them go. Posting here because someone else may find it useful. This let's us flip the same TF config between regions with very little effort.

variable aws_vpc_cidr_prefix { default = "172.20" }
variable aws_region
{ default = "us-east-1" }

variable aws_azs
{
 
default = {
    us
-east-1 = "us-east-1a,us-east-1c,us-east-1d"
    us
-west-2 = "us-west-2a,us-west-2b,us-west-2c"
 
}
}

#
# Each app subnet group is a /21 (172.20.0.0/21)
# each app subnet group is split across up to 4 seperate
# AZ's, i.e. 172.20.[0,4,8,12].0/22
#
# Example:
# gfcp-app              -> 172.20.0.0 - 172.20.15.255
# gfcp-app (us-east-1a) -> 172.20.0.0 - 172.20.3.255
# gfcp-app (us-east-1c) -> 172.20.4.0 - 172.20.7.255
# gfcp-app (us-east-1d) -> 172.20.8.0 - 172.20.11.255
# gfcp-app (unused)     -> 172.20.12.0 - 172.20.15.255
#
# This configuration gives:
# - 4 AZ's per region (currently only use 3)
# - 16 App networks
# - 4096 IP addresses per App network
# - 1024 IPaddresses per App, per AZ
#
variable aws_appnet_map
{
 
default = {
    gfcp
-app =   "0" # jboss, fuse
    gfcp
-dmz =  "16" # boxes with public and private ips
    gfcp
-rds =  "32" # main databases
    gfcp
-web =  "48" # web-facing, apache ended up in dmz
    gfcp
-tmp =  "64" # temporary instances, like data-pipeline
    undef80  
=  "80" # unused block 80
    undef96  
=  "96" # unused block 96
    undef112
= "112" # unused block 112
    undef128
= "128" # unused block 128
    undef144
= "144" # unused block 144
    undef160
= "160" # unused block 160
    undef176
= "176" # unused block 176
    ops
-vpn  = "192" # vpn client ips
    ops
-dmz  = "208" # nat boxes, bastion
    ops
-rds  = "224" # ops databases
    ops
-app  = "240" # puppet, rundeck, etc
 
}
}

resource
"aws_subnet" "gfcp-app" {
...
  count
= "${length(split(",", lookup(var.aws_azs, var.aws_region)))}"
  availability_zone
= "${element(split(",", lookup(var.aws_azs, var.aws_region)), count.index)}"
  cidr_block
= "${var.aws_vpc_cidr_prefix}.${lookup(var.aws_appnet_map, "gfcp-app")+(4*count.index)}.0/22"
}
resource "aws_elb" "gfcp-single" {
...
subnets = [ "${aws_subnet.gfcp-app.*.id}" ]
...
}

resource "aws_autoscaling_group" "gfcp-app" {
...
  availability_zones = [ "${split(",", lookup(var.aws_azs, var.aws_region))}" ]
  vpc_zone_identifier = [ "${aws_subnet.gfcp-app.*.id}" ]
...
}






On Monday, August 3, 2015 at 6:46:18 AM UTC-4, Gareth Adams wrote:

Erez Eskenazi

unread,
Aug 21, 2016, 10:20:53 AM8/21/16
to Terraform
thx matt
I was trying to do exactly that for around 2 hours

Btw now with terraform 0.7 you can use:

variable aws_azs {
 
default = {
    us
-east-1 = ["us-east-1a","us-east-1c","us-east-1d"]
    us
-west-2 = ["us-west-2a","us-west-2b","us-west-2c"]
 
}
}


and there is no need to split

Steven Truong

unread,
Jan 7, 2017, 11:11:16 AM1/7/17
to Terraform


Hi Erez,

I tried on terraform 0.8.2 and this did not work on mine.  I searched for the doc and it clearly indicates that nested lists or maps are no good.

https://github.com/hashicorp/terraform/blob/master/website/source/docs/configuration/interpolation.html.md

lookup(map, key, [default]) - Performs a dynamic lookup into a map variable. The map parameter should be another variable, such as var.amis. If key does not exist in map, the interpolation will fail unless you specify a third argument, default, which should be a string value to return if no key is found in map. This function only works on flat maps and will return an error for maps that include nested lists or maps.

ST

Troy

unread,
Jan 8, 2017, 6:24:37 PM1/8/17
to Terraform
    variable "aws_region" {
        default = "eu-west-1"
    }

    variable "aws_availability_zones" {
      default = {
        eu-west-1 = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]
        us-east-1 = ["eu-west-1b", "eu-west-1c", "eu-west-1d", "eu-west-1e"]
      }
    }
 
Try:

resource "aws_elb" "elasticsearch" {
  availability_zones = "${var.aws_availability_zones[var.aws_region]}"
}

Lowe Schmidt

unread,
Jan 9, 2017, 4:55:24 AM1/9/17
to terrafo...@googlegroups.com
You can also use the data sources aws_availability_zones ( https://www.terraform.io/docs/providers/aws/d/availability_zones.html )

--
Lowe Schmidt | +46 723 867 157

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/terraform/issues
IRC: #terraform-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Terraform" group.
To unsubscribe from this group and stop receiving emails from it, send an email to terraform-tool+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/terraform-tool/3b0fd539-42c2-41cf-be36-0814a88f4737%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages