Search code examples
amazon-web-servicesterraformterraform-provider-aws

Filter out Subnet IDs based on sufficient capacity in availability zones in Terraform


I'm trying to deploy an EKS cluster and everything seems to be fine except for one!

The facade module looks like this:

module "eks" {
  source = "../../../../infrastructure_modules/eks"

  ## EKS ##
  create_eks      = var.create_eks
  cluster_version = var.cluster_version
  cluster_name    = local.cluster_name
  vpc_id          = data.aws_vpc.this.id
  subnets         = data.aws_subnet_ids.this.ids

  # note: either pass worker_groups or node_groups
  # this is for (EKSCTL API) unmanaged node group
  worker_groups = var.worker_groups

  # this is for (EKS API) managed node group
  node_groups = var.node_groups

  ## Common tag metadata ##
  env      = var.env
  app_name = var.app_name
  tags     = local.eks_tags
  region   = var.region
}

The VPC id is retrieved through the following block :

data "aws_vpc" "this" {
  tags = {
    Name = "tagName"
  }
}

Which then is used to retrieve the subnet_IDs as following:

data "aws_subnet_ids" "this" {
  vpc_id = data.aws_vpc.this.id
}

Nevertheless, deploying this results in error stating:

Error: error creating EKS Cluster (data-layer-eks): UnsupportedAvailabilityZoneException: Cannot create cluster 'data-layer-eks' because us-east-1e, the targeted availability zone, does not currently have sufficient capacity to support the cluster.

Which is a well known error, and anybody can come across this for even EC2s.

I could solve this by simply hardcoding the subnet value, but that's really undesirable and hardly maintainable.

So the question is, how can I filter out subnet_IDs based on availability zones that have sufficient capacity?


Solution

  • First you need to collect the subnets with all of their attributes:

    data "aws_subnets" "this" {
      filter {
        name   = "vpc-id"
        values = [data.aws_vpc.this.id]
      }
    }
    
    data "aws_subnet" "this" {
      for_each = toset(data.aws_subnets.this.ids)
      id       = each.value
    }
    

    data.aws_subnet.this is now a map(object) with all of the subnets and their attributes. You can now filter by availability zone accordingly:

    subnets = [for subnet in data.aws_subnet.this : subnet.id if subnet.availability_zone != "us-east-1e"]
    

    You can also filter by truthy conditionals if that condition is easier for you:

    subnets = [for subnet in data.aws_subnet.this : subnet.id if contains(["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"], subnet.availability_zone)]
    

    It depends on your personal use case.