Search code examples
amazon-web-servicesterraformterraform-provider-aws

In Terraform, how can I create an iterative list out of two aws_subnet objects?


New to Terraform. I have two aws_subnet objects which I want to associate with route tables. As I understand it, each AZ will need it's own route table. The easiest thing to do would be just declare two route tables, one for each subnet but would like to know if there is a better way to do it instead of just settling for things thrown together.

I have declared my subnets as a list in variables.tf:

variable "my_public_subnets" {
  type         = list
  description  = "public subnet within vpc cidr block"
  default      = ["10.1.2.0/24", "10.1.1.0/24"]
}

And have two public subnets in main.tf


resource "aws_subnet" "pub_1" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = var.my_public_subnets[0]
  availability_zone = "us-east-1a"
}
    
resource "aws_subnet" "pub_2" {
  vpc_id     = aws_vpc.vpc.id
  cidr_block = var.my_public_subnets[1]
  availability_zone = "us-east-1b"
}

Instead of:

resource "aws_route_table_association" "pub_ra_1" {
  subnet_id      = aws_subnet.pub_1.id
  route_table_id = aws_route_table.bar.id
}

resource "aws_route_table_association" "pub2_ra_2" {
  subnet_id      = aws_subnet.pub_2.id
  route_table_id = aws_route_table.bar.id
}

Is there way to do something like this? Create a list/array/map of those two subnets so I don't have to declare a aws_route_table_association for both of them? Maybe there's a better way to set this up in general?

locals {
  my_pub_subnets = [aws_subnet.pub_1, aws_subnet.pub_2]
}

resource "aws_route_table_association"  "pub_rt_a" {
  for_each                = locals.my_pub_subnets
  subnet_id               = each.value
  route_table_id          = aws_route_table.some_public_route_table.id
  depends_on              = [aws_subnet.pub_1]
}

Solution

  • Modules are how you create repeatable procedures in TF.

    Something like:

    locals{
    subnets = {
    public = "10.1.2.0/24",
    private = "10.1.1.0/24"
    }
    
    module "subnets" {
     source = "./modules/subnets"
     for_each                = subnets
    
     name = each.key
     cidr = each.value
    }
    

    for the AZ names, you could also use data.aws_availability_zones.available.names

    I would guess that most of you want is really well done inside the VPC module.

    You would have to import the VPC into your state to start, but this is how I do my subnets with it.

    locals {
     subnets         = chunklist(cidrsubnets("10.2.8.0/24", 3, 3, 3, 3, 3, 3), 2)
      public_subnets  = local.subnets[1]
      private_subnets = local.subnets[2]
    }
    data "aws_availability_zones" "available" {
    }
    
    resource "aws_eip" "nat" {
      count    = length(local.private_subnets)
    
      vpc  = true
    
    
    }
    
    module "vpc" {
      source  = "terraform-aws-modules/vpc/aws"
      version = "3.14.0"
    
      name                 = "foo"
      cidr                 = "10.2.8.0/24"
      azs                  = data.aws_availability_zones.available.names
      private_subnets      = local.private_subnets
      public_subnets       = local.public_subnets
      enable_nat_gateway   = true
      single_nat_gateway   = true
      enable_dns_hostnames = true
      reuse_nat_ips        = true # <= Skip creation of EIPs for the NAT Gateways
      external_nat_ip_ids  = aws_eip.nat.*.id
    
      public_subnet_tags = {
        "Tier" = "Public"
      }
    
      private_subnet_tags = {
        "Tier" = "Private"
      }
    }
    
    output "public_subnets" {
      value       = module.vpc.public_subnets
    
    }
    output "public_subnets_cidr" {
      value       = module.vpc.public_subnets_cidr_blocks
    
    }
    output "private_subnets" {
      value       = module.vpc.private_subnets
    
    }
    output "private_subnets_cidr" {
      value       = module.vpc.private_subnets_cidr_blocks
    
    }