terraform-google-modules / terraform-google-sql-db

Creates a Cloud SQL database instance
https://registry.terraform.io/modules/terraform-google-modules/sql-db/google
Apache License 2.0
265 stars 418 forks source link

local.users will be known only after apply #621

Open JMorinPS opened 1 month ago

JMorinPS commented 1 month ago

TL;DR

We are trying to add some additionnal users from a custom module that manage random password and JSON format, but looks like Postgres module is not ready to handle it this way

Expected behavior

Created Secret manager containing user and random passwords with JSON format Giving output of the module as additional_users field Adding new users to the Postgres instance

Observed behavior

╷
│ Error: Invalid for_each argument
│ 
│   on .terraform/modules/postgres/modules/postgresql/main.tf line 254, in resource "random_password" "additional_passwords":
│  254:   for_each = local.users
│     ├────────────────
│     │ local.users will be known only after apply
│ 
│ The "for_each" map includes keys derived from resource attributes that
│ cannot be determined until apply, and so Terraform cannot determine the
│ full set of keys that will identify the instances of this resource.
│ 
│ When working with unknown values in for_each, it's better to define the map
│ keys statically in your configuration and place apply-time results only in
│ the map values.
│ 
│ Alternatively, you could use the -target planning option to first apply
│ only the resources that the for_each value depends on, and then apply a
│ second time to fully converge.

Terraform Configuration

See next comment

Terraform Version

1.9.0

Additional information

No response

JMorinPS commented 1 month ago

The main idea behind our database-credentials module is to generate Secret Manager JSON formatted on GCP and then read the content to create additional users.

Here some details of mentioned module

Module call

# Modules call

module "postgres_credentials" {
  source        = "/path/to/my/module"
  labels        = {foo = bar}
  users         = ["user1", "user2"]
}

module "postgres" {

  depends_on = [module.postgres_credentials.applied]
  source     = "GoogleCloudPlatform/sql-db/google//modules/postgresql"
  version    = "20.2.0"
  [...]

  additional_users     = module.postgres_credentials.credentials

Module details (shortened version)

resource "google_secret_manager_secret" "secret_manager" {
  secret_id = "my-users"
  labels    = {}
  replication {
    auto {}
  }
}

resource "random_password" "passwords" {
  for_each = toset(var.users)
  length   = 32
  numeric  = true
  lower    = true
  upper    = true
  special  = true
}

locals {

  secret_data = <<-EOF
  [%{for index, user in var.users}
      {
          "name": "${user}",
          "password": "${random_password.passwords[user].result}",
          "random_password": "false"
      }%{if length(var.users) > 1 && index(var.users, user) + 1 < length(var.users)}${","}%{else}%{endif}%{endfor}
  ]
  EOF
}

resource "google_secret_manager_secret_version" "secret_manager_version" {
  secret      = google_secret_manager_secret.secret_manager.id
  secret_data = local.secret_data
}

Module output

output "credentials" {
  value       = nonsensitive(jsondecode(google_secret_manager_secret_version.secret_manager_version.secret_data))
  description = "Secret data that can be used by GCP Terraform Database modules"
}

output "applied" {
  value       = true
  depends_on  = [google_secret_manager_secret_version.secret_manager_version]
  description = "Boolean value thar will be return once new version is created"
}

As you can notice, we also tried a dependency with a boolean from an output named applied but seems not working as expected