Finding an AWS EC2 AMI with the AWS PowerShell SDK

Problem Statement

If you’ve ever launched an Amazon EC2 instance (aka. virtual machine), you’re probably familiar with the concept of an Amazon Machine Image (AMI). AMIs are simply virtual machine images that you can launch your EC2 instance with.

Because anyone can publish a custom AMI, and because AWS has a massive marketplace of vendor-provided AMIs, it can be very challenging to find a specific AMI. For example, let’s say that your goal is to find the latest Ubuntu 18.04 LTS Bionic Beaver AMI available, or maybe you want the latest Debian Buster Linux image. Finding those individual AMIs, among thousands of AMIs in the marketplace, can be tough.

Basic Searching for AMIs with PowerShell

Amazon EC2 exposes a describe-images API that can be accessed using any of the AWS SDKs. For example, you can use the Get-EC2Image command in the AWS PowerShell SDK to search for images that match a certain set of criteria. You’ll want to refer back to the describe-images REST API documentation to understand which specific set of AMI filters are supported, however.

If you run Get-EC2Image without any parameters, it will take a long time to execute (35 seconds in my one-off test), and you will get the entire library of images returned to you. Although you can sift through this list yourself on the client side, this isn’t an efficient process. It is arguably easier to use the server-side filtering options available in the API.

# Measure how long it takes to populate a variable with the full image library
Measure-Command -Expression { $ImageList = Get-EC2Image }

<# Result:
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 35
Milliseconds      : 870
Ticks             : 358707539
TotalDays         : 0.000415170762731481
TotalHours        : 0.00996409830555556
TotalMinutes      : 0.597845898333333
TotalSeconds      : 35.8707539
TotalMilliseconds : 35870.7539
#>

# Now let's see how many AMIs were returned
$ImageList.Count

# Result: 105840
# Wow, that's a lot of images!

Filtering AMI Results with PowerShell

The Get-EC2Image command exposes a -Filter parameter, which accepts an array of [Amazon.EC2.Model.Filter] objects as input. The filter name and value are defined in the describe-images documentation that we looked at earlier. Let’s take a look at how to construct a filter, based on the image name, and pass it into the command.

# Start by creating a generic ArrayList, as we can define more than one filter
$Filter = [System.Collections.ArrayList]::new()

# Add a new filter, based on the image name.
# NOTE: The $null = ... simply suppresses the output from the Add() method
$null = $Filter.Add([Amazon.EC2.Model.Filter]::new('name', '*bionic*'))

# Obtain a list of images, using the server-side filter
$ImageList = Get-EC2Image -Filter $Filter

# Examine the results
$ImageList.Count

# Result: 924

That is a much smaller result set than we originally received, without using a filter! To quantify it, that’s about a 11400% reduction in our initial result set!

Adding an Owner Filter

However, we only want one image, not 924. We need to do more filtering to get there. Let’s try adding another filter, to limit the results to only the AWS Marketplace images.

# Start by creating a generic ArrayList, as we can define more than one filter
$Filter = [System.Collections.ArrayList]::new()

# Add a new filter, based on the image name.
# NOTE: The $null = ... simply suppresses the output from the Add() method
$null = $Filter.Add([Amazon.EC2.Model.Filter]::new('name', '*bionic*'))
$null = $Filter.Add([Amazon.EC2.Model.Filter]::new('owner-alias', 'aws-marketplace'))

# Obtain a list of images, using the server-side filter
$ImageList = Get-EC2Image -Filter $Filter

# Examine the results
$ImageList.Count

# Result: 17

Wow, by simply adding an AWS Marketplace filter, we’ve experienced a further 5400% reduction in the number of image results. Now that we’re under ~30 results, we can start to visually examine the images on-screen, and see if we can narrow down what we’re looking for. Let’s start by taking a look at the results in more detail. We’ll pipe the $ImageList variable into PowerShell’s built-in Select-Object command, so we can view some of the key properties about each AMI.

$ImageList | Select-Object -Property CreationDate, ImageId, Name

<# Result:

CreationDate             ImageId               Name
------------             -------               ----
2019-11-18T08:18:40.000Z ami-0325d4fb7241c0a91 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-arm64-server-20191113-10d15682-9d0b-41dd-93c2-5e7de4f74165-ami-0ddcb9754ba4ffda3.4
2019-02-26T11:21:58.000Z ami-041261af8354d4a0b ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190212.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0a313d6098716f372.4
2019-02-26T11:25:22.000Z ami-05e0b411893499c11 ZeroC Ice 3.7 AMI on ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd6-5fd4acae-d28b-4c90-9f32-e6f0b4ee34a0-ami-039d6d7e077ed24e6.4
2019-04-19T08:29:57.000Z ami-0643ea9028da5ede3 hapee-ubuntu-bionic-amd64-hvm-1.8r2-20190418-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-06a9997a1a81e688c.4
2019-06-21T07:08:03.000Z ami-06ad92f74f2c20787 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190617-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-095192256fe1477ad.4
2019-02-27T08:49:40.000Z ami-06d5223e6851b3502 ZeroC Ice 3.7 AMI on ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-arm6-80c8a0ac-4a5f-4fb8-bf18-f3b2eaf43e03-ami-06496bd1baf9c7899.4
2019-07-04T11:33:14.000Z ami-0836ba22e3a34b7e7 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-06e1496840168e4ad.4
2019-07-04T10:53:34.000Z ami-09200a6e650491df2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190627.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-026c8acd92718196b.4
2019-07-31T06:47:51.000Z ami-09ca8a080b6699260 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190722.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-07d0cf3af28718ef8.4
2018-08-20T16:04:40.000Z ami-09eb876a926ae86db ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180814-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0b425589c7bb7663d.4
2019-11-14T21:11:29.000Z ami-0a7c7013f4ccb2223 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-04e7d1b7d2c34c116.4
2019-11-14T21:05:05.000Z ami-0b199ce4c7f1a6dd2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20191113-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-00a208c7cdba991ea.4
2019-06-21T07:40:44.000Z ami-0bf637af74e9c3d86 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-0cda679a4be93d2f9.4
2019-06-21T09:39:16.000Z ami-0cbe3990c7332f723 hapee-ubuntu-bionic-amd64-hvm-1.9r1-20190620-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-09bf076b7db09c73a.4
2019-11-13T05:31:49.000Z ami-0e8a32ed7b7db1e8c hapee-ubuntu-bionic-amd64-hvm-2.0r1-20191031-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-0646386e725375c55.4
2019-05-17T06:33:55.000Z ami-0fc06a2d18521ddff ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190514-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-024a64a6685d05041.4
2018-08-08T21:44:09.000Z ami-12cdeb6a          ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180806-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-920b10ed.4
#>

Sorting the AMI Results

As you can see, we now have an unsorted list of AMIs that match our criteria. Let’s sort by the CreationDate property and see if we can narrow down the specific image names we’re interested in. We’ll use the built-in Sort-Object command to sort the list of AMI objects based on the CreationDate property.

$ImageList | Sort-Object -Property CreationDate -Descending | Select-Object -Property CreationDate, ImageId, Name

<# Result:
CreationDate             ImageId               Name
------------             -------               ----
2019-11-18T08:18:40.000Z ami-0325d4fb7241c0a91 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-arm64-server-20191113-10d15682-9d0b-41dd-93c2-5e7de4f74165-ami-0ddcb9754ba4ffda3.4
2019-11-14T21:11:29.000Z ami-0a7c7013f4ccb2223 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-04e7d1b7d2c34c116.4
2019-11-14T21:05:05.000Z ami-0b199ce4c7f1a6dd2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20191113-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-00a208c7cdba991ea.4
2019-11-13T05:31:49.000Z ami-0e8a32ed7b7db1e8c hapee-ubuntu-bionic-amd64-hvm-2.0r1-20191031-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-0646386e725375c55.4
2019-07-31T06:47:51.000Z ami-09ca8a080b6699260 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190722.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-07d0cf3af28718ef8.4
2019-07-04T11:33:14.000Z ami-0836ba22e3a34b7e7 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-06e1496840168e4ad.4
2019-07-04T10:53:34.000Z ami-09200a6e650491df2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190627.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-026c8acd92718196b.4
2019-06-21T09:39:16.000Z ami-0cbe3990c7332f723 hapee-ubuntu-bionic-amd64-hvm-1.9r1-20190620-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-09bf076b7db09c73a.4
2019-06-21T07:40:44.000Z ami-0bf637af74e9c3d86 ubuntu-minimal/images/hvm-ssd/ubuntu-bionic-18.04-amd64-minimal-201-0641ddeb-4f55-4c19-a94b-5c409a5e0164-ami-0cda679a4be93d2f9.4
2019-06-21T07:08:03.000Z ami-06ad92f74f2c20787 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190617-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-095192256fe1477ad.4
2019-05-17T06:33:55.000Z ami-0fc06a2d18521ddff ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190514-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-024a64a6685d05041.4
2019-04-19T08:29:57.000Z ami-0643ea9028da5ede3 hapee-ubuntu-bionic-amd64-hvm-1.8r2-20190418-475d174d-0c14-4cf2-aa82-a15147bc1e05-ami-06a9997a1a81e688c.4
2019-02-27T08:49:40.000Z ami-06d5223e6851b3502 ZeroC Ice 3.7 AMI on ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-arm6-80c8a0ac-4a5f-4fb8-bf18-f3b2eaf43e03-ami-06496bd1baf9c7899.4
2019-02-26T11:25:22.000Z ami-05e0b411893499c11 ZeroC Ice 3.7 AMI on ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd6-5fd4acae-d28b-4c90-9f32-e6f0b4ee34a0-ami-039d6d7e077ed24e6.4
2019-02-26T11:21:58.000Z ami-041261af8354d4a0b ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190212.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0a313d6098716f372.4
2018-08-20T16:04:40.000Z ami-09eb876a926ae86db ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180814-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0b425589c7bb7663d.4
2018-08-08T21:44:09.000Z ami-12cdeb6a          ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180806-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-920b10ed.4
#>

Now that the results are sorted by CreationDate, let’s more closely examine the Name property values. Notice how some of them mention “arm64” CPU architecture? We don’t need those. Also, there are some results containing third-party vendors, like “hapee” and “ZeroC Ice” which we can ignore as well. There’s a common prefix on the images that we are interested in, however, which looks like this:

ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*

Let’s try passing this name prefix into our earlier Name filter, and see what we get back.

$Filter = [System.Collections.ArrayList]::new()
$null = $Filter.Add([Amazon.EC2.Model.Filter]::new('name', 'ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*'))
$null = $Filter.Add([Amazon.EC2.Model.Filter]::new('owner-alias', 'aws-marketplace'))

$ImageList = Get-EC2Image -Filter $Filter

$ImageList.Count

# Result: 8

<#
CreationDate             ImageId               Name
------------             -------               ----
2019-11-14T21:05:05.000Z ami-0b199ce4c7f1a6dd2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20191113-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-00a208c7cdba991ea.4
2019-07-31T06:47:51.000Z ami-09ca8a080b6699260 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190722.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-07d0cf3af28718ef8.4
2019-07-04T10:53:34.000Z ami-09200a6e650491df2 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190627.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-026c8acd92718196b.4
2019-06-21T07:08:03.000Z ami-06ad92f74f2c20787 ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190617-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-095192256fe1477ad.4
2019-05-17T06:33:55.000Z ami-0fc06a2d18521ddff ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190514-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-024a64a6685d05041.4
2019-02-26T11:21:58.000Z ami-041261af8354d4a0b ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20190212.1-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0a313d6098716f372.4
2018-08-20T16:04:40.000Z ami-09eb876a926ae86db ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180814-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-0b425589c7bb7663d.4
2018-08-08T21:44:09.000Z ami-12cdeb6a          ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-20180806-3b73ef49-208f-47e1-8a6e-4ae768d8a333-ami-920b10ed.4
#>

Finishing Up: Grab the Latest Image

This result set is much better, and better yet, it only takes 1 second to execute, instead of 35 seconds! Now we have a nice, small history of the basic “vanilla” Ubuntu 18.04 Bionic Beaver images that we can use. Naturally, we’ll want to use the latest, most up-to-date image that has the latest security updates. Since our list of images is sorted by CreationDate, we can just index into the array of results and grab the first one’s ImageId property.

($ImageList | Sort-Object -Property CreationDate -Descending | Select-Object -Property CreationDate, ImageId, Name -First 1).ImageId

# Result: ami-0b199ce4c7f1a6dd2

Now that you’ve filtered down the image list, and selected the latest one, you can feed this ImageId into your other scripts, AWS CloudFormation templates, or Hashicorp Terraform templates, to provision EC2 instances from the latest AMI!

This article originally appeared on https://trevorsullivan.net.