Terraform, get count of length of data source

2k views Asked by At

Folks,

I'm trying to create a subnet per each aws availability zone available in a AWS region.

data "aws_availability_zones" "azs" {
  depends_on = [aws_vpc.k3s_vpc]
  state = "available"
}

locals {
  azs= "${data.aws_availability_zones.azs.names}"
}

resource "aws_subnet" "private_subnets" {
  count             = length(data.aws_availability_zones.azs.names)
  vpc_id            = aws_vpc.k3s_vpc.id
  cidr_block        = var.private_subnets_cidr[count.index]
  availability_zone       = local.azs[count.index]
}

getting below error

Error: Invalid count argument

The "count" value depends on resource attributes that cannot be determined until apply, so Terraform cannot predict how many instances will be created. To work around this, use the -target argument to first apply only the resources that the count depends on.

Any ideas ?

1

There are 1 answers

0
Martin Atkins On

Inside your data "aws_availability-zones" "azs" block you've written depends_on = [aws_vpc.k3s_vpc], which means that Terraform can't look up the availability zones until the VPC is already created, but the VPC doesn't exist yet during planning and so you see this error.

The availability zones for a particular region your account don't vary based on the creation of VPCs, so it's not clear to me why you included that dependency. If you remove it then Terraform should see that it is able to resolve that data source during the planning phase and thus determine how many subnets to create.

However, I would still suggest caution with this approach: if the result of that lookup were to change in future in a way that introduces availability zones anywhere except the end of the list then your existing subnets would get reassigned to new availability zones, and thus the provider will plan to replace them. Instead, it might be better to use the availability zone names themselves as the identifiers for the subnets, so that it won't matter what order they appear in the resulting list:

data "aws_availability_zones" "azs" {
  state = "available"
}

locals {
  azs = toset(data.aws_availability_zones.azs.names)
}

resource "aws_subnet" "private_subnets" {
  for_each = local.azs

  vpc_id            = aws_vpc.k3s_vpc.id
  cidr_block        = var.private_subnets_cidr[each.value]
  availability_zone = each.value
}

Notice that under this approach you'd also need to change variable "public_subnets_cidr" to be a map instead of a list, with the availability zone names as keys, so that the CIDR ranges are also assigned directly to AZs and won't get reassigned if new zones appear in your account later.