GCP Terraform: Unable to set subnets to be advertised via BGP on cloud router

I'm attempting to create a cloud router, with two network ranges being advertised from it, however I'm getting some very vague errors from terraform and I can't quite put my finger on what's wrong.

resource "google_compute_router" "router1" {
  name     = "${var.alias}-prod1-ha-vpn-cloud-router-1"
  project  = var.project_name
  network  = var.network1
  region   = var.region1
  bgp {
    asn = var.asn
    advertise_mode = "CUSTOM"
    dynamic "advertised_ip_ranges" {
      for_each = var.advertised_ip_ranges_prod1
      content {
        range = advertised_ip_ranges_prod1.value["cidr"]
        description = advertised_ip_ranges_prod1.value["desc"]
      }
    }
  }
}

And the actual module code:

module "vpn-ha-gateway" {
  source          = "-----"
  project_name    = var.project_name
  customer        = var.customer
  alias           = var.alias
  region1         = var.region1
  count           = (var.vpn_type == "ha-vpn" ? 1 : 0)   ## Build if vpn_type is ha-vpn
  network1        = module.prod1-vpc.self_link
  customer_redundancy = var.customer_redundancy
  customer_ha_vpn_peers = var.customer_ha_vpn_peers
  shared_secret   = var.shared_secret
  asn             = var.asn
  cust_asn        = var.cust_asn
  advertised_ip_ranges_prod1      = var.advertised_ip_ranges_prod1
}

We define the advertised_ip_ranges_prod1 as a map, as we don't know how many there will be per module:

variable "advertised_ip_ranges_prod1" {
  type = list(map(string))
  description = "advertised by BGP on prod1"
  default = [
    {
        cidr = "1.2.3.4/28" ## Prod1 advertised range
        desc = "secondary range advertised via BGP"
    },
    {
        cidr = "5.6.7.8/28" # Test advertised range
        desc = "Test range advertised via BGP"
    },
  ]
}

And while I have other modules built in the same way (with the for_each) working correctly, this one keeps throwing up these vague errors:

Error: Reference to undeclared resource

  on .terraform\modules\vpn-ha\main.tf line 36, in resource "google_compute_router" "router1":
  36:         range = advertised_ip_ranges_prod1.value["cidr"]

A managed resource "advertised_ip_ranges_prod1" "value" has not been declared
in module.vpn-ha-gateway.


Error: Reference to undeclared resource

  on .terraform\modules\vpn-ha-gateway\main.tf line 37, in resource "google_compute_router" "router1":
  37:         description = advertised_ip_ranges_prod1.value["desc"]

A managed resource "advertised_ip_ranges_prod1" "value" has not been declared
in module.vpn-ha-gateway.```


Solution 1:

So the issue (I think) is with how you reference the current iteration of the dynamic element from within the for_each "block". In general, the name of the "variable" that you use to reference the current iterator value in the for_each block is actually the name of the dynamic tag itself. As a result, I think you need to replace this:

resource "google_compute_router" "router1" {
  name     = "${var.alias}-prod1-ha-vpn-cloud-router-1"
  project  = var.project_name
  network  = var.network1
  region   = var.region1
  bgp {
    asn = var.asn
    advertise_mode = "CUSTOM"
    dynamic "advertised_ip_ranges" {
      for_each = var.advertised_ip_ranges_prod1
      content {
        range = advertised_ip_ranges_prod1.value["cidr"]
        description = advertised_ip_ranges_prod1.value["desc"]
      }
    }
  }
}

With this:

resource "google_compute_router" "router1" {
  name     = "${var.alias}-prod1-ha-vpn-cloud-router-1"
  project  = var.project_name
  network  = var.network1
  region   = var.region1
  bgp {
    asn = var.asn
    advertise_mode = "CUSTOM"
    dynamic "advertised_ip_ranges" {
      for_each = var.advertised_ip_ranges_prod1
      content {
        range = advertised_ip_ranges.value["cidr"]
        description = advertised_ip_ranges.value["desc"]
      }
    }
  }
}

e.g. s/advertised_ip_ranges_prod1/advertised_ip_ranges/

For reference, see the very first example on their documentation about this, which is the following:

resource "aws_elastic_beanstalk_environment" "tfenvtest" {
  name                = "tf-test-name"
  application         = "${aws_elastic_beanstalk_application.tftest.name}"
  solution_stack_name = "64bit Amazon Linux 2018.03 v2.11.4 running Go 1.12.6"

  dynamic "setting" {
    for_each = var.settings
    content {
      namespace = setting.value["namespace"]
      name = setting.value["name"]
      value = setting.value["value"]
    }
  }
}

Note that they use setting.value, even though the name of the variable is settings. You're basically running into that exact same, rather confusing, difference.