Terrafrom refer to a resource attribute created by using for each in other downstream resource

Viewed 41

I am trying to create user credentials in Azure SQL Server for each DB with their own set of roles added to them but I am having difficulty in getting the proper result from random password resources any insights would be helpful

Here is my code

variable "db_details" {
  description = "Details of certs to be created"
  type = list(object({
    db_name       = string
    roles         = list(string)
  }))
}

## Random Password for SQL db's
resource "random_password" "db_passwords" {
  for_each     = { for db in var.db_details : db.db_name => db }
  length = 32
  special = false
}

resource "mssql_user" "db_users" {
  server {
    host = "mysqlserverr"
    port = "1433"
    login {
      username = local.sql_server_username
      password = data.azurerm_key_vault_secret.sql_server_password.value
    }
  }
  for_each     = { for db in var.db_details : db.db_name => db }
  database = join("-", [
    local.resource_name_prefix,
    each.value.db
  ])
  username = join("-", [
    "svc",
    each.value.db,
    "user"
  ])
  password = random_password.db_passwords[each.value.db].result
  roles    = each.value.roles
}

What I want to achieve is to get a result of each db index key at random.password resource passed to the corresponding db in password

Here is the error log

│ Error: Unsupported attribute
│ 
│   on sql.tf line 130, in resource "mssql_user" "db_users":
│  130:     each.value.db
│     ├────────────────
│     │ each.value is object with 2 attributes
│ 
│ This object does not have an attribute named "db".
1 Answers

If you use locals in Terraform you can achieve the result you want with no issues at all.

Here is a working example:

## Random Password for SQL db's
resource "random_password" "db_passwords" {
  for_each = { for db in local.db_details : db.db_name => db }
  length   = 32
  special  = false
}

locals {
# In your case you'd use var.db_details I just didn't want to set up a variable in a working example FYI
  dbs = { for db in local.db_details : db.db_name => { db_name = db.db_name, roles = db.roles, password = random_password.db_passwords[db.db_name] } }
  db_details = [
    {
      db_name = "db1"
      roles   = ["role1", "role2"]
    },
    {
      db_name = "db2"
      roles   = ["role2", "role3"]
    }
  ]
}

Terraform console (requires plan + apply first due to random_password resource):

local.dbs

{
  "db1" = {
    "db_name" = "db1"
    "password" = {
      "bcrypt_hash" = "$2a$10$y207dv1mPszS3pOXHPb9Nu6FDNS0dVFVFLou4zl9i/lWeBzBzeUIm"
      "id" = "none"
      "length" = 32
      "lower" = true
      "min_lower" = 0
      "min_numeric" = 0
      "min_special" = 0
      "min_upper" = 0
      "number" = true
      "numeric" = true
      "result" = "iSRCBbXiP2c0JqpqLEoPlfVO9g2no7KK"
      "special" = false
      "upper" = true
    }
    "roles" = [
      "role1",
      "role2",
    ]
  }
  "db2" = {
    "db_name" = "db2"
    "password" = {
      "bcrypt_hash" = "$2a$10$lBVnhACCNlBgQUoX7PYc2e7lGq1iAEvZPrdkZrgIj81MUtFFMrham"
      "id" = "none"
      "length" = 32
      "lower" = true
      "min_lower" = 0
      "min_numeric" = 0
      "min_special" = 0
      "min_upper" = 0
      "number" = true
      "numeric" = true
      "result" = "JXh9BUNXziq73sbk8wUBzAuTocfJGx1d"
      "special" = false
      "upper" = true
    }
    "roles" = [
      "role2",
      "role3",
    ]
  }
}

When making the map you'll notice I went password = random_password.db_passwords[db.db_name] but you can return only the password part by going random_password.db_passwords[db.db_name].result if desired.

This works because your original for loop will change the resource name to become indexed e.g. random_password.db_passwords["db1"] so we use db.db_name in my example to access each instance of the random_password.db_passwords resources.

Related