Tomasz Kubat
Tomasz Kubat

Reputation: 168

How do I conditionally add an attribute to a dynamic tag?

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

Answers (2)

Martin Atkins
Martin Atkins

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

dmkvl
dmkvl

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

Related Questions