Terraform For_Each: How to reference created resource ID

Viewed 2869

I created a module that creates AWS subnets. Within the module, I use a for_each loop to create the subnets. I assumed I could index a given subnet's resource ID, but it seems that the loop does not output index the subnet IDs in the predictable order... Not sure what I am missing here. Any help is appreciated!

main.tf

module "subnets" {
  source = "./modules/subnets/"
  vpc_id = aws_vpc.main.id
  subnets = [
    {
      name              = "private-1a"
      cidr_block        = "10.0.0.0/28"
      availability_zone = "us-east-1a"
    },
    {
      name              = "private-1b"
      cidr_block        = "10.0.1.0/24"
      availability_zone = "us-east-1b"
    },
    {
      name              = "public-1a"
      cidr_block        = "10.0.2.0/24"
      availability_zone = "us-east-1a"
    },
    {
      name              = "public-1b"
      cidr_block        = "10.0.3.0/24"
      availability_zone = "us-east-1b"
    }
  ]
}


output "private1a" {
  value = module.security_subnets.subnet_id[0]
} 
output "private1b" {
  value = module.security_subnets.subnet_id[1]
} 
output "public1a" {
  value = module.security_subnets.subnet_id[2]
} 
output "public1b" {
  value = module.security_subnets.subnet_id[3]
} 

module

locals {
  subnets = {
    for i in var.subnets :
    i.name => i
  }
}

resource "aws_subnet" "main" {
  for_each          = local.subnets
  vpc_id            = var.vpc_id
  cidr_block        = each.value.cidr_block
  availability_zone = each.value.availability_zone

  tags = merge(
    {
      "Name" = format("%s", each.value.name)
    }
  )

}

output subnet_id {
  value = values(aws_subnet.main)[*].id
}

2 Answers

As a general rule, its not a good practice to depend on the order of items returned, as this can lead to future issues if you decide to add something in between or remove something.

Nevertheless, in your case the order of items return is well defined. Since you are using values the order is:

The values are returned in lexicographical order by their corresponding keys, so the values will be returned in the same order as their keys would be returned from keys.

Using map structure instead of an array for subnets variable should return values by indexes and then resolve the problem of mixing the values if order is changed (like on of the items is removed):

module "subnets" {
  source = "./modules/subnets/"
  vpc_id = aws_vpc.main.id
  subnets = {
    "private-1a" = {
      name              = "private-1a"
      cidr_block        = "10.0.0.0/28"
      availability_zone = "us-east-1a"
    },
    "private-1b" = {
      name              = "private-1b"
      cidr_block        = "10.0.1.0/24"
      availability_zone = "us-east-1b"
    },
    "public-1a" = {
      name              = "public-1a"
      cidr_block        = "10.0.2.0/24"
      availability_zone = "us-east-1a"
    },
    "public-1b" = {
      name              = "public-1b"
      cidr_block        = "10.0.3.0/24"
      availability_zone = "us-east-1b"
    }
  }
}


output "private1a" {
  value = module.security_subnets.subnet_id["private-1a"]
} 

// and so on

Related