2

I wanted to create a terraform EC2 Spot instance on AWS using the terraform module terraform-aws-ec2-instance.

Upon creation, I noticed the EC2 instance was missing the tags I had assigned to it. So I started digging and came up with this MCVE:

provider "aws" {
  region = local.region
}

locals {
  name   = "example-ec2-complete"
  region = "us-east-2"
}

module "ec2_instance" {
  source  = "terraform-aws-modules/ec2-instance/aws"
  version = "4.3.0"

  name = "spot-instance"

  create_spot_instance = true
  spot_price           = "0.60"
  spot_type            = "persistent"

  ami                    = "ami-05502a22127df2492"
  instance_type          = "t2.micro"
  subnet_id = element(module.vpc.private_subnets, 0)

  tags = {
    Name = "Test"
    Terraform   = "true"
    Environment = "dev"
  }
}

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"
  name = "my-openshift-test-vpc"
  cidr = "10.0.0.0/16"
  azs             = ["us-east-2a", "us-east-2b", "us-east-2c"]
  private_subnets = ["10.0.1.0/24"]
  private_subnet_names = ["my-openshift-private-subnet-one"]
}

resource "local_file" "ec2_instance_id" {
  content  = "${module.ec2_instance.spot_instance_id}"
  filename = "instance_id.txt"
}

You can launch the above example and retrieve the assigned tags with the following bash one-liner (you must launch it 2 times as the instance id isn't immediately loaded by Terraform and it will fail the first time):

terraform init && terraform apply && aws ec2 describe-tags --filters "Name=resource-id,Values=`cat instance_id.txt`"

Upon creation, we can see the instance has no tags: screenshot of the aws console showing no tags are present on the ec2 instance

I tried the oldest and newest versions of the module, to no avail:

  #version = "~> 3.0"
  #version = "4.3.0"
  #version = "2.0"
  #version = "2.21.0"
  #version = "3.0.0""

I later found out this happens because the tags DO get added, but to the spot Instance Request, as can be shown with terraform show/terraform plan:

$ terraform show
# local_file.ec2_instance_id:
resource "local_file" "ec2_instance_id" {
...
# no tags in the output!
...
}

# module.ec2_instance.aws_spot_instance_request.this[0]:
resource "aws_spot_instance_request" "this" {
...
    tags                           = {
        "Environment" = "dev"
        "Name"        = "Test"
        "Terraform"   = "true"
    }
    tags_all                       = {
        "Environment" = "dev"
        "Name"        = "Test"
        "Terraform"   = "true"
    }
...

image showing that the tags are assigned to the Spot Request and not the EC2 instance

I couldn't find any official explanations on this, but, reading from the AWS Documentation, it is said here:

When you tag a Spot Instance request, the instances and volumes that are launched by the Spot Instance request are not automatically tagged. You need to explicitly tag the instances and volumes launched by the Spot Instance request. You can assign a tag to a Spot Instance and volumes during launch, or afterward.

Therefore I assume the reason this cannot be implemented in Terraform is because AWS doesn't support tagging EC2 Spot Instances in an idempotent way. This is also being discussed here and here.

As a workaround, I created the tag manually after getting the instance ID:

resource "aws_ec2_tag" "example" {
  #resource_id = aws_vpn_connection.example.transit_gateway_attachment_id
  resource_id = module.ec2_instance.spot_instance_id
  key         = "Name"
  value       = "Test"
}

The above code can be directly added at the bottom of the original example.

Are there other better Terraform constructs that would allow this given the context? It seems fundamentally wrong to create the tags in a separate place from where the instance is created, as well as being error-prone considering the way Spot Instances are instantiated on AWS.

Thanks!

2 Answers 2

2

As there is no way to add the tags along with resource creation, one way is:

  • Create a variable with default tags
variable "tags" {
  type        = map(string)
  default     = {
    Name        = "Test"
    Terraform   = "true"
    Environment = "dev"
  }
}
  • Create a resource using for_each to reuse
resource "aws_ec2_tag" "example" {
  for_each    = var.tags
  resource_id = module.ec2_instance.spot_instance_id
  key         = each.key
  value       = each.value
}
Sign up to request clarification or add additional context in comments.

Comments

2

With the new AWS / Terraform API for Spot Instance Requests, you can specify the tags directly. For example:

resource "aws_instance" "al2023_arm" {
  ami           = "ami-0abdbf3b4977a9632"
  instance_type = "t4g.small"
  key_name      = "key"
  tags = {
    Name = "al2023_arm"
  }
  instance_market_options {
    market_type = "spot"
    spot_options {
      instance_interruption_behavior = "stop"
      spot_instance_type             = "persistent"
    }
  }
}

But this leaves the spot instance request without a tag, so you also need to:

resource "aws_ec2_tag" "name_the_spot_instance_request" {
  resource_id = aws_instance.al2023_arm.spot_instance_request_id
  key         = "Name"
  value       = "al2023_arm"
}

3 Comments

Just tested it and yes, this does work! It doesn't add the tags on the Spot Request though, so now the question is inversed :) How do you tag a Spot Request AND an EC2 instance? Since it is possible to add a tag this way, but only to the instance, then I believe we can say that github.com/terraform-aws-modules/terraform-aws-ec2-instance doesn't support adding tags to instances yet.
I didn't bother to check my spot requests, but you are right! Personally, I can live with my spot requests having only the default tags, but this is really disappointing.
Shamelessly stolen the workaround from Dourado's answer. ;-)

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.