Refer to another resource in a for_each

I’ve got some terraform code that is akin to the following:

locals {
  project_ids = toset(["proj1", "proj2"])
}
resource "google_project" "project" {
  for_each            = local.project_ids
  project_id          = each.key
  name                = each.key
  folder_id           = local.data_science_folder_id
  auto_create_network = false
  billing_account     = local.billing_account_id
}
module "service_api_enablement" {
  # enables services for a given project
  for_each            = local.project_ids
  source = "/path/to/source"
  project_id  = google_project.project[each.key].project_id
  #do some stuff to enable some services....not relevant for this topic
  #
  #
}

(this is somewhat contrived, its not the real code, but it exhibits the problem I’m having)

proj3 already exists so I need to import it. So I change my locals block to

locals {
  project_ids = toset(["proj1", "proj2", "proj3"])
}

and issue:

terraform import "google_project.projects[\"proj3\"]" "proj3"

Unfortunately that fails:

google_project.project["proj3"]: Import prepared!
  Prepared google_project for import
google_project.project["proj3"]: Refreshing state... [id=projects/proj3]

Error: Invalid index

  on main.tf line 16, in module "service_api_enablement":
  16:   project_id = google_project.project[each.key].project_id
    |----------------
    | each.key is "proj3"
    | google_project.project is object with 2 attributes

The given key does not identify an element in this collection value.

I think the problem is that my for_each in module "service_api_enablement" refers to local.project_ids which includes “proj3” however it does that before its actually been created by resource "google_project" "project". Hence what I think I need to do is change the for_each so that it refers to the collection of projects in resource "google_project" "project" but I don’t know what the syntax for that is.

I’m wondering if splatting could help but I can’t quite wrap my head around it.

Can anyone help?

(Hope I’ve elucidated the problem enough.)

ok, think I’ve figured it out. The for_each needed to be:

for_each = toset([for p in google_project.project: p.project_id])

Hence my code is now:

locals {
  project_ids = toset(["proj1", "proj2"])
}
resource "google_project" "project" {
  for_each            = local.project_ids
  project_id          = each.key
  name                = each.key
  folder_id           = local.data_science_folder_id
  auto_create_network = false
  billing_account     = local.billing_account_id
}
module "service_api_enablement" {
  # enables services for a given project
  for_each = toset([for p in google_project.project: p.project_id])
  source = "/path/to/source"
  project_id = google_project.project[each.key].project_id
  #do some stuff to enable some services....not relevant for this topic
  #
  #
}

I felt like splatting should work but when I changed it to

for_each = toset([google_project.project[*].project_id])

I got an error

Error: Unsupported attribute

on main.tf line 14, in module “service_api_enablement”:
14: for_each = toset([google_project.project[*].project_id])

This object does not have an attribute named “project_id”.

but never mind, I got it working so I’m not too worried about not using the splatting syntax.

Hi @jamiekt,

I’m glad you found a working solution. I just wanted to add that it seems like you are pretty close to Chaining for_each between resources here, and so that doc section might give you some ideas on how to simplify the configuration. In particular, I think you could write module "service_api_enablement" like this:

module "service_api_enablement" {
  for_each = google_project.project
  source   = "/path/to/source"

  project_id = each.value.project_id

  # ...
}

The trick here, as I think you’ve at least partially figured out based on what you tried, is that terraform import isn’t a full “terraform plan” in the usual sense and so it isn’t able to fully take into account all of the interactions between different sorts of changes in your configuration. However, we can help it understand our intent by showing the relationships between the resources using techniques like the one you used and the chaining pattern I showed here.

Import is designed mainly for getting started from a totally empty state when you’ve previously been using a tool other than Terraform, so it does have some rough edges if you try to use it piecemeal with a configuration where you’ve already been managing most objects with Terraform already. There’s usually a way to make it work, but I would recommend against workflows that require routine use of terraform import once you’ve already adopted Terraform, and whereever possible let Terraform be the one to create the new objects you add later.

Thanks @apparentlymart , the chaining of for_each is VERY nice, I’ll definitely be adopting that.

I would recommend against workflows that require routine use of terraform import once you’ve already adopted Terraform, and whereever possible let Terraform be the one to create the new objects you add later.

Unfortunately that’s not an option in this case. Anyway, I’ve gotten it woking so I’m happy. By definition the resources only have to be imported once so this isn’t something that requires ongoing maintenance.