Terraform creating a list out of a dict

Viewed 2949

I got the following problem. I create a local variable like:

variable vpcs {
  type = map
  default = {
    "tftest" = {
      "cidr" = "10.1.0.0/16",
      "tags" = {
        "cost"    = "shared",
        "service" = "TEST"
      }
    }
  }
}

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

variable subnets {
  type = map
  default = {
    "pub1"  = { "subnet" = 0, "rttable" = "public" },
    "pub2"  = { "subnet" = 1, "rttable" = "public" },
    "priv1" = { "subnet" = 2, "rttable" = "default" },
    "priv2" = { "subnet" = 3, "rttable" = "default" }
  }
}

locals {
  vpc_subnet_var = flatten([
    for v in keys(var.vpcs) : [
      for s in keys(var.subnets) : {
        network_name   = v
        tags           = var.vpcs[v]["tags"]
        subnet_name    = format("%v_%s", v, s)
        subnet_cidr    = cidrsubnet(var.vpcs[v]["cidr"], 8, var.subnets[s]["subnet"])
        subnet_az      = element(local.my_azs, var.subnets[s]["subnet"] % 2)
        subnet_rttable = var.subnets[s]["rttable"]
      }
    ]
  ])
  prefix = "${var.prefix}-${terraform.workspace}-tf"
  common_tags = {
    Environment = terraform.workspace
    Project     = var.project
    ManagedBy   = "Terraform"
  }
  my_azs = slice(data.aws_availability_zones.azs.names, 0, 2)
}

resource "aws_vpc" "test" {
  for_each   = var.vpcs
  cidr_block = each.value["cidr"]
  tags = merge(each.value["tags"],
    {
      Name = each.key
  })
}

resource "aws_subnet" "test" {
  for_each = {
    for s in local.vpc_subnet_var : s.subnet_name => s
  }
  vpc_id            = aws_vpc.test[each.value.network_name].id
  availability_zone = each.value.subnet_az
  cidr_block        = each.value.subnet_cidr
  tags = merge(each.value.tags,
    {
      Name = each.value.subnet_name
  })
}

An example test output would be:

test                = [
      + {
          + tftest_priv1 = {
              + arn                             = (known after apply)
              + assign_ipv6_address_on_creation = false
              + availability_zone               = "eu-west-1a"
              + availability_zone_id            = (known after apply)
              + cidr_block                      = "10.1.2.0/24"
              + id                              = (known after apply)
              + ipv6_cidr_block                 = (known after apply)
              + ipv6_cidr_block_association_id  = (known after apply)
              + map_public_ip_on_launch         = false
              + owner_id                        = (known after apply)
              + tags                            = {
                  + "Name"    = "tftest_priv1"
                  + "cost"    = "shared"
                  + "service" = "TEST"
                }
              + timeouts                        = null
              + vpc_id                          = (known after apply)
            }
          + tftest_priv2 = {
              + arn                             = (known after apply)
              + assign_ipv6_address_on_creation = false
              + availability_zone               = "eu-west-1b"
              + availability_zone_id            = (known after apply)
              + cidr_block                      = "10.1.3.0/24"
              + id                              = (known after apply)
              + ipv6_cidr_block                 = (known after apply)
              + ipv6_cidr_block_association_id  = (known after apply)
              + map_public_ip_on_launch         = false
              + owner_id                        = (known after apply)
              + tags                            = {
                  + "Name"    = "tftest_priv2"
                  + "cost"    = "shared"
                  + "service" = "TEST"
                }
              + timeouts                        = null
              + vpc_id                          = (known after apply)
            }
          + tftest_pub1  = {
              + arn                             = (known after apply)
              + assign_ipv6_address_on_creation = false
              + availability_zone               = "eu-west-1a"
              + availability_zone_id            = (known after apply)
              + cidr_block                      = "10.1.0.0/24"
              + id                              = (known after apply)
              + ipv6_cidr_block                 = (known after apply)
              + ipv6_cidr_block_association_id  = (known after apply)
              + map_public_ip_on_launch         = false
              + owner_id                        = (known after apply)
              + tags                            = {
                  + "Name"    = "tftest_pub1"
                  + "cost"    = "shared"
                  + "service" = "TEST"
                }
              + timeouts                        = null
              + vpc_id                          = (known after apply)
            }
          + tftest_pub2  = {
              + arn                             = (known after apply)
              + assign_ipv6_address_on_creation = false
              + availability_zone               = "eu-west-1b"
              + availability_zone_id            = (known after apply)
              + cidr_block                      = "10.1.1.0/24"
              + id                              = (known after apply)
              + ipv6_cidr_block                 = (known after apply)
              + ipv6_cidr_block_association_id  = (known after apply)
              + map_public_ip_on_launch         = false
              + owner_id                        = (known after apply)
              + tags                            = {
                  + "Name"    = "tftest_pub2"
                  + "cost"    = "shared"
                  + "service" = "TEST"
                }
              + timeouts                        = null
              + vpc_id                          = (known after apply)
            }
        },
    ]

Now I want to use the created Subnets for creating a db_subnet_group in the private subnets e.g.:

resource "aws_db_subnet_group" "main" {
  name = "${local.prefix}-main"
 subnet_ids = [ 
    aws_subnet.test["tftest_priv1"].id,
    aws_subnet.test["tftest_priv2"].id
  ]
}

But of course not with fixed values :) I thought of using dynamic blocks, but this does not work :/

resource "aws_db_subnet_group" "main" {
  name = "${local.prefix}-main"
  dynamic "subnet_ids" {
      for_each = {
            for s in local.vpc_subnet_var : s.subnet_az => s... if s.subnet_rttable == "public"
        }
  content {
      subnet_ids = aws_subnet.test[s.subnet_name].id
  }
  }

I am able to get the needed ids like in my output example:

output "test" {
    value = [for s in local.vpc_subnet_var : {
        subnet_ids = aws_subnet.test[s.subnet_name].id
    } if s.subnet_rttable == "default"
    ]
}
------------------------ OUTPUT ------------------------
  + test                = [
      + {
          + subnet_ids = (known after apply)
        },
      + {
          + subnet_ids = (known after apply)
        },
    ]

But how do I only get the IDs to put it in a list like ["priv_subnet_id1", "priv_subnet_id2"]... To use it e.g. in the aws_db_subnet_group resource? Probably create another local?

I hope you understand the question :D I tried a lot of things now but didn't get it to work. If you need more input please tell me (it is my first question on stackoverflow)

Thx, Daniel

1 Answers

Based on the data available, I think (assuming I did not misunderstood the issue) you are very close.

I don't have aws_subnet.test thus I can't fully reproduce nor verify the outcome, but I think the following should result in a list of subnet ids. I setup a private_subnets_list local variable:

locals {

  private_subnets_list = [
      for s in local.vpc_subnet_var:
        aws_subnet.test[s.subnet_name].id if s.subnet_rttable == "default"]

}

The above assumes that aws_subnet.test[s.subnet_name].id is correct and that aws_subnet.test are indexed by s.subnet_name.

Then to create the subnet group, the following could be tried:

resource "aws_db_subnet_group" "main" {
  name = "${local.prefix}-main"
  subnet_ids = local.private_subnets_list
}
Related