I need help to run for_each on terraform for following variable set
locals {
db_users = {
test_user1 = { #user
test_cluster1 = { #cluster
db_name = ["db_a", "db_b", "db_c"]
db_role = ["readWrite", "read", "readWrite"]
db_type = ["CLUSTER", "CLUSTER", "CLUSTER"]
},
test_cluster2 = {
db_name = ["db_a", "db_b", "db_c"]
db_role = ["readWrite", "read", "readWrite"]
db_type = ["CLUSTER", "CLUSTER", "CLUSTER"]
}
},
test_user2 = {
test_cluster1 = {
db_name = ["db_d", "db_e", "db_f"]
db_role = ["readWrite", "readWrite", "read"]
#db_type = [["CLUSTER", "LAKE"], ["CLUSTER", "LAKE"], ["CLUSTER", "LAKE"]]
db_type = ["CLUSTER", "CLUSTER", "CLUSTER"]
},
test_cluster2 = {
db_name = ["db_d", "db_e", "db_f"]
db_role = ["readWrite", "readWrite", "read"]
db_type = ["CLUSTER", "CLUSTER", "CLUSTER"]
}
}
}
The db_type
can be both based on the situation. If we have multiple db_type then both value should be associated with the final resource.
I also tried flatten of the variable with following
value = flatten([
for ip_key, ip in local.db_users : [
for a, b in ip : [
for index in range(length(b.db_name)) : {
username = ip_key
user_index = index
roles = {
role_name = b.db_role[index]
database_name = b.db_name[index]
}
scopes = {
type = b.db_type[index]
name = a
}
}
]
]
])
Output after flattening the value
[
{
"roles" = {
"database_name" = "db_a"
"role_name" = "readWrite"
}
"scopes" = {
"name" = "test_cluster1"
"type" = "CLUSTER"
}
"username" = "test_user1"
},
{
"roles" = {
"database_name" = "db_b"
"role_name" = "read"
}
"scopes" = {
"name" = "test_cluster1"
"type" = "CLUSTER"
}
"username" = "test_user1"
},
...
{
"roles" = {
"database_name" = "db_d"
"role_name" = "readWrite"
}
"scopes" = {
"name" = "test_cluster1"
"type" = "CLUSTER"
}
"username" = "test_user2"
},
{
"roles" = {
"database_name" = "db_e"
"role_name" = "readWrite"
}
"scopes" = {
"name" = "test_cluster1"
"type" = "CLUSTER"
}
"username" = "test_user2"
},
...
]
Looking into the type of the value, it is as follows
tuple([
object({
roles: object({
database_name: string,
role_name: string,
}),
scopes: object({
name: string,
type: string,
}),
username: string,
}),
...
object({
roles: object({
database_name: string,
role_name: string,
}),
scopes: object({
name: string,
type: string,
}),
username: string,
}),
])
Question: What I want to achieve???
The resource which is responsible for this is the following. There can be as many roles and scopes in the same block as possible. For this we can you dynamic block setup
resource "users" "user" {
username = var.username
roles {
database_name = var.database_name
role_name = var.role_name
}
roles {
database_name = var.database_name
role_name = var.role_name
}
...
scopes {
name = var.cluster
type = var.type
}
scopes {
name = var.cluster
type = var.type
}
...
}
and the resource should finally look like the following value.
Resource No. 1
username=test_user1
role = {
db_name=db_a
role=readWrite
}
role = {
db_name=db_b
role=read
}
role = {
db_name=db_c
role=readWrite
}
scope = {
name = test_cluster1
type = "cluster"
}
scope = {
name = test_cluster1
type = "lake"
}
Resource No. 2
username=test_user1
role = {
db_name=db_d
role=readWrite
}
role = {
db_name=db_e
role=read
}
role = {
db_name=db_f
role=readWrite
}
scope = {
name = test_cluster2
type = "cluster"
}
scope = {
name = test_cluster2
type = "lake"
}
Resource No. 3
username=test_user1
role = {
db_name=db_a
role=readWrite
}
role = {
db_name=db_b
role=read
}
role = {
db_name=db_c
role=readWrite
}
scope = {
name = test_cluster1
type = "cluster"
}
scope = {
name = test_cluster1
type = "lake"
}
Resource No. 4
username=test_user2
role = {
db_name=db_d
role=readWrite
}
role = {
db_name=db_e
role=read
}
role = {
db_name=db_f
role=readWrite
}
scope = {
name = test_cluster2
type = "cluster"
}
scope = {
name = test_cluster2
type = "lake"
}
Exact Source code used. ( I know this is not correct).
here username
, database_name
, role_name
, name
, and type
all should be a string value
resource "users" "user" {
for_each = local.db_users
username = each.key
dynamic "roles" {
for_each = each.value
content {
database_name = each.value.db_name
role_name = each.value.db_role
}
}
dynamic "scopes" {
for_each = each.value
content {
name = each.key
type = each.value.db_type
}
}
}
Error
│ Error: Unsupported attribute
│
│ on database_users.tf line 25, in resource "mongodbatlas_database_user" "user":
│ 25: database_name = each.value.db_name
│ ├────────────────
│ │ each.value is object with 2 attributes
│
│ This object does not have an attribute named "db_name".
╷
│ Error: Unsupported attribute
│
│ on database_users.tf line 26, in resource "mongodbatlas_database_user" "user":
│ 26: role_name = each.value.db_role
│ ├────────────────
│ │ each.value is object with 2 attributes
│
│ This object does not have an attribute named "db_role".
╵
╷
│ Error: Unsupported attribute
│
│ on database_users.tf line 33, in resource "mongodbatlas_database_user" "user":
│ 33: type = each.value.db_type
│ ├────────────────
│ │ each.value is object with 2 attributes
│
│ This object does not have an attribute named "db_type".
╵
PS: considering the above flatten value
is not used. How to solve this using the same db_users contents?
PPS: I am also okay with using of flatten value
as long as it serves my purpose.
Your db_users
should be flattened in a different way, namely:
locals {
db_users_flat = merge([
for username, clusters in local.db_users :
{
for clustername, cluster in clusters :
"${username}-${clustername}" => {
username = username
clustername = clustername
cluster = cluster
}
}
]...) # please do NOT remove the dots
}
then
resource "users" "user" {
for_each = local.db_users_flat
username = each.value.username
dynamic "roles" {
for_each = range(length(each.value.cluster.db_name))
content {
database_name = each.value.cluster.db_name[roles.key]
role_name = each.value.cluster.db_role[roles.key]
}
}
dynamic "scopes" {
for_each = range(length(each.value.cluster.db_type))
content {
name = each.value.clustername
type = each.value.cluster.db_type[scopes.key]
}
}
}