Terraform in AWS: use AMI Names not IDs - and is Terraform the right answer still?

In AWS the AMI ID for a particular VM image is unique to that region. But it’s name is consistent across AWS. When building infra-as-code (such as terraform) use the name, not the ID. And some musings on terraform. Is it still the right answer for infra-as-code?

It’s been awhile since I did anything real with terraform. My teams use it heavily at work but a lot of my personal projects lately have not been cloud based. So yesterday I took a day off (for my own mental health) and did some things that make me happy. Including some coding. Which included refreshing a bit on terraform.

One thing that annoys me about Cannonical is their insistence on using only silly code names (e.g. trusty, precise, focal, etc.) and not including the release numbers (e.g. 16.04, 18.10, 20.04, etc.). I can’t remember the release names, but the numbers are date based and easy to remember.

AMI IDs

To launch an EC2 instance you need to tell AWS what OS to launch. This is the AMI ID. Lots of helpful web pages will point you at the Cannonical lookup page where you can easily find these. But AMI IDs are not consistent across regions. A basic latest 20.04 amd64 Ubuntu image in us-west-2 is ami-004e1dc83789471c1 but in us-east-2 it’s ami-0769e4bb31e1db8e4. If you aspire to DRY principles then having to change a variable to a script just because I want to run the same thing in a different region is really lame.

AMI Names

Alas, I learned something yesterday: the NAME of the image is consistent. Now, if you look at the Cannonical lookup page it tells you the “name” is basically their release code name (e.g. trusty, precise, focal, etc). Not really helpful. Back to Ubuntu’s reliance on silly names not numbered releases.

How do you find the names? AWS CLI to the rescue:

aws ec2 describe-images   --region us-west-2 --filters "Name=name, Values=*ubuntu-*-20.04-amd64-server-*" |grep \"Name
            "Name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20200528",
            "Name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20200423-aced0818-eef1-427a-9e04-8ba38bada306-ami-068663a3c619dd892.4",
            "Name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20200423",
            "Name": "ubuntu-pro/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20200423-ae7ed378-8838-4fcf-842d-d1d09b34f116-ami-0118f3de163338756.4",            

The key is the filter:

 --filters "Name=name, Values=*ubuntu-*-20.04-amd64-server-*"

If I was looking for another Ubuntu release - or perhaps for arm64 not amd64 - I could change that and find the name. From what I can tell there’s a naming convention they follow, but I’m a “trust but verify” kind of person.

OK. Now I know I have a name that I can look up. The CLI gave me four options.

Don’t Need to Know the Full Names!

But I don’t actually need to know the full names. We can do the same kind of search right in terraform, and luckily terraform provides a way to select the most recent AMI, conveniently with this snip:

  most_recent = true

Here is a sample terraform that will launch an EC2 instance using the latest Ubuntu 20.04 image:

provider "aws" {
 region = "us-west-2"
}

data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["*ubuntu-focal-20.04-amd64-server-*"]
  }

  filter {
        name   = "virtualization-type"
        values = ["hvm"]
  }
  owners = ["099720109477"] # Canonical

}

resource "aws_instance" "simple" {
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t2.micro"

  tags = {
    Name = "HelloWorld"
  }
}

output "image_id" {
    value = "${data.aws_ami.ubuntu.id}"
}

Is Terraform Still the Right Choice for Infra-As-Code?

Of course, this is just a toy. In a production setting I would want to create a VPC, set security groups, etc. I spent some time yesterday with this. Terraform is technically the Hashicorp Configuration Language which isn’t hard to learn for basics but non-trivial for large deployments. I think it’s time to revisit the state of all this stuff for infra-as-code. I’m torn. I’m all in for infra-as-code, don’t get me wrong. But terraform is a programming language all of it’s own now, and the more you invest in that the farther you get from regular team software Engineers doing the infra deployments. This tends to push the “DevOps” team towards a separate group (which is happening in my day job) which is the wrong direction in my opinion. If your regualr team that codes in golang (one hopes) or java (run away) or node or ruby or whatever… if that team has to learn a complex new language to deploy their cloud then it adds a LOT to the cognitive load. They are already writing helm charts, probably, so yet another language might be tipping the boat over. This is a good reason to wonder again if Pulumi might be worth another look. Or go back to pure python with the boto3 libraries, or golang with the AWS API. Not cloud-agnostic, but with the deep hooks the cloud vendors have into all of us is there any such thing as multi-cloud? Really? Even terraform is so cloud-specific it’s another code base - one per cloud. This is something for further study.