rimed
rimed

Reputation: 125

Terraform 1.2.0: Referencing resources and object mapping

I have deployed a cloud run application for currently two domains with a load balancer, which is already running. Now this setup needs to be rolled out to other domains. Because the resource setup is always the same, I face some issues:

  1. I want to prevent repeating code (which is managed through a for_each)
  2. Still there are some domain-specific values to cover, which i tried through a mapping table
  3. Referencing resources, which are created with for_each in another resource

The first issue I solved like this, which seems to work:

Old:

resource "google_cloud_run_service" "cr_domain1" {
  name     = "cr-domain1"
  location = "europe-west6"
  project  = "my_project"

  template {
   ...
  }
}

resource "google_cloud_run_service" "cr_domain2" {
  name     = "cr-domain2"
  location = "europe-southwest1"
  project  = "my_project"

  template {
   ...
  }
}

New:

resource "google_cloud_run_service" "cr" {
  for_each = toset( ["domain1", "domain2"] )
  name     = "cr-${each_key}"
  location = "tdb" # This is my second issue
  project  = "my_project"

  template {
   ...
  }
}

Regarding second issue I still need domain-specific location setup, which I tried to solve like this, but I am getting errors:

variable "cr_location" {
    type    = list(object({
       domain1 = string
       domain2 = string
    }))
    default = [{
        domain1 = "europe-west6"
        domain2 = "europe-southwest1"
    }]
}

resource "google_cloud_run_service" "cr" {
  for_each = toset( ["domain1", "domain2"] )
  name     = "cr-${each_key}"
  location = "${var.cr_location[0]}.${each.key}"
  project  = "my_project"

  template {
   ...
  }
}

Error is "Cannot include the given value in a string template: string required". But I have already declared it as a string in my variable "cr_location". Any idea what's the issue here? The expected output should be:

Also regarding issue 3 I do not understand how to referencing resources, which are created with for_each in another resource. So before my for_each in the cloud run resource block (see issue 1) I had this 2 resources:

Now I only have resource "google_cloud_run_service" "cr". But in my loadbalancer.tf I still have to references to the old namings (last coderow within "service"):

resource "google_compute_region_network_endpoint_group" "backendneg" {
  for_each              = toset( ["domain1", "domain2"] )
  name                  = "backendneg-${each.key}"
  project               = "my_project"
  network_endpoint_type = "SERVERLESS"
  region                = "${var.cr_location[0]}.${each.key}" # Here same issues as issue 2
  cloud_run {
    service = google_cloud_run_service.cr_domain1.name # Old reference
  }
}

So if there is no "cr_domain1" anymore how do I reference to this resource? My issue is that I have to create over 20 resources like that and I couldn't figure it out how to do it. I appreciate any guideline here.

Upvotes: 1

Views: 505

Answers (1)

Marko E
Marko E

Reputation: 18108

What I would suggest here is to try and refactor the variable because it is making a lot of things harder than they should be. So I would go for this kind of a variable definition:

variable "cr_location" {
  type = map(string)
  default = {
    domain1 = "europe-west6"
    domain2 = "europe-southwest1"
  }
}

Then, the rest should be easy to create:

resource "google_cloud_run_service" "cr" {
  for_each = var.cr_location
  name     = "cr-${each.key}"
  location = each.value
  project  = "my_project"

  template {
   ...
  }
}

And for the network endpoint resource:

resource "google_compute_region_network_endpoint_group" "backendneg" {
  for_each              = var.cr_location
  name                  = "backendneg-${each.key}"
  project               = "my_project"
  network_endpoint_type = "SERVERLESS"
  region                = each.value
  cloud_run {
    service = google_cloud_run_service.cr[each.key].name
  }
}

You could even try resource chaining with for_each [1] to make sure you are doing this for all the Cloud Run resources created:

resource "google_compute_region_network_endpoint_group" "backendneg" {
  for_each              = google_cloud_run_service.cr
  name                  = "backendneg-${each.key}"
  project               = "my_project"
  network_endpoint_type = "SERVERLESS"
  region                = each.value.location
  cloud_run {
    service = each.value.name
  }
}

[1] https://www.terraform.io/language/meta-arguments/for_each#chaining-for_each-between-resources

Upvotes: 2

Related Questions