3

I am trying to create an ec2 instance that will hold my Jenkins server. I want this to be in a private subnet which I created using a for each loop. Below is my subnet for each loop and my ec2 instance resource. I have also included the error message. Idea is I want it in the first private subnet.

ec2 instance

resource "aws_instance" "jenkins" {
  ami           = "${var.ubuntuAMI}"
  instance_type = "t3.micro"
  availability_zone = "us-east-1a"
  key_name = "me"
  monitoring = true
  vpc_security_group_ids = [aws_security_group.ssh_access.id]
  disable_api_termination = true
  subnet_id = "${aws_subnet.private[each.key]}"

  tags = {
    Name = "Jenkins"
  }
}

subnet resource

resource "aws_subnet" "private" {
  for_each = var.subnet_numbers_private

  vpc_id            = aws_vpc.Main_VPC.id
  availability_zone = each.key
  cidr_block        = cidrsubnet(aws_vpc.Main_VPC.cidr_block, 8, each.value)
  tags = {
      Name = "Private-${each.key}"
  }
}

variable used by the subnet loop

variable "subnet_numbers_private" {
  description = "Map for private subnets"
  default     = {
    "us-east-1a" = 1
    "us-east-1b" = 2
    "us-east-1c" = 3
  }
}

error message seen when doing a terraform plan

The "each" object can be used only in "resource" blocks, and only when the
"for_each" argument is set.

1 Answer 1

1

When you want to refer to a specific instance of a resource that has multiple instances due to using for_each, you need to include the specific key of the instance you want in your references:

  subnet_id = aws_subnet.private["us-east-1a"].id

If you just want to select any one subnet from the list, without needing to specify its name directly, you can use an expression like the following to select a single key by sorting the keys lexically and taking the first one:

  subnet_id = aws_subnet.private[keys(aws_subnet.private)[0]].id

However, beware that if you do the above then any later changes to var.subnet_numbers_private that affect which subnet sorts first will cause a different subnet id to be selected, which will then in turn cause Terraform to plan to replace your instance to move it into a new subnet.


From the way you wrote the question it sounds like you want just one instance in a single subnet rather than one instance per subnet, but if you did want an instance per subnet then you can achieve that by putting for_each in the aws_instance resource too, using the subnet resource itself (represented in expressions as a map) as the repetition expression:

resource "aws_instance" "jenkins" {
  for_each = aws_subnet.private

  # ...
  subnet_id = each.value.id
  # ...
}

In this latter case, the aws_instance.jenkins instances will also be identified by the keys you selected for the subnets, so you'd have instances with addresses like aws_instance.jenkins["us-east-1a"] and aws_instance.jenkins["us-east-1b"] so both you and Terraform can see which instance belongs to which subnet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.