Simple feature detection in Terraform to harden code against missing AWS resources

By Antony Melvin  – Architecture & Planning Practice Lead

Increasingly solutions architects are moving from GUI consoles to infrastructure-as-code (IaC) tools like Terraform to build out infrastructure. As the public clouds grow in their capabilities, a bit like the railroad rush in the 19th century, there will inevitably be an odd dead end. So how do you back up the train?

Read on to find out more about our simple and useful feature detection in terraform.

Background

AWS arrange their regional-specific resources into regions and within regions into availability zones (AZs). You can equate availability zones as data centres and regions as clusters of data centres. AWS doesn’t like this simplistic description, but it makes the next bit easier to explain.

So, if you imagine a data centre being open it may not initially or ultimately support every resource. It may need time to ramp up. For example, availability zone us-west-2d does not (at the time of writing) support t2-micro EC2 instance types, although the other three public us-west-2 availability zones do support t2-micros. Perhaps it’s newer, or smaller or t2-micro is phasing out? Hard to know the why, but once you’ve established a gap you realise there will be others.

There will be other examples of resources not being universally available, different availability zones or regions with limitations.

AWS like all the cloud providers will encourage their customers to move to serverless architecture as that tends to be universally available. But before that happy day when cloud providers obfuscate further, there will be gaps. This is not really a problem when you spin up resources in the console as you can more easily work around issues. But who spins up resources in a console anymore?

If you have comprehensive unit tests, then you can identify availability gaps during development. But the pace of cloud resource expansion may fox even the most diligent unit test plan. Unit tests will go red and require a fix.

Use feature detection in Terraform

So how do we launch an IaC auto-scaling group in us-west-2 that targets all AZs that support t2-micro and ignores ones that don’t? I’ll demonstrate using Terraform and avoiding hardcoding is usually a good first step. Beyond there how will the code extend to new valid targets? For example, what if us-west-2d subsequently supports t2-micro or if us-west-2e opens?

In our example, us-west-2d doesn’t support an EC2 type of t2-micro that you can be identify in the usual ways and is evident from the following terraform apply when building an auto-scaling group:

The example stated, us-west-2d doesn’t support an EC2 type of t2-micro can be identified in the usual ways and was evident from the following terraform apply when building an auto-scaling group

On reading this you may reach into a search engine. There are reports of this error in github e.g.

https://github.com/hashicorp/terraform-provider-aws/issues/15272

https://github.com/hashicorp/terraform-provider-aws/issues/16703

…and even a fix in terraform AWS provider v3.52.0, dated 29 July 2021

https://github.com/hashicorp/terraform-provider-aws/pull/16704

https://github.com/hashicorp/terraform-provider-aws/blob/v3.52.0/CHANGELOG.md

But you still have to code in the fix, which I’ll go through below.

Feature detection is old school

As a sidebar, this brings up the concept of feature detection. Feature detection has been a big part of website builds since the 1990s when DHTML coding had to understand if the browser in question was either Internet Explorer or Netscape Navigator – and run different flavours of JavaScript based on the browser type and version. I’m sure, dear reader, you remember the ‘Browser Wars’ and on the front-line the problems that the difference between document.all and document.layers caused in JavaScript coding in 1998 before feature detection became common. No? (https://www.irt.org/articles/js106/).

It will also be a consideration in building a super-cloud layer which I’ll blog about later.

Should feature detection be a big part of defensive HCL code? 

Based on the work done already in github, the AZs that support t2-micro instances in us-west-2 can be enumerated before the fix and output, without creating any infrastructure, as follows:

#all AZs
data "aws_availability_zones" "all_azs" {
  state = "available"
  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
  all_availability_zones = true
}

#output the region AZs
output "all_azs" {
  value = data.aws_availability_zones.all_azs.names
  description = "All AZs"
}


# Select the azs that support my instance type
data "aws_ec2_instance_type_offerings" "supports-my-instance" {
  filter {
    name   = "instance-type"
    values = [var.instance_type]
  }
  location_type = "availability-zone"
}

#output the valid AZs
output "azs_that_support_instance" {
  value = data.aws_ec2_instance_type_offerings.supports-my-instance.locations
  description = "AZs that support instance"
}

This causes terraform plan to Identify the AZs that exist in the region as well as the ones that we should use:

Which causes terraform plan to Identify the AZs that exist in the region as well as the ones that we should use

But until AWS provider v3.52.0 (July 2021) while you could identify the correct AZs, you couldn’t use that to filter out unsupported AZs.

Code Fix 

The following code allows an autoscaling group to launch t2-micros only into subnets in AZs that support them.

The code is abbreviated for clarity. The first fragment creates the auto-scaling group (ASG) resource. The second fragment does the feature detection and identifies appropriate AZs and puts them in a list of strings. The third fragment converts the list of strings into a set of AZs that the ASG resource code can accept as a value for vpc_zone_identifier.

# Launch EC2 instances into AZs that support them
resource "aws_autoscaling_group" "example" {
  launch_configuration = aws_launch_configuration.example.name
  vpc_zone_identifier  = data.aws_subnet_ids.default.ids

  target_group_arns = [aws_lb_target_group.asg.arn]
  health_check_type = "ELB"

  min_size             = 2
  max_size             = 10

  tag {
    key                 = "Name"
    value               = "terraform-asg-example"
    propagate_at_launch = true
  }
}

# Select the azs that support my instance type
data "aws_ec2_instance_type_offerings" "supports-my-instance" {
  filter {
    name   = "instance-type"
    values = [var.instance_type]
  }
  location_type = "availability-zone"
}

# Restrict the availability zones to be targeted by the auto-scale
data "aws_subnet_ids" "default" {
  vpc_id = data.aws_vpc.default.id
  filter {
    name = "availability-zone"
    values = data.aws_ec2_instance_type_offerings.supports-my-instance.locations
  }
}

Confirming the bug fix point-in-time

If you change your AWS provider version to v3.51.0 or earlier, you can confirm the fix was implemented in v3.52.0. You can also see that prior to 29 July 2021 aws_ec2_instance_type_offerings couldn’t be filtered by location in HCL.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "3.51.0"
    }
  }
}

If you change your AWS provider version to v3.51.0 or earlier, you can confirm that the fix was implemented in v3.52.0 and that prior to 29 July 2021 aws_ec2_instance_type_offerings couldn’t be filtered by location in HCL.

Summary – Feature detection in Terraform

Here, we outline how to use a fix implemented in AWS provider v3.52.0 to filter auto-scaling group targets based on location, a set that is filtered by whether the AZs (locations) support t2-micro EC2 types. The Terraform bug worked on is not identified by terraform plan – but is thrown during a terraform apply.

Using feature detection to avoid having to hardcode appropriate availability zones can easily extend to other areas. As AWS rolls out new facilities there are no guarantees that everything that they usually support will remain so. The best practice of specifying an AWS provider version is not bulletproof if you are dynamically discovering potential resources from an ever-growing cloud universe.

Feature detection before resource generation smells like a good idea.

If you enjoyed this blog, check out our AWS series here, or get in touch with us here to find out more.