Reputation: 363
Is there a better way to optime the code below so I don't have to ask for availability zone again and again instead can do it in once. as the region is variable so I cant define hardcoded availability zone. can you guys please I want my public subnets to be /24
provider "aws" {
region = var.region
}
resource "aws_vpc" "app_vpc" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_name
}
}
data "aws_availability_zones" "available" {
state = "available"
}
#provision public subnet
resource "aws_subnet" "public_subnet_01" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_01
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "public_subnet_01"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
resource "aws_subnet" "public_subnet_02" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_02
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "public_subnet_02"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
resource "aws_subnet" "public_subnet_03" {
vpc_id = aws_vpc.app_vpc.id
cidr_block = var.public_subnet_03
availability_zone = data.aws_availability_zones.available.names[2]
tags = {
Name = "public_subnet_03"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
Upvotes: 8
Views: 10610
Reputation: 74055
An important hazard to consider with the aws_availability_zones
data source is that the set of available zones can change over time, and so it's important to write your configuration so that you don't find yourself trapped in a situation where Terraform thinks you intend to replace a subnet that you are currently using and therefore cannot destroy.
A key part of that is ensuring that Terraform understands that each of the subnets belongs to a specific availability zone, so that when the set of availability zones changes Terraform can either add a new subnet for a new availability zone or remove an existing subnet for a now-removed availability zone, without affecting the others that haven't changed. The easiest way to achieve that is to use resource for_each
with the set of availability zones:
resource "aws_subnet" "public" {
for_each = aws_avaiability_zones.available.names
# ...
}
The above will declare subnet instances with addresses that each include the availability zone name, like this:
aws_subnet.public["eu-west-1a"]
aws_subnet.public["eu-west-1b"]
aws_subnet.public["eu-west-1e"]
Because they are identified by the availability zone name, Terraform can see that each subnet belongs to a particular availability zone.
For subnets in particular there is an additional challenge: we must assign each subnet its own CIDR block, which means we need a systematic way of allocating IP address space to availability zones so that the networks won't get renumbered by future changes to the set of availability zones.
The documentation for the aws_availability_zone
data source includes an example of declaring a mapping table that assigns each region and each availability zone a number between 1 and 14 which is then used to populate one of the octets of the IP address to create a separate prefix per (region, AZ) pair. That example creates only a single VPC and a single subnet, but we can expand on that by using for_each
to do it for each of the availability zones, as long as we update the mapping tables whenever we use a new region or a new availability zone suffix letter is assigned (up to 14 of each):
variable "region_number" {
# Arbitrary mapping of region name to number to use in
# a VPC's CIDR prefix.
default = {
us-east-1 = 1
us-west-1 = 2
us-west-2 = 3
eu-central-1 = 4
ap-northeast-1 = 5
}
}
variable "az_number" {
# Assign a number to each AZ letter used in our configuration
default = {
a = 1
b = 2
c = 3
d = 4
e = 5
f = 6
# and so on, up to n = 14 if that many letters are assigned
}
}
data "aws_region" "current" {}
# Determine all of the available availability zones in the
# current AWS region.
data "aws_availability_zones" "available" {
state = "available"
}
# This additional data source determines some additional
# details about each VPC, including its suffix letter.
data "aws_availability_zone" "all" {
for_each = aws_avaiability_zones.available.names
name = each.key
}
# A single VPC for the region
resource "aws_vpc" "example" {
cidr_block = cidrsubnet("10.1.0.0/16", 4, var.region_number[data.aws_region.current.name])
}
# A subnet for each availability zone in the region.
resource "aws_subnet" "example" {
for_each = aws_availability_zone.all
vpc_id = aws_vpc.example.id
availability_zone = each.key
cidr_block = cidrsubnet(aws_vpc.example.cidr_block, 4, var.az_number[each.value.name_suffix])
}
For example, if we were working in us-west-2
and there were availability zones us-west-2a
and us-west-2c
, the above would declare:
aws_vpc.example
with CIDR block 10.1.48.0/20
, where 48 is the decimal representation of hex 0x30, where 3 is the number for us-west-2.aws_subnet.example["us-west-2a"]
in us-west-2a
with CIDR block 10.1.49.0/24
, where 49 is the decimal representation of hex 0x31.aws_subnet.example["us-west-2c"]
in us-west-2c
with CIDR block 10.1.51.0/24
, where 51 is the decimal representation of hex 0x33.Notice that there is no subnet for 10.1.50.0/24
, because 50 (hex 0x32) is reserved for a hypothetical us-west-2b
. By allocating these addresses statically by subnet letter we can ensure that they will not change over time as availability zones are added and removed.
Upvotes: 9
Reputation: 238051
You can automate creation of subnets using count and cidrsubnets.
An example would be:
resource "aws_subnet" "public_subnet" {
count = length(data.aws_availability_zones.available.names)
vpc_id = aws_vpc.app_vpc.id
cidr_block = cidrsubnet(aws_vpc.app_vpc.cidr_block, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "public_subnet_${count.index}"
}
depends_on = [aws_vpc_dhcp_options_association.dns_resolver]
}
The above will automatically create subnet in each AZ as well as assing cidr block (/24, assuming that vpc is /16) to it.
Upvotes: 4