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

Acquiring subnet availability zone ids in bulk, in a module


The module I'm working on represents one app which is deployed to a VPC. The VPC is declared elsewhere.

The relevant data path includes these resources:

variable "vpc_id" { }

data "aws_subnets" "private" {
  filter {
    name   = "vpc-id"
    values = [data.aws_vpc.vpc.id]
  }

  filter {
    name = "tag:Visibility"
    values = ["private"]
  }
}

data "aws_subnet" "private" {
  for_each = toset(data.aws_subnets.private.ids)

  vpc_id = data.aws_vpc.vpc.id
  id = each.value
}

resource "aws_rds_cluster" "database" {
  availability_zones = data.aws_subnet.private.*.availability_zones
}

That feels like the correct syntax, though it is a verbose chain of data retrieval. However, when I terraform plan it:

│ Error: Unsupported attribute
│ 
│   on ../../../../../appmodule/rds_postgres.tf line 23, in resource "aws_rds_cluster" "webapp":
│   23:   availability_zones = data.aws_subnet.private.*.availability_zone_id
│ 
│ This object does not have an attribute named "availability_zone_id".

I'm using aws-provider 4.18.0 and Terraform v1.1.2. The documentation for the subnet data source shows that availability_zone_id

Am I missing something obvious here?


Solution

  • As mentioned in the comments, you can get the list of AZs by using the values built-in function [1]. This is necessary as the data source you are relying on to provide the AZs is in a key value format due to for_each meta-argument use:

    data "aws_subnet" "private" {
      for_each = toset(data.aws_subnets.private.ids)
    
      vpc_id = data.aws_vpc.vpc.id
      id = each.value
    }
    

    The change you need to make is:

    resource "aws_rds_cluster" "database" {
      availability_zones = values(data.aws_subnet.private)[*].availability_zone
    }
    

    A test with an output and a default VPC shows the following result:

      + subnet_azs = [
          + "us-east-1b",
          + "us-east-1c",
          + "us-east-1d",
          + "us-east-1a",
          + "us-east-1f",
          + "us-east-1e",
        ]
    

    As you can see, it is already a list so you can use it as is.

    Note that there is an explanation why you should use the availability_zone attribute:

    availability_zone_id - (Optional) ID of the Availability Zone for the subnet. This argument is not supported in all regions or partitions. If necessary, use availability_zone instead


    [1] https://www.terraform.io/language/functions/values