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.
joshk132
  • 465
  • 10
  • 22

1 Answers1

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.

Martin Atkins
  • 2,241
  • 9
  • 10