Your submission was sent successfully! Close

You have successfully unsubscribed! Close

Build your CloudFormation Templates with the latest Ubuntu AMI

1. Overview

If you are already familiar with CloudFormation Templates, you know that it is a common practice to create a mapping with hard-coded AMI IDs with their respective regions for launching instances. While the unique AMI ID per region is solved, the Always get the latest AMI is not.

In this quick tutorial, I would like to show how to ensure you always get the latest AMI ID for any given version of Ubuntu.

Why is this important? Simple, it gives you faster boot times and less reboots. If the AMI you are choosing is an old build, the update & upgrade process will not only take longer, but it could also lead you into a reboot if the package being updated is none other than the kernel itself.

In short, we will be querying the SSM parameter store for getting the latest AMI ID.

What you’ll need

  • An AWS account
  • A basic knowledge of EC2
  • A basic knowledge of AWS CloudFormation Templates

What you’ll learn

How to avoid hardcoding AMI IDs in your CloudFormation templates.


2. Let’s start

Instead of building a list of mapped and hard-coded AMI IDs into your template, you can query the SSM parameter store for getting the latest version in a very consistent way.

For Regular (free) Ubuntu LTS

From the AWS Documentation, “Each Amazon Linux AMI now has its own Parameter Store namespace that is public and describable. Upon querying, an AMI namespace returns only its regional ImageID value.”

If you want to use the regular Ubuntu server version in your CloudFormation template, add the following to your Parameters section. You can change the Ubuntu version (e.g. jammy, focal, bionic) and also the architecture (ARM64 or AMD64):

    LatestAmiId:
                Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
                Default: '/aws/service/canonical/ubuntu/server/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id'

When launching the instance, in the Resources section, you can specify the AMI ID as it follows:

    MyInstance:
        Type: AWS::EC2::Instance
        Properties:
            ImageId: !Ref LatestAmiId

For Ubuntu Pro

For Ubuntu Pro, it is slightly different, because since it is a marketplace product the querying pattern changes: you need to query marketplace with a specific product ID:

Name Architecture identifier
Ubuntu Pro FIPS 16.04 LTS amd64 prod-hykkbajyverq4
Ubuntu Pro FIPS 18.04 LTS amd64 prod-7izp2xqnddwdc
Ubuntu Pro FIPS 20.04 LTS amd64 prod-k6fgbnayirmrc
Ubuntu Pro 14.04 LTS amd64 prod-7u42cjnp5pcuo
Ubuntu Pro 16.04 LTS amd64 prod-f6ogoaqs7lwre
Ubuntu Pro 18.04 LTS amd64 prod-jlgu4232gpnwa
Ubuntu Pro 20.04 LTS amd64 prod-3sk4unyn4iwqu
Ubuntu Pro 22.04 LTS amd64 prod-lfutkwiaknxsk

Note:
For ARM (Graviton) and other Ubuntu versions, please visit https://ubuntu.com/server/docs/cloud-images/amazon-ec2

So the parameter in your CloudFormation template will look like this (change <product-id> with one from the table above):

    LatestAmiId:
                Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
                Default: '/aws/service/marketplace/<product-id>/latest'

Note:
Remember that if you are launching a marketplace product, you will need to subscribe to it, even if the product is completely free of charge.

For EKS Ubuntu LTS

For Ubuntu-EKS AMI IDs, the search is as follows (you can replace Ubuntu version, EKS version and architecture):

    LatestAmiId:
                Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
                Default: '/aws/service/canonical/ubuntu/eks/20.04/1.23/stable/current/amd64/hvm/ebs-gp2/ami-id'

3. Putting all together

This is how it would look like in a very basic template for Ubuntu LTS:

AWSTemplateFormatVersion: 2010-09-09
Description: Launch EC2 instance with the latest Ubuntu AMI

Parameters:
    AvailabilityZone:
        Type: AWS::EC2::AvailabilityZone::Name
    LatestAmiId:
                Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
                Default: '/aws/service/canonical/ubuntu/server/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id'
    KeyPair:
        Description: Amazon EC2 Key Pair used to ssh to the cluster nodes
        Type: "AWS::EC2::KeyPair::KeyName"
    InstanceType:
        Type: String
        Default: t2.micro
        AllowedValues:
            - t2.micro
            - t2.medium
            - t2.large
            - t2.xlarge
            - t2.2xlarge

Resources:
    MyInstance:
        Type: AWS::EC2::Instance
        Properties:
            ImageId: !Ref LatestAmiId
            InstanceType: !Ref InstanceType
            AvailabilityZone: !Ref AvailabilityZone
            KeyName: !Ref KeyPair
            SecurityGroupIds:
                - !Ref MyBasicSecurityGroup

    MyBasicSecurityGroup:
        Type: AWS::EC2::SecurityGroup
        Properties:
            GroupName: "A very basic Security group"
            GroupDescription: "Allows SSH inbound traffic"
            SecurityGroupIngress:
                - IpProtocol: tcp
                  FromPort: 22
                  ToPort: 22
                  CidrIp: 0.0.0.0/0

Outputs:
    InstanceIP:
        Value: !GetAtt MyInstance.PublicIp
        Description: Instance public IP

4. That’s all folks!