Reputation: 168
in config.meta file I store accesses to the google bigquery datasets. The accesses
object is a list of dictionaries, where each dictionary contains the role as the first element and the second element element (e.g. special_group
or group_by_email
) which is being variated among the list.
My goal is to apply the accesses dynamically. The approach is pretty easy when each dictionary on the accesses
list has the same second element (e.g. special_group
only). But how to apply them dynamically depends on the second element name/value?
# config.meta file
{
"accesses": [
{
"role": "OWNER",
"special_group": "projectOwners"
},
{
"role": "READER",
"group_by_email": "[email protected]"
},
...
]
...
}
terraform file:
resource "google_bigquery_dataset" "dataset" {
dataset_id = "${var.dataset}"
location = "${var.bq_region}"
friendly_name = "${var.dataset}"
dynamic "access" {
# get accesses (list of dictionaries) from a file
for_each = [for a in jsondecode(file("$config.meta"))["access"]: {
role = a.role
special_group = a.special_group
# how to get special_group/group_by name conditionally?
# like: if dictionary has the key 'special_group' create that variable
# othervise create 'group_by_name'
}]
content {
role = access.value.role
special_group = access.value.special_group
# how to get special_group/group_by name conditionally?
# like: if the 'special_group' had been declared add 'special_group' to the access block
# othervise add 'group_by_name'.
}
}
}
How do I conditionally add an attribute to a dynamic tag?
Upvotes: 0
Views: 3117
Reputation: 74779
Inside a configuration block in Terraform, omitting an optional argument and setting that argument to null
are equivalent. Because access
is a nested block, we can exploit that to write expressions that conditionally set the argument.
dynamic "access" {
# get accesses (list of dictionaries) from a file
for_each = [for a in jsondecode(file("$config.meta"))["access"]: {
role = a.role
special_group = try(a.special_group, null)
group_by_email = try(a.group_by_email, null)
}]
content {
role = access.value.role
special_group = access.value.special_group
group_by_email = access.value.group_by_email
}
}
This first uses the try
function so that a missing attribute will result in falling back on a null
value rather than an error. This means that the resulting objects will look something like this:
[
{
"role": "OWNER",
"special_group": "projectOwners",
"group_by_name": null,
},
{
"role": "READER",
"special_group": null,
"group_by_email": "[email protected]",
},
]
Inside the content
block we then just assign all of these directly. The ones that are null
will be treated by Terraform as if they are not set at all, due to the behavior I described earlier. (Notice that the behavior of treating null
as unset doesn't apply when constructing object values: it is a special behavior only for direct arguments inside a block.)
This should then have the same effect as if you'd written out the following two access
blocks directly:
access {
role = "OWNER"
special_group = "projectOwners"
}
access {
role = "READER"
group_by_email = "[email protected]"
}
Upvotes: 3
Reputation: 768
locals {
items = fileexists("$config.meta") ? jsondecode(file("$config.meta")).accesses : []
special_groups = [for v in local.items: v if lookup(v, "special_group", "") != ""]
group_by_emails = [for v in local.items: v if lookup(v, "group_by_email", "") != ""]
}
You could use multiple definitions of dynamic
block
resource "google_bigquery_dataset" "dataset" {
...
dynamic "access" {
for_each = [for a in local.special_groups: {
role = lookup(a, "role", "")
special_group = lookup(a, "special_group", "")
}]
content {
role = access.value.role
special_group = access.value.special_group
}
}
dynamic "access" {
for_each = [for a in local.group_by_emails: {
role = lookup(a, "role", "")
group_by_email = lookup(a, "group_by_email", "")
}]
content {
role = access.value.role
group_by_email = access.value.group_by_email
}
}
}
Upvotes: 0