Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

359
Views
Terraform looping with for_each

How would one get the subnet id if you're using for_each as opposed to count? In my case, I'm doing something like this

resource "aws_instance" "k8s" {
    for_each = var.profiles

    ami = data.aws_ami.latest-ubuntu.id
    instance_type = "t2.medium"
    iam_instance_profile = "${each.value}"
    subnet_id = ??????????????
    vpc_security_group_ids = [var.security_group]
    key_name = var.keyname
    
    connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file(var.private_key_path)

  }
    tags = {
     Name = "${each.key}"
  }
}

And this is because I'm creating similar instances but need to assign them different instance profiles.

Ideally, I'd have done something like

subnet_id = element(var.subnets, count.index )

to place the instances in different subnets but I don't think count and for_each can be used in the same block definition.

I have to subnets and 4 instances and would like to loop through the subnets, placing each instance in one.

Any ideas, please? Thanks.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

If var.profiles is a list, you can use each.key to get the index of each element.

over 4 years ago · Santiago Trujillo Report

0

you can provide you profiles as a list of objects :

for exemple :

 variable "profiles" {
  type = list(object({
    name = string
    key =  string
  }))
  default = 
    [
      {name = "exemple" , key = "exemplekey" },
      {name = "exemple2" , key = "exemplekey2" }
    ]
}

like this the each.key will containes the index of the element in the list and each.value will contain the object {name,key}

so your code would be like this

resource "aws_instance" "k8s" {
    for_each = var.profiles
    ami = data.aws_ami.latest-ubuntu.id
    instance_type = "t2.medium"
    iam_instance_profile = each.value
    subnet_id = element(var.subnets , each.key)
    vpc_security_group_ids = [var.security_group]
    key_name = var.keyname
    connection {
    type        = "ssh"
    host        = self.public_ip
    user        = "ubuntu"
    private_key = file(var.private_key_path)

  }
    tags = {
     Name = each.value.key
  }

}

over 4 years ago · Santiago Trujillo Report

0

A good general strategy with resource for_each is to design the data structure you pass it so that each.value contains all of the per-instance data you need inside the resource block.

In this case, that means that the for_each expression for your aws_instance resource would be a map of objects where each object has both an instance profile and a subnet id.

One way to achieve that would be to write a for expression that transforms var.profiles (which is presumably a set(string) value) into a map of objects that would get the result you want. For example:

resource "aws_instance" "k8s" {
  for_each = {
    # This assigns a subnet to each of the profiles
    # by first sorting them by name to produce a list
    # and then selecting subnets based on the order
    # of the sort result.
    for i, profile_name in sort(var.profiles) : profile_name => {
      iam_instance_profile = profile_name
      subnet_id            = element(var.subnets, i)
    }
  }

  ami                    = data.aws_ami.latest-ubuntu.id
  instance_type          = "t2.medium"
  iam_instance_profile   = each.value.iam_instance_profile
  subnet_id              = each.value.subnet_id
  vpc_security_group_ids = [var.security_group]
  key_name               = var.keyname
}

Using count.index with the element element relied on each item having its own index, but that isn't the case for a set of strings so in the above I used sort to convert to a list under the assumption that for this situation it doesn't really matter which subnet is assigned to each instance as long as the instances end up roughly evenly distributed between the subnets.

However, there is a big implication of that to keep in mind: if you add a new item to var.profiles later then it may cause the subnet_id for existing instances to be reassigned, and would thus require those instances to be recreated. If you don't want that to be true then you'll need to make the selection of subnet per profile more explicit somehow, which could be done by making var.profiles be a list(string) instead of a set(string) and then documenting that new profiles should only be added to the end of that list, or alternatively you could move the decision up into the caller of your module by making var.profiles itself a map of objects where the caller would then specify one subnet per profile:

variable "profiles" {
  type = map(object({
    subnet_id = string
  }))
}

In that case, your resource block would become simpler because the variable value would already be of a suitable shape:

resource "aws_instance" "k8s" {
  for_each = var.profiles

  ami                    = data.aws_ami.latest-ubuntu.id
  instance_type          = "t2.medium"
  iam_instance_profile   = each.key
  subnet_id              = each.value.subnet_id
  vpc_security_group_ids = [var.security_group]
  key_name               = var.keyname
}
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!