AWS Launch templates in Terraform

In late 2017 AWS has added “launch templates” in their EC2 ecosystem as complement or replacement to “launch configurations”.
This resource is a bit more complex but adds a lot of possibilities to handle many parts of what you might want to configure in an AutoScalingGroup.

It stayed under our radar for quite some time, but with regular wrangling with new versions of user_data in our launch configuration, we decided to look at them.
Here is a quick overview of what to expect in launch templates and how to setup one as a replacement of a launch configuration. All of this through the eyes of Terraform.

What’s been added

Launch templates are very similar to launch configurations yet they provide a deeper and wider way to configure the instances that are launched within an auto scaling group.

You will roughly find what you are used to find in launch configurations, but with more details. For example you can have a way to define block device mappings for the instances in the ASG, capacity reservations, elastic gnu specs, license configuration, RAM disk id. All that along the more classic name, description, instance_type, and so on.

Defining a template with terraform and using it

We had regular issues with having to juggle launch configurations linked to AutoScalingGroups whenever we had to update launch configurations. And we thought that was dangerous and could be done better.
Launch templates support versioning so the idea is that there is no need to create a new Launch Template whenever you update the user data for example.

In Terraform you can almost copy and paste your launch configuration resource content into a launch template resource. There are a few tricks.
Here is an example of migrating a minimal launch configuration into a launch template.

Launch configuration

resource "aws_launch_configuration" "stats-reader" {
  image_id             = "${data.aws_ami.ubuntu-1804.id}"
  instance_type        = "t2.small"
  iam_instance_profile = "profile-1"
  security_groups      = ["<sg-1>"]
  key_name             = "key-1"
  user_data            = <<EOF
#!/bin/bash -xe
apt-get update -y
apt-get install -y awscli docker.io jq
EOF
}

Launch template

data "template_file" "user_data_hw" {
  template = <<EOF
#!/bin/bash -xe
apt-get update -y
apt-get install -y awscli docker.io jq
EOF
}

resource "aws_launch_template" "hello-world" {
  name = "hello-template"
  disable_api_termination = true
  iam_instance_profile {
    name = "profile-1"
  }
  image_id = "${data.aws_ami.ubuntu-1804.id}"
  instance_initiated_shutdown_behavior = "terminate"
  instance_type = "t2.small"
  key_name = "key-1"
  vpc_security_group_ids = ["<sg-1>"]

  user_data = "${base64encode(data.template_file.user_data_hw.rendered)}"
}

The main difference to note is actually around the user_data : it doesn’t work the same way.
In a launch configuration you can use a block of UTF-8 encoded user data directly as a value for the user_data attribute.
In a launch template you just cannot. Instead you need to use a template_file resource that you fill with the block of user_data as template. And then, within the launch template you can call that data.template_file with the important rendered attribute within a base64encode function call. That will render the user_data block and encode it in base64.
This is tricky because, contrary to the launch configuration there is nothing in the name of attribute that tells you to use base64.
Source : Launch Template not accepting User Data · Issue #5530 · terraform-providers/terraform-provider-aws · GitHub

Attaching to an ASG

The attachement to an ASG is also different. As launch_templates support multiple versions the syntax is different than with launch_configurations :

resource "aws_autoscaling_group" "hello-world-blue" {
  launch_template = {
    id      = "${aws_launch_template.hello-world.id}"
    version = "$$Latest"
  }
  # more content...
}

As described in AWS: aws_launch_template - Terraform by HashiCorp there are three options for this : version number, $Latest, or $Default. It defaults to … $Default if no version is provided.

A word on usage

As said on the box there is no more need to juggle with different launch configuration resources in terraform. If you have specified “$$Latest” in the version attribute of the launch template link in the ASG then every time you update the launch template the ASG will get the updated version through your terraform apply runs.

Conclusion

This was easier than anticipated and will avoid us quite a few headaches with all ASG.
The more advanced attributes we haven’t covered are additional niceties that we might look into soon, yet for most the game doesn’t change much. Launch templates (and launch configuration) provide key steps to define how instances are launched and initially setup within AutoScalingGroups. The other part of the game is directly within the AutoScalingGroups with all the spot, on demand and reservation bits. More on that later.

Have fun.

Need help ?

We specialise in helping small and medium teams transform the way they build, manage and maintain their Internet based services.

With more than 10 years of experience in running Ruby based web and network applications, and 6 years running products servicing from 2000 to 100000 users daily we bring skills and insights to your teams.

Wether you have a small team looking for insights to quickly get into the right gear to support a massive usage growth, or a medium sized one trying to tackle growth pains between software engineers and infrastructure : we can help.

We are based in France, EU and especially happy to respond to customers from Denmark, Estonia, Finland, France, Italy, Netherlands, Norway, Spain, and Sweden.

We can provide training, general consulting on infrastructure and design, and software engineering remotely or in house depending on location and length of contract.

Contact us to talk about what we can do : sales@imfiny.com.