ratr
ratr

Reputation: 616

How can I access first value of tuple and create a list in terraform?

I have a list of AZ & I would like to create a list of subnets in each AZ:

locals {
distict_az = tolist(["eu-west-1a", "eu-west-1b", "eu-west-1c"])
}

The following data source should return a tuple.

data "aws_subnet_ids" "all_subnets" {
depends_on = [
  null_resource.next
]
count = length(local.distict_az)
vpc_id             = aws_vpc.virtual_network.id
filter {
  name   = "availabilityZone"
  values = ["${local.distict_az[count.index]}"]
}
}

The data source above returns the following:

{
  "module": "module.cluster_aws.module.aws_network[0]",
  "mode": "data",
  "type": "aws_subnet_ids",
  "name": "all_subnets",
  "provider": "module.cluster_aws.provider[\"registry.terraform.io/hashicorp/aws\"]",
  "instances": [
    {
      "index_key": 0,
      "schema_version": 0,
      "attributes": {
        "filter": [
          {
            "name": "availabilityZone",
            "values": [
              "eu-west-1a"
            ]
          }
        ],
        "id": "vpc-01131c4180030ef3d",
        "ids": [
          "subnet-025a72306d7b94d03",
          "subnet-05f678ed690f0854a",
          "subnet-05fb3f9f2b2b5857a",
          "subnet-0e02ae5e08c62df02",
          "subnet-0fa45fd0f1b8e0d31"
        ],
        "tags": null,
        "vpc_id": "vpc-01131c4180030ef3d"
      },
      "sensitive_attributes": []
    },
    {
      "index_key": 1,
      "schema_version": 0,
      "attributes": {
        "filter": [
          {
            "name": "availabilityZone",
            "values": [
              "eu-west-1b"
            ]
          }
        ],
        "id": "vpc-01131c4180030ef3d",
        "ids": [
          "subnet-07c4811e4d7d10d21",
          "subnet-0b39190edb761ecf6"
        ],
        "tags": null,
        "vpc_id": "vpc-01131c4180030ef3d"
      },
      "sensitive_attributes": []
    },
    {
      "index_key": 2,
      "schema_version": 0,
      "attributes": {
        "filter": [
          {
            "name": "availabilityZone",
            "values": [
              "eu-west-1c"
            ]
          }
        ],
        "id": "vpc-01131c4180030ef3d",
        "ids": [
          "subnet-0769f8431f923f76e",
          "subnet-0a6a86a1eead531cd"
        ],
        "tags": null,
        "vpc_id": "vpc-01131c4180030ef3d"
      },
      "sensitive_attributes": []
    }
  ]
}

I am struggling how can I access the first subnet from each AZ and pass it on to the next resource as a list?

So for example if each AZ have 5 subnets each, I would like to create a list of 3 subnets(one from each of the above defined AZ). So I am expecting a list with the following values:

expected_list = tolist(["subnet-025a72306d7b94d03", "subnet-07c4811e4d7d10d21", "subnet-0769f8431f923f76e"])

The order doesn't matter.

Upvotes: 0

Views: 2338

Answers (1)

Martin Atkins
Martin Atkins

Reputation: 74289

I suggest being careful here because if you rely on an arbitrary ordering then that ordering could change in future and cause significant downstream effects on resources declared based on these results.

With that said, the following would get most of the way there by creating a set of all subnet ids per availability zone:

locals {
  distict_az = toset(["eu-west-1a", "eu-west-1b", "eu-west-1c"])
}

data "aws_subnet_ids" "all_subnets" {
  for_each = local.distinct_az

  vpc_id = aws_vpc.virtual_network.id

  filter {
    name   = "availabilityZone"
    values = [each.key]
  }
}

locals {
  az_subnet_ids = tomap({
    for az, obj in data.aws_subnet_ids.all_subnets : az => toset(obj.ids)
  })
}

The above is relatively safe because it hasn't yet made any assumptions about ordering of availability zones or subnets. The next step requires making some decision about the ordering of the subnets and availability zones, and since you said the order isn't important to you I'm going to assume that a lexical ordering by string is okay and so use sort:

locals {
  az_selected_subnet_ids = tomap({
    for az, ids in local.az_subnet_ids : az => sort(ids)[0]
  })
  # NOTE: The above will fail if any of the AZs has zero
  # ids, because there will be no zeroth element.

  # The following discards the keys of the map above and takes
  # just the values. This function is defined to sort the results
  # lexically by their corresponding keys, and so the result will
  # be in an order derived from the AZ name.
  all_selected_subnet_ids = values(az_selected_subnet_ids)
  # If the order of the selected subnet ids doesn't matter, I'd
  # recommend being explicit about that by using toset(values(...))
  # instead.
}

Since this is selecting subnets by an immutable property (availability zone) and ordering them by ID, the "first" for each subnet should stay constant as long as AWS typically assigns new subnet ids that are lexically greater than all subnet ids issued before, since the new ones will therefore appear later in the sort result.

Upvotes: 2

Related Questions