Terraform-AWS random instance names in Auto Scaling groups and tag propagation to volumes

When you use Auto Scaling groups to deploy instances, every instance is named the same if you setup the Name tag inside aws_autoscaling_group to be propagated at launch. Last few days I've been looking how to make instance names, that is their Name tag, get random values. There is no built-in way of doing this as far as I saw.

Also another problem is that you want the same tag/tags on volumes of that same instance. It is absolutely necessary if you use cost allocation tags so you can track disk costs across your environment.

All this can easily be solved with script and user_data inside aws_launch_template resource.

Prerequisites

  1. Instance needs to have IAM profile attached that is allowed to describe instance to get its own ID and write tags of instance and volume resources. Here is the example policy you can attach to your role
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:DeleteTags",
                "ec2:CreateTags"
            ],
            "Resource": [
                "arn:aws:ec2:*:${ACCOUNT_ID}:instance/*",
                "arn:aws:ec2:*:${ACCOUNT_ID}:volume/*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"
        }
    ]
}
policy needed for getting and creating tag on instance and its volumes

2. EC2 instance ami image needs to have AWS CLI tool installed. If you are using for example Amazon Linux 2 AMI you are good to go

Amazon Linux 2 AMI

Terraform code

So with all prerequisites taken care of you need to adjust resources in your Terraform code. First create script in the root module, or any child module you are using for TF deployment. Dont forget to adjust your region or even better create variable for it. I made it like this just to speed things up.

#!/bin/bash
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
ROOT_VOLUME_ID=$(aws ec2 describe-instances --region eu-central-1 --instance-id $INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)
CURRENT_TAG=$(aws ec2 --region eu-central-1 describe-tags --filters Name=key,Values=Name Name=resource-id,Values=$INSTANCE_ID | grep Value | awk -F'"' {'print $4'})
SUFFIX=$(head /dev/urandom | tr -dc a-z0-9 | head -c 8)
aws ec2 create-tags --region eu-central-1 --resources $INSTANCE_ID --tags Key=Name,Value=$CURRENT_TAG-$SUFFIX
aws ec2 create-tags --resources $ROOT_VOLUME_ID --region eu-central-1 --tags Key=Name,Value=$CURRENT_TAG-$SUFFIX
user-data.sh

So this code

  • gets instance id from the running instance,
  • get its root volume id
  • gets current value from Name tag
  • created random string 8 char long
  • creates new Name tag from current value + random string generated earlier
  • create the same tag for its volume

Now edit TF code. Maker sure you use image with aws tools installed, setup user_data with script created and be sure to include that aws_launch_template in your aws_autoscaling_group resource and create Name tag that will be used like prefix in final Name tag that instance will set automatically for itself and its root volume.

resource "aws_launch_template" "example" {
...
  image_id      = "ami-0649a2ac1437cf3b7"
...
  iam_instance_profile {
    name = "instance_role_created_in_prerequisites"
  }

  user_data = base64encode(file("user-data.sh"))
  }
  
resource "aws_autoscaling_group" "bar" {
...
  desired_capacity   = 2
  max_size           = 5
  min_size           = 1

  launch_template {
    id      = aws_launch_template.example.id
    version = "$Latest"
  }

  tag {
    key                 = "Name"
    value               = "asg-instance"
    propagate_at_launch = true
  }
  }
main.tf

Result

Now when you apply that code you will get instances named like in this example