Search code examples
amazon-web-servicesterraformnested-loops

Terraform nested loop of objects to create multiple subnets


Been trying to create multiple subnet resources and got stuck at some point. So here is the code:

# variables.tf

variable "vpcs" {
    type = map(object({
        cidr = string
        tags = map(string)
        tenancy = string
    }))
    default = {
      "RU" = {
        cidr = "10.0.0.0/16"
        tags = {
          "Name" = "RU-VPC"
        }
        tenancy = "default"
      }
      "UZ" = {
        cidr = "192.168.0.0/16"
        tags = {
          "Name" = "UZ-VPC"
        }
        tenancy = "default"
      }
    }
  
}

variable "subnets" {
    type = map(object({
        cidr = string
        az = string
        tags = map(string)
    }))
    default = {
      "RU-Public-A" = {
        az = "us-east-1a"
        cidr = "10.0.1.0/24"
        tags = {
          "Name" = "RU-Public-A"
        }
      }
      "RU-Public-B" = {
        az = "us-east-1b"
        cidr = "10.0.2.0/24"
        tags = {
          "Name" = "RU-Public-B"
        }
      }
      "UZ-Public-A" = {
        az = "us-east-1a"
        cidr = "192.168.1.0/24"
        tags = {
          "Name" = "UZ-Public-A"
        }
      }
      "RU-Public-B" = {
        az = "us-east-1b"
        cidr = "192.168.1.0/24"
        tags = {
          "Name" = "UZ-Public-B"
        }
      }
    }
  
}
# main.tf

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

data "aws_availability_zones" "available" {
  state = "available"
}


resource "aws_vpc" "main" {
    for_each = var.vpcs
    cidr_block = each.value["cidr"]
    instance_tenancy = each.value["tenancy"]
    tags = each.value["tags"]
  
}

resource "aws_internet_gateway" "main" {
    for_each = aws_vpc.main
    vpc_id = each.value.id
    tags = {
      "Name" = "${each.key}-IGW"
    }
  
}

resource "aws_subnet" "public" {

  
}

So I was able to create multiple VPCs and attach Internet Gateways to them using a loop. But with the subnets I'm having a problem as I can't figure out how to implement nested looping of objects. Googled for it, but couldn't find a similar problem.

Any help or hint would be aprecciated. Thank you.


Solution

  • Your subnets has sub-optimal design leading to your issues. It would be much better and easier to make it in the following form:

    variable "subnets" {
        type = map(map(object({
            cidr = string
            az = string
            tags = map(string)
        })))
        default = {
          
         RU = {      
          "Public-A" = {
            az = "ap-southeast-2a"
            cidr = "10.0.1.0/24"
            tags = {
              "Name" = "RU-Public-A"
            }
            }
            "Public-B" = {
              az = "ap-southeast-2b"
              cidr = "10.0.2.0/24"
              tags = {
                "Name" = "ap-southeast-2b"
              }
            }
          },
          UZ = {
            "Public-A" = {
              az = "ap-southeast-2a"
              cidr = "192.168.1.0/24"
              tags = {
                "Name" = "UZ-Public-A"
              }
            }
            "Public-B" = {
              az = "ap-southeast-2b"
              cidr = "192.168.2.0/24"
              tags = {
                "Name" = "UZ-Public-B"
              }
            }
          }
        }  
    }
    

    then you flatten it:

    locals {
    
      flat_subnets = merge([
            for vpck, vpcv in var.vpcs: {
              for subnetk, subnetv in var.subnets[vpck]: 
                "${vpck}-${subnetk}" => {
                    vpc_key = vpck
                    subnet = subnetv
                }
              }
          ]...)
    
    }
    

    and use as:

    resource "aws_subnet" "public" {
        for_each = local.flat_subnets
        
        vpc_id     = aws_vpc.main[each.value.vpc_key].id
        cidr_block = each.value.subnet.cidr
        availability_zone = each.value.subnet.az
        
        tags = each.value.subnet.tags
    }