Terraform : Is it possible to interpolate or loop through ressources from a list within the output block?

Viewed 74

I am trying to output gcp project information by doing something like this :

output "projects" {
  value = tomap({
    for project_name in ["project_1", "project_2", "project_3"] :
    project_name => tomap({
      id     = google_project."${project_name}".id
      number = google_project."${project_name}".number
    })
  })
  description = "Projects"
}

Or like this :

output "projects" {
  value = tomap({
    for_each = toset([google_project.project_1,google_project.project_2])
    id       = each.key.id
    number   = each.key.number

  })
  description = "Projects"
}

Is it at all possible to use resource names this way? Do I have to specify every resource by duplicating code?

E.g.

output "projects" {
  value = tomap({
    project_1 = tomap({
      id     = google_project.project_1.id
      number = google_project.project_1.number
    })
    project_2 = tomap({
      id     = google_project.project_2.id
      number = google_project.project_2.number
    })
    project_3 = tomap({
      id     = google_project.project_3 .id
      number = google_project.pproject_3 .number
    })
  })
  description = "Projects"
}

EDIT : declared resources.

In main.tf projects 1 to 3 are declared the same way.

resource "google_project" "project_3" {
  name                = var.projects.project_3.name
  project_id          = var.projects.project_3.id
  folder_id           = google_folder.parent.name
  billing_account     = data.google_billing_account.acct.id
  auto_create_network = false
}

in variables.tf

variable "projects" {
  type = map(object({
    name = string
    id   = string
  }))
}

in variables.tfvars

projects = {
  project_1= {
    name = "project_1"
    id   = "project_1-12345"
  }  
  project_2= {
    name = "project_2"
    id   = "project_2-12345"
  }
  project_3= {
    name = "project_2"
    id   = "project_2-12345"
  }
}
2 Answers

I edited my initial answer after seeing the Terraform resource that creates a project. The need is a way to get a resource name in the output bloc with interpolation.

I think if a single resource is used to create all the projets instead of one resource per projet, it's easier to expose this resource in the output bloc.

For example you can configure projects metadata information from a json file, or directly a local variable or a var if needed :

Example for a json file and local variable

mymodule/resource/projects.json :

{
    "projects": {
        "project_1": {
            "id": "project_1",
            "number": "23333311"
        },
        "project_2": {
            "id": "project_2",
            "number": "33399999"
        }
    }
}

Then retrieve projects as a variable from locals.tf file :

mymodule/locals.tf :

locals {
  projects = jsondecode(file("${path.module}/resource/projects.json"))["projects"]
}

Create your projects in a single resource with a foreach :

resource "google_project" "projects" {
  for_each           = local.projects
  name                = each.key
  project_id          = each.value["id"]
  folder_id           = google_folder.parent.name
  billing_account     = data.google_billing_account.acct.id
  auto_create_network = false
}

Expose the projects resource in an output.tf file :

output "projects" {
  value = google_project.projects
  description = "Projects"
}

The same principle can be done with a var instead of local variable.

I misunderstood your question originally. I see now that you want to reference a resource by a variable name. No you cannot do that. But your setup here doesn't really make sense, and seems more complex than it needs to be.

Consider if these options would improve your setup.

locals {
  projects = { # This is equivalent to your input.
    project_1 = {
      name = "project_1"
      id   = "project_1-12345"
    }
    project_2 = {
      name = "project_2"
      id   = "project_2-12345"
    }
    project_3 = {
      name = "project_3"
      id   = "project_3-12345"
    }
  }
}

resource "google_project" "this" {
  for_each = local.projects

  name                = each.key # or each.value.name / don't really need name
  project_id          = each.value.id
  folder_id           = google_folder.parent.name
  billing_account     = data.google_billing_account.acct.id
  auto_create_network = false
}

output "projects_from_input" {
  description = "You can of course, just use the input."
  value       = local.projects
}

output "projects_explicit_values" {
  description = "Alternatively, if you need a subset of resource values."
  value = { for k, v in google_project.this : k => {
    name = v.name
    id   = v.project_id
  } }
}

output "complete_resources" {
  description = "But you can just output the complete resource."
  value       = google_project.this
}
Related