It's common to need to create multiple similar resources. It would be great if there were to be a way of iterating through an array or group of arrays, creating a resource for each item.
An example might be creating subnets in AWS VPC. Right now my declaration looks like this:
resource "aws_subnet" "igw-subnet-eu-west-1a" {
vpc_id = "${aws_vpc.eu-west-1.id}"
availability_zone = "eu-west-1a"
cidr_block = "192.168.192.0/24"
}
resource "aws_subnet" "igw-subnet-eu-west-1b" {
vpc_id = "${aws_vpc.eu-west-1.id}"
availability_zone = "eu-west-1b"
cidr_block = "192.168.193.0/24"
}
resource "aws_subnet" "igw-subnet-eu-west-1c" {
vpc_id = "${aws_vpc.eu-west-1.id}"
availability_zone = "eu-west-1c"
cidr_block = "192.168.194.0/24"
}
With an iterator, maybe this could be compressed to something like:
variable subnets {
zones = [ "eu-west-1a", "eu-west-1b", "eu-west-1c" ],
blocks = [ "192.168.192.0/24", "192.168.193.0/24", "192.168.194.0/24" ]
}
resource "aws_subnet" "igw-subnet-${foreach(subnets,zones)}" {
vpc_id = "${aws_vpc.eu-west-1.id}"
availability_zone = "${foreach(subnets,zones)}"
cidr_block = "${foreach(subnets,blocks)}"
}
I suggested something somewhat similar in #133. See some possible syntax below. This is more general as you can have more possible configurations without needing to rely on iteration. However, I can see both approaches as being useful as the iteration syntax is more compact for the scenario @bashtoni presented above.
resource "aws_instance" "base_instance" {
instance_type = "m1.small"
ami = "${lookup(var.aws_amis, var.aws_region)}"
abstract = true
}
resource "base_instance" "web" {
provisioner "remote-exec" {
inline = [
"setup web"
]
}
}
How about something like this?
variable subnets {
az = ['a', 'c', 'd']
base_net = "10.0.0.0/16"
}
variable vpc {}
variable region {}
for ${var.subnets.az} {
resource "aws_subnet" "igw-subnet-${var.region}${forValue()}" {
vpc_id = "${var.vpc}"
availability_zone = "${var.region}${forVal()}"
cidr_block = cidr("${var.subnet.base_net}", "0.0.${forNumber()}.0/24")
}
}
New for "statement".
New forValue() and forNumber() builtin functions to access the iteration state.
New cidr() function to validate a string as a CIDR format, and optionally use a mask for addition (as in this case).
I think this is in the same realm of #353 so I'm going to close this to get all our thinking going there.
I know this issue was closed long ago but I wanted to share the way I found to solve this specific scenario with the current interpolation features and the embedded functions:
variable "name" {
name = "jose"
}
variable "region" {
default = "us-west-1"
}
variable "vpc_cidr_block" {
default = "172.31.0.0/16"
}
variable "azs" {
default = {
"us-west-1" = "us-west-1b,us-west-1c"
"us-west-2" = "us-west-2a,us-west-2b,us-west-2c"
"us-east-1" = "us-east-1c,us-west-1d,us-west-1e"
# use "aws ec2 describe-availability-zones --region us-east-1"
# to figure out the name of the AZs on every region
}
}
resource "aws_vpc" "main" {
cidr_block = "${var.vpc_cidr_block}"
enable_dns_hostnames = true
enable_dns_support = true
instance_tenancy = "default"
tags {
"Name" = "${var.name}"
}
}
resource "aws_subnet" "private-subnet" {
vpc_id = "${aws_vpc.main.id}"
count = "${length(split(",", lookup(var.azs, var.region)))}"
cidr_block = "${cidrsubnet(var.vpc_cidr_block, 4, count.index)}"
availability_zone = "${element(split(",", lookup(var.azs, var.region)), count.index)}"
map_public_ip_on_launch = false
tags {
"Name" = "private-${element(split(",", lookup(var.azs, var.region)), count.index)}-sn"
}
}
If I run plan for this model I get this:
+ module.vpc.aws_subnet.private-subnet.0
availability_zone: "" => "us-west-1b"
cidr_block: "" => "172.31.0.0/20"
map_public_ip_on_launch: "" => "0"
tags.#: "" => "1"
tags.Name: "" => "private-us-west-1b-sn"
vpc_id: "" => "${aws_vpc.main.id}"
+ module.vpc.aws_subnet.private-subnet.1
availability_zone: "" => "us-west-1c"
cidr_block: "" => "172.31.16.0/20"
map_public_ip_on_launch: "" => "0"
tags.#: "" => "1"
tags.Name: "" => "private-us-west-1c-sn"
vpc_id: "" => "${aws_vpc.main.id}"
+ module.vpc.aws_vpc.main
cidr_block: "" => "172.31.0.0/16"
default_network_acl_id: "" => "<computed>"
default_security_group_id: "" => "<computed>"
dhcp_options_id: "" => "<computed>"
enable_dns_hostnames: "" => "1"
enable_dns_support: "" => "1"
instance_tenancy: "" => "default"
main_route_table_id: "" => "<computed>"
tags.#: "" => "1"
tags.Name: "" => "jose"
@jfromaniello This is a great solution. I am in a position where I need to get these subnet IDs as a list (for instance, to create an ELB). Do you know of an easy way to retrieve a list of all subnets created?
@scholzie I'm in the same boat. Did you ever figure this out?
found the answer on https://www.terraform.io/docs/configuration/interpolation.html
To reference attributes of other resources, the syntax is TYPE.NAME.ATTRIBUTE. For example, ${aws_instance.web.id} will interpolate the ID attribute from the "aws_instance" resource named "web". If the resource has a count attribute set, you can access individual attributes with a zero-based index, such as ${aws_instance.web.0.id}. You can also use the splat syntax to get a list of all the attributes: ${aws_instance.web.*.id}. This is documented in more detail in the resource configuration page.
Most helpful comment
I know this issue was closed long ago but I wanted to share the way I found to solve this specific scenario with the current interpolation features and the embedded functions:
If I run plan for this model I get this: