Working with Terraform to build VM’s in Azure for proof-of-concept work, I often opt to use Ubuntu for my Linux systems. Up until 18.04 everything worked fine but when I tried to go to 20.04 or later my Terraform deployment would error out with:

Failure sending request: StatusCode=404 -- Original Error: Code="PlatformImageNotFound" Message="The platform image 'Canonical:UbuntuServer:20.04-lts:latest' is not available. Verify that all fields in the storage profile are correct.

Did Microsoft suddenly fall behind the curve with available OS images for one of the world’s most popular operating systems?

Thankfully, no. The answer is actually pretty simple… they just started breaking out different versions of the popular operating systems under different ‘offer’ families and your Terraform code needs to reflect as much.

For up to like version 19.04 you can use ‘UbuntuServer’ for the offer value and it looks something like this under the source_image_reference sub-block in your TF code:

source_image_reference {
    #version = "latest"
    publisher = "Canonical"
    offer     = "UbuntuServer"
    sku       = "18.04-lts"
    version   = "latest"

However for later versions your need to select the correct offer family and it looks like this.

source_image_reference {
    #version = "latest"
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"

Also note that the deliminator between the major and minor version numbers has changed from a period to an underscore. This is a great discovery but it brings up another question; how do you figure out the correct names for the ‘offer’ and ‘sku’ values? For that you can turn to Azure CLI.

To get a full list of offers for a given publisher (in this case Canonical) in a given region, run the following:

az vm image list-offers -p "Canonical" -l "eastus2" --output table

Which returns this:

Location    Name
----------  --------------------------------------------
eastus2     0001-com-ubuntu-confidential-vm-experimental
eastus2     0001-com-ubuntu-confidential-vm-focal
eastus2     0001-com-ubuntu-confidential-vm-test-focal
eastus2     0001-com-ubuntu-minimal-focal-daily
eastus2     0001-com-ubuntu-minimal-groovy-daily
eastus2     0001-com-ubuntu-minimal-hirsute-daily
eastus2     0001-com-ubuntu-minimal-impish-daily
eastus2     0001-com-ubuntu-minimal-jammy-daily
eastus2     0001-com-ubuntu-private-fips-motorola
eastus2     0001-com-ubuntu-pro-advanced-sla
eastus2     0001-com-ubuntu-pro-advanced-sla-att
eastus2     0001-com-ubuntu-pro-advanced-sla-cfx
eastus2     0001-com-ubuntu-pro-advanced-sla-csw
eastus2     0001-com-ubuntu-pro-advanced-sla-dd
eastus2     0001-com-ubuntu-pro-advanced-sla-kudsec
eastus2     0001-com-ubuntu-pro-advanced-sla-nestle
eastus2     0001-com-ubuntu-pro-advanced-sla-servicenow
eastus2     0001-com-ubuntu-pro-advanced-sla-shell
eastus2     0001-com-ubuntu-pro-advanced-sla-sk
eastus2     0001-com-ubuntu-pro-advanced-sla-ub01
eastus2     0001-com-ubuntu-pro-advanced-sla-unp
eastus2     0001-com-ubuntu-pro-bionic
eastus2     0001-com-ubuntu-pro-bionic-fips
eastus2     0001-com-ubuntu-pro-focal
eastus2     0001-com-ubuntu-pro-focal-fips
eastus2     0001-com-ubuntu-pro-hidden-msft-fips
eastus2     0001-com-ubuntu-pro-jammy
eastus2     0001-com-ubuntu-pro-microsoft
eastus2     0001-com-ubuntu-pro-minimal-cis-focal
eastus2     0001-com-ubuntu-pro-trusty
eastus2     0001-com-ubuntu-pro-xenial
eastus2     0001-com-ubuntu-pro-xenial-fips
eastus2     0001-com-ubuntu-server-arm-preview-focal
eastus2     0001-com-ubuntu-server-eoan
eastus2     0001-com-ubuntu-server-focal
eastus2     0001-com-ubuntu-server-focal-daily
eastus2     0001-com-ubuntu-server-groovy
eastus2     0001-com-ubuntu-server-groovy-daily
eastus2     0001-com-ubuntu-server-hirsute
eastus2     0001-com-ubuntu-server-hirsute-daily
eastus2     0001-com-ubuntu-server-impish
eastus2     0001-com-ubuntu-server-impish-daily
eastus2     0001-com-ubuntu-server-jammy
eastus2     0001-com-ubuntu-server-jammy-daily
eastus2     0001-com-ubuntu-server-kinetic-daily
eastus2     0002-com-ubuntu-minimal-bionic-daily
eastus2     0002-com-ubuntu-minimal-disco-daily
eastus2     0002-com-ubuntu-minimal-focal-daily
eastus2     0002-com-ubuntu-minimal-xenial-daily
eastus2     0002-com-ubuntu-server-arm-preview-bionic
eastus2     0003-com-ubuntu-minimal-eoan-daily
eastus2     0003-com-ubuntu-server-trusted-vm
eastus2     test-ubuntu-premium-offer-0002
eastus2     Ubuntu15.04Snappy
eastus2     Ubuntu15.04SnappyDocker
eastus2     UbunturollingSnappy
eastus2     UbuntuServer

You will also notice that this returns the developer code names for a version family and if you don’t have those memorized (for shame…) you can take a look at pages like this one: and just do a text search for the version you are looking for and next to it you will see the code name and deduce which offer value to use. You can followup that command with this:

az vm image list-skus -p "Canonical" -l "eastus2" -f 0001-com-ubuntu-server-jammy --output table

Which returns this:

Location    Name
----------  ---------------
eastus2     22_04-lts
eastus2     22_04-lts-arm64
eastus2     22_04-lts-gen2

And now you have everything you need to properly populate your Terraform and use the version of Ubuntu you want. This is definitely one of those “something I am tired of googling” annoyances so now it is documented for my benefit and hopefully yours. Per usual, thank you to stack overflow for being a top result to my google query:


1 of 1

This post has no comments. Be the first to leave one!

Join the discussion

Your email address will not be published. Required fields are marked *