AWS Launch Template with Dynamic User Data

Want to load user data into an AWS Launch Template without fussing around with base64 encoding? Here’s a handy trick using Terraform and the filebase64 function. Kudos to Loren Gordon for the tip to use a function instead of a data object!

In my scenario, I use a launch template to create any number of Amazon EC2 instances to act as Kubernetes nodes as part of my Certified Kubernetes Administrator Study Guide efforts. It makes sense to bootstrap the instances with a bash script that disables swap, sets up the bridge network, installs Docker, installs all of the Kubernetes dependencies, and then creates desired aliases. Here is the script and the documentation, for those interested.

I use Terraform to do the heavy lifting. And while I could, and originally did, embed the base64 encoded string into the launch template’s user_data field, this comes with a few problems:

  • I can’t read raw base64 encoded text because I’m not a robot.
  • Inserting stateful data into a Terraform configuration is an anti-pattern; state should be abstracted and iterated independently.
  • Editing, linting, and reviewing encoded user data is tricky because of the required decoding, making a change, and then encoding process. 😟

My colleague and highly skilled engineer, Mike Preston, suggested splitting out the user data for these very same reasons.

Quick Reminder: I live stream ever week on Tuesdays and Thursdays on Twitch! Drop a follow, enable notifications, and come join in the adventure.

Creating a Data Object

Thus, I split out user data into a file named user_data.sh and rely on the filebase64 function to retrieve the script contents.

user_data = filebase64("user_data.sh")

To my delight, this retrieves the raw content and encodes the results using base64 without the need to use a local_file data object. This obviates the need to do any encoding on the client side. Let’s go!

Using the Data Object for User Data

Setting the value of the user_data parameter to filebase64("user_data.sh") connects the dots between my normal bash script and the base64 encoded string that AWS needs.

resource "aws_launch_template" "cka-node" {
  name                   = var.instance-name
  image_id               = "ami-07a29e5e945228fa1"
  instance_type          = var.instance-type
  <snip>
  user_data = filebase64("user_data.sh")
}

Now I am able to work on the script independently, without any decoding required, and Terraform handles all of the launch template versioning and updates for me.

Later, when the upstream Auto Scaling group uses the launch template to create or rollout new instances, the script is automatically embedded into user_data. Easy!

Validating User Data

One way that user data can be retrieved and validated is from within the instance. Think of this as a sort of sanity check. The link-local address shown below is used to perform retrieval.

curl http://169.254.169.254/latest/user-data/

Next Steps

Please accept a crisp high five for reaching this point in the post!

If you’d like to learn more about Infrastructure as Code, or other modern technology approaches, head over to the Guided Learning page.

If there’s anything I missed, please reach out to me on Twitter or catch my next Twitch live stream every week on Tuesday and Thursday. Cheers! 🙂