amazon-web-servicesterraformsubnetcidravailability-zone

Creating multiple subnets in order per availability zone with Terraform


New to Terraform and trying to create the following VPC from Adrian Cantril's class using the DRY method.

VPC Diagram

I can get the first 4 subnets created but when I try to repeat it, it duplicates it per AZ giving me an error.

I have tried a few other things that created 1 subnet per AZ, e.g. 10.16.0.0/20 in AZ A, 10.16.16.0 /20 in AZ B, etc..

Below is a snippet of the code I am using.

variable "vpc_cidr" {
  type    = string
  default = "10.16.0.0/16"
}

resource "aws_subnet" "private_subnets-az-a" {
  count             = 4
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 4, count.index)
  availability_zone = data.aws_availability_zones.available.names[0]
}

resource "aws_subnet" "private_subnets-az-b" {
  count             = 4
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 4, count.index)
  availability_zone = data.aws_availability_zones.available.names[1]


Solution

  • This should be pretty DRY:

    locals {
      region  = "us-east-1"
      subnets = {
        for i, v in setproduct(["a", "b", "c"], ["reserved", "db", "app", "web"]) :
        "${local.region}${v[0]}-${v[1]}" =>
        {
          az   = "${local.region}${v[0]}"
          cidr = cidrsubnet("10.16.0.0/16", 4, i)
        }
      }
    }
    
    resource "aws_subnet" "this" {
      for_each          = local.subnets
    
      vpc_id            = aws_vpc.vpc.id
      cidr_block        = each.value.cidr
      availability_zone = each.value.az
    }
    

    This will create 12 subnets as per the diagram.

    locals.subnets looks like this:

    subnets = {
      "us-east-1a-reserved" = {
        "az" = "us-east-1a"
        "cidr" = "10.16.0.0/20"
      }
      "us-east-1a-db" = {
        "az" = "us-east-1a"
        "cidr" = "10.16.16.0/20"
      }
      "us-east-1a-app" = {
        "az" = "us-east-1a"
        "cidr" = "10.16.32.0/20"
      }
      "us-east-1a-web" = {
        "az" = "us-east-1a"
        "cidr" = "10.16.48.0/20"
      }
      "us-east-1b-reserved" = {
        "az" = "us-east-1b"
        "cidr" = "10.16.64.0/20"
      }
      // ... and so on
    }
    

    This for_each approach is more usable than the count variants because you can access the created subnets as follows: aws_subnet.this["us-east-1a-reserved"].arn as opposed to aws_subnet.this[3].arn.

    See setproduct and for expressions to understand what's going on in locals.subnets.

    On a related note, it's a good idea to try to create a VPC and subnets from scratch when you are learning, but if you go to production, I'd recommend using this Terraform VPC module - https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest.