diff --git a/modules/kms/README.md b/modules/kms/README.md
index 4782d8240d..90621bef37 100644
--- a/modules/kms/README.md
+++ b/modules/kms/README.md
@@ -120,14 +120,14 @@ module "kms" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [keyring](variables.tf#L64) | Keyring attributes. | object({…})
| ✓ | |
-| [project_id](variables.tf#L115) | Project id where the keyring will be created. | string
| ✓ | |
+| [project_id](variables.tf#L114) | Project id where the keyring will be created. | string
| ✓ | |
| [iam](variables.tf#L17) | Keyring IAM bindings in {ROLE => [MEMBERS]} format. | map(list(string))
| | {}
|
| [iam_bindings](variables.tf#L24) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | map(object({…}))
| | {}
|
| [iam_bindings_additive](variables.tf#L39) | Keyring individual additive IAM bindings. Keys are arbitrary. | map(object({…}))
| | {}
|
| [import_job](variables.tf#L54) | Keyring import job attributes. | object({…})
| | null
|
| [keyring_create](variables.tf#L72) | Set to false to manage keys and IAM bindings in an existing keyring. | bool
| | true
|
-| [keys](variables.tf#L78) | Key names and base attributes. Set attributes to null if not needed. | map(object({…}))
| | {}
|
-| [tag_bindings](variables.tf#L120) | Tag bindings for this keyring, in key => tag value id format. | map(string)
| | {}
|
+| [keys](variables.tf#L78) | Key names and base attributes. Set attributes to null if not needed. | map(object({…}))
| | {}
|
+| [tag_bindings](variables.tf#L119) | Tag bindings for this keyring, in key => tag value id format. | map(string)
| | {}
|
## Outputs
diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf
index 2708a7f7cf..d0d335674b 100644
--- a/modules/kms/variables.tf
+++ b/modules/kms/variables.tf
@@ -87,7 +87,6 @@ variable "keys" {
algorithm = string
protection_level = optional(string, "SOFTWARE")
}))
-
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
members = list(string)
diff --git a/modules/organization/README.md b/modules/organization/README.md
index 1b004aa623..f0253d11b3 100644
--- a/modules/organization/README.md
+++ b/modules/organization/README.md
@@ -440,12 +440,44 @@ module "org" {
iam = {
"roles/resourcemanager.tagAdmin" = ["group:${var.group_email}"]
}
+ iam_bindings = {
+ viewer = {
+ role = "roles/resourcemanager.tagViewer"
+ members = ["group:gcp-support@example.org"]
+ }
+ }
+ iam_bindings_additive = {
+ user_app1 = {
+ role = "roles/resourcemanager.tagUser"
+ member = "group:app1-team@example.org"
+ }
+ }
values = {
- dev = {}
+ dev = {
+ iam_bindings_additive = {
+ user_app2 = {
+ role = "roles/resourcemanager.tagUser"
+ member = "group:app2-team@example.org"
+ }
+ }
+ }
prod = {
description = "Environment: production."
iam = {
- "roles/resourcemanager.tagViewer" = ["group:${var.group_email}"]
+ "roles/resourcemanager.tagViewer" = ["group:app1-team@example.org"]
+ }
+ iam_bindings = {
+ admin = {
+ role = "roles/resourcemanager.tagAdmin"
+ members = ["group:gcp-support@example.org"]
+ condition = {
+ title = "gcp_support"
+ expression = <<-END
+ request.time.getHours("Europe/Berlin") <= 9 &&
+ request.time.getHours("Europe/Berlin") >= 17
+ END
+ }
+ }
}
}
}
@@ -455,8 +487,9 @@ module "org" {
env-prod = module.org.tag_values["environment/prod"].id
}
}
-# tftest modules=1 resources=6 inventory=tags.yaml e2e serial
+# tftest modules=1 resources=10 inventory=tags.yaml
```
+
You can also define network tags, through a dedicated variable *network_tags*:
@@ -498,7 +531,7 @@ module "org" {
| [org-policy-custom-constraints.tf](./org-policy-custom-constraints.tf) | None | google_org_policy_custom_constraint
|
| [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | google_org_policy_policy
|
| [outputs.tf](./outputs.tf) | Module outputs. | |
-| [tags.tf](./tags.tf) | None | google_tags_tag_binding
· google_tags_tag_key
· google_tags_tag_key_iam_binding
· google_tags_tag_value
· google_tags_tag_value_iam_binding
|
+| [tags.tf](./tags.tf) | None | google_tags_tag_binding
· google_tags_tag_key
· google_tags_tag_key_iam_binding
· google_tags_tag_key_iam_member
· google_tags_tag_value
· google_tags_tag_value_iam_binding
· google_tags_tag_value_iam_member
|
| [variables-iam.tf](./variables-iam.tf) | None | |
| [variables-logging.tf](./variables-logging.tf) | None | |
| [variables-tags.tf](./variables-tags.tf) | None | |
@@ -522,11 +555,11 @@ module "org" {
| [logging_exclusions](variables-logging.tf#L32) | Logging exclusions for this organization in the form {NAME -> FILTER}. | map(string)
| | {}
|
| [logging_settings](variables-logging.tf#L39) | Default settings for logging resources. | object({…})
| | null
|
| [logging_sinks](variables-logging.tf#L49) | Logging sinks to create for the organization. | map(object({…}))
| | {}
|
-| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
| [org_policies](variables.tf#L51) | Organization policies applied to this organization keyed by policy name. | map(object({…}))
| | {}
|
| [org_policy_custom_constraints](variables.tf#L78) | Organization policy custom constraints keyed by constraint name. | map(object({…}))
| | {}
|
-| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this organization, in key => tag value id format. | map(string)
| | {}
|
-| [tags](variables-tags.tf#L52) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [tag_bindings](variables-tags.tf#L81) | Tag bindings for this organization, in key => tag value id format. | map(string)
| | {}
|
+| [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
## Outputs
diff --git a/modules/organization/tags.tf b/modules/organization/tags.tf
index d25757c243..0321818a5a 100644
--- a/modules/organization/tags.tf
+++ b/modules/organization/tags.tf
@@ -15,50 +15,96 @@
*/
locals {
- _tag_values = flatten([
- for tag, attrs in local.tags : [
- for value, value_attrs in attrs.values : {
- description = value_attrs.description,
- key = "${tag}/${value}"
- id = try(value_attrs.id, null)
- name = value
- roles = keys(value_attrs.iam)
- tag = tag
- tag_id = attrs.id
- tag_network = try(attrs.network, null) != null
+ _tag_iam = flatten([
+ for k, v in local.tags : [
+ for role in keys(v.iam) : {
+ # we cycle on keys here so we don't risk injecting dynamic values
+ role = role
+ tag = k
+ tag_id = v.id
}
]
])
- _tag_values_iam = flatten([
- for key, value_attrs in local.tag_values : [
- for role in value_attrs.roles : {
- id = value_attrs.id
- key = value_attrs.key
- name = value_attrs.name
+ _tag_value_iam = flatten([
+ for k, v in local.tag_values : [
+ for role in v.roles : {
+ id = v.id
+ key = v.key
+ name = v.name
role = role
- tag = value_attrs.tag
+ tag = v.tag
}
]
])
- _tags_iam = flatten([
- for tag, attrs in local.tags : [
- for role in keys(attrs.iam) : {
- role = role
- tag = tag
- tag_id = attrs.id
+ _tag_values = flatten([
+ for k, v in local.tags : [
+ for vk, vv in v.values : {
+ description = vv.description,
+ key = "${k}/${vk}"
+ iam_bindings = keys(vv.iam_bindings)
+ iam_bindings_additive = keys(vv.iam_bindings_additive)
+ id = try(vv.id, null)
+ name = vk
+ # we only store keys here so we don't risk injecting dynamic values
+ roles = keys(vv.iam)
+ tag = k
+ tag_id = v.id
+ tag_network = try(v.network, null) != null
}
]
])
- tag_values = {
- for t in local._tag_values : t.key => t
+ tag_iam = {
+ for t in local._tag_iam : "${t.tag}:${t.role}" => t
+ }
+ tag_iam_bindings = merge([
+ for k, v in local.tags : {
+ for bk in keys(v.iam_bindings) : "${k}:${bk}" => {
+ binding = bk
+ tag = k
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_iam_bindings_additive = merge([
+ for k, v in local.tags : {
+ for bk in keys(v.iam_bindings_additive) : "${k}:${bk}" => {
+ binding = bk
+ tag = k
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_value_iam = {
+ for v in local._tag_value_iam : "${v.key}:${v.role}" => v
}
- tag_values_iam = {
- for t in local._tag_values_iam : "${t.key}:${t.role}" => t
+ tag_value_iam_bindings = merge([
+ for k, v in local.tag_values : {
+ for bk in v.iam_bindings : "${k}:${bk}" => {
+ binding = bk
+ id = v.id
+ key = k
+ name = v.name
+ tag = v.tag
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_value_iam_bindings_additive = merge([
+ for k, v in local.tag_values : {
+ for bk in v.iam_bindings_additive : "${k}:${bk}" => {
+ binding = bk
+ id = v.id
+ key = k
+ name = v.name
+ tag = v.tag
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_values = {
+ for v in local._tag_values : v.key => v
}
tags = merge(var.tags, var.network_tags)
- tags_iam = {
- for t in local._tags_iam : "${t.tag}:${t.role}" => t
- }
}
# keys
@@ -82,7 +128,7 @@ resource "google_tags_tag_key" "default" {
}
resource "google_tags_tag_key_iam_binding" "default" {
- for_each = local.tags_iam
+ for_each = local.tag_iam
tag_key = (
each.value.tag_id == null
? google_tags_tag_key.default[each.value.tag].id
@@ -94,6 +140,30 @@ resource "google_tags_tag_key_iam_binding" "default" {
)
}
+resource "google_tags_tag_key_iam_binding" "bindings" {
+ for_each = local.tag_iam_bindings
+ tag_key = (
+ each.value.tag_id == null
+ ? google_tags_tag_key.default[each.value.tag].id
+ : each.value.tag_id
+ )
+ role = local.tags[each.value.tag]["iam_bindings"][each.value.binding].role
+ members = (
+ local.tags[each.value.tag]["iam_bindings"][each.value.binding].members
+ )
+}
+
+resource "google_tags_tag_key_iam_member" "bindings" {
+ for_each = local.tag_iam_bindings_additive
+ tag_key = (
+ each.value.tag_id == null
+ ? google_tags_tag_key.default[each.value.tag].id
+ : each.value.tag_id
+ )
+ role = local.tags[each.value.tag]["iam_bindings_additive"][each.value.binding].role
+ member = local.tags[each.value.tag]["iam_bindings_additive"][each.value.binding].member
+}
+
# values
resource "google_tags_tag_value" "default" {
@@ -108,7 +178,7 @@ resource "google_tags_tag_value" "default" {
}
resource "google_tags_tag_value_iam_binding" "default" {
- for_each = local.tag_values_iam
+ for_each = local.tag_value_iam
tag_value = (
each.value.id == null
? google_tags_tag_value.default[each.value.key].id
@@ -121,6 +191,36 @@ resource "google_tags_tag_value_iam_binding" "default" {
)
}
+resource "google_tags_tag_value_iam_binding" "bindings" {
+ for_each = local.tag_value_iam_bindings
+ tag_value = (
+ each.value.id == null
+ ? google_tags_tag_value.default[each.value.key].id
+ : each.value.id
+ )
+ role = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings"][each.value.binding].role
+ )
+ members = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings"][each.value.binding].members
+ )
+}
+
+resource "google_tags_tag_value_iam_member" "bindings" {
+ for_each = local.tag_value_iam_bindings_additive
+ tag_value = (
+ each.value.id == null
+ ? google_tags_tag_value.default[each.value.key].id
+ : each.value.id
+ )
+ role = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings_additive"][each.value.binding].role
+ )
+ member = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings_additive"][each.value.binding].member
+ )
+}
+
# bindings
resource "google_tags_tag_binding" "binding" {
diff --git a/modules/organization/variables-tags.tf b/modules/organization/variables-tags.tf
index 4688504d57..21a0efe6f4 100644
--- a/modules/organization/variables-tags.tf
+++ b/modules/organization/variables-tags.tf
@@ -19,11 +19,47 @@ variable "network_tags" {
type = map(object({
description = optional(string, "Managed by the Terraform organization module.")
iam = optional(map(list(string)), {})
- id = optional(string)
- network = string # project_id/vpc_name
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
+ network = string # project_id/vpc_name
values = optional(map(object({
description = optional(string, "Managed by the Terraform organization module.")
iam = optional(map(list(string)), {})
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
})), {})
}))
nullable = false
@@ -54,11 +90,47 @@ variable "tags" {
type = map(object({
description = optional(string, "Managed by the Terraform organization module.")
iam = optional(map(list(string)), {})
- id = optional(string)
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
values = optional(map(object({
description = optional(string, "Managed by the Terraform organization module.")
iam = optional(map(list(string)), {})
- id = optional(string)
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
})), {})
}))
nullable = false
diff --git a/modules/project/README.md b/modules/project/README.md
index 75388aefc3..cd92870a47 100644
--- a/modules/project/README.md
+++ b/modules/project/README.md
@@ -1173,7 +1173,7 @@ module "bucket" {
| [quotas.tf](./quotas.tf) | None | google_cloud_quotas_quota_preference
|
| [service-accounts.tf](./service-accounts.tf) | Service identities and supporting resources. | google_kms_crypto_key_iam_member
· google_project_default_service_accounts
· google_project_iam_member
· google_project_service_identity
|
| [shared-vpc.tf](./shared-vpc.tf) | Shared VPC project-level configuration. | google_compute_shared_vpc_host_project
· google_compute_shared_vpc_service_project
· google_compute_subnetwork_iam_member
· google_project_iam_member
|
-| [tags.tf](./tags.tf) | None | google_tags_tag_binding
· google_tags_tag_key
· google_tags_tag_key_iam_binding
· google_tags_tag_value
· google_tags_tag_value_iam_binding
|
+| [tags.tf](./tags.tf) | None | google_tags_tag_binding
· google_tags_tag_key
· google_tags_tag_key_iam_binding
· google_tags_tag_key_iam_member
· google_tags_tag_value
· google_tags_tag_value_iam_binding
· google_tags_tag_value_iam_member
|
| [variables-iam.tf](./variables-iam.tf) | None | |
| [variables-quotas.tf](./variables-quotas.tf) | None | |
| [variables-tags.tf](./variables-tags.tf) | None | |
@@ -1204,7 +1204,7 @@ module "bucket" {
| [logging_exclusions](variables.tf#L108) | Logging exclusions for this project in the form {NAME -> FILTER}. | map(string)
| | {}
|
| [logging_sinks](variables.tf#L115) | Logging sinks to create for this project. | map(object({…}))
| | {}
|
| [metric_scopes](variables.tf#L146) | List of projects that will act as metric scopes for this project. | list(string)
| | []
|
-| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [network_tags](variables-tags.tf#L17) | Network tags by key name. If `id` is provided, key creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
| [org_policies](variables.tf#L158) | Organization policies applied to this project keyed by policy name. | map(object({…}))
| | {}
|
| [parent](variables.tf#L185) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | string
| | null
|
| [prefix](variables.tf#L195) | Optional prefix used to generate project id and name. | string
| | null
|
@@ -1216,8 +1216,8 @@ module "bucket" {
| [shared_vpc_host_config](variables.tf#L235) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…})
| | null
|
| [shared_vpc_service_config](variables.tf#L244) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…})
| | {…}
|
| [skip_delete](variables.tf#L272) | Allows the underlying resources to be destroyed without destroying the project itself. | bool
| | false
|
-| [tag_bindings](variables-tags.tf#L45) | Tag bindings for this project, in key => tag value id format. | map(string)
| | null
|
-| [tags](variables-tags.tf#L51) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
+| [tag_bindings](variables-tags.tf#L81) | Tag bindings for this project, in key => tag value id format. | map(string)
| | null
|
+| [tags](variables-tags.tf#L88) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | map(object({…}))
| | {}
|
| [vpc_sc](variables.tf#L278) | VPC-SC configuration for the project, use when `ignore_changes` for resources is set in the VPC-SC module. | object({…})
| | null
|
## Outputs
diff --git a/modules/project/tags.tf b/modules/project/tags.tf
index 4f2a666459..1f9ff378b5 100644
--- a/modules/project/tags.tf
+++ b/modules/project/tags.tf
@@ -15,50 +15,96 @@
*/
locals {
- _tag_values = flatten([
- for tag, attrs in local.tags : [
- for value, value_attrs in attrs.values : {
- description = value_attrs.description,
- key = "${tag}/${value}"
- id = try(value_attrs.id, null)
- name = value
- roles = keys(value_attrs.iam)
- tag = tag
- tag_id = attrs.id
- tag_network = try(attrs.network, null) != null
+ _tag_iam = flatten([
+ for k, v in local.tags : [
+ for role in keys(v.iam) : {
+ # we cycle on keys here so we don't risk injecting dynamic values
+ role = role
+ tag = k
+ tag_id = v.id
}
]
])
- _tag_values_iam = flatten([
- for key, value_attrs in local.tag_values : [
- for role in value_attrs.roles : {
- id = value_attrs.id
- key = value_attrs.key
- name = value_attrs.name
+ _tag_value_iam = flatten([
+ for k, v in local.tag_values : [
+ for role in v.roles : {
+ id = v.id
+ key = v.key
+ name = v.name
role = role
- tag = value_attrs.tag
+ tag = v.tag
}
]
])
- _tags_iam = flatten([
- for tag, attrs in local.tags : [
- for role in keys(attrs.iam) : {
- role = role
- tag = tag
- tag_id = attrs.id
+ _tag_values = flatten([
+ for k, v in local.tags : [
+ for vk, vv in v.values : {
+ description = vv.description,
+ key = "${k}/${vk}"
+ iam_bindings = keys(vv.iam_bindings)
+ iam_bindings_additive = keys(vv.iam_bindings_additive)
+ id = try(vv.id, null)
+ name = vk
+ # we only store keys here so we don't risk injecting dynamic values
+ roles = keys(vv.iam)
+ tag = k
+ tag_id = v.id
+ tag_network = try(v.network, null) != null
}
]
])
- tag_values = {
- for t in local._tag_values : t.key => t
+ tag_iam = {
+ for t in local._tag_iam : "${t.tag}:${t.role}" => t
+ }
+ tag_iam_bindings = merge([
+ for k, v in local.tags : {
+ for bk in keys(v.iam_bindings) : "${k}:${bk}" => {
+ binding = bk
+ tag = k
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_iam_bindings_additive = merge([
+ for k, v in local.tags : {
+ for bk in keys(v.iam_bindings_additive) : "${k}:${bk}" => {
+ binding = bk
+ tag = k
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_value_iam = {
+ for v in local._tag_value_iam : "${v.key}:${v.role}" => v
}
- tag_values_iam = {
- for t in local._tag_values_iam : "${t.key}:${t.role}" => t
+ tag_value_iam_bindings = merge([
+ for k, v in local.tag_values : {
+ for bk in v.iam_bindings : "${k}:${bk}" => {
+ binding = bk
+ id = v.id
+ key = k
+ name = v.name
+ tag = v.tag
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_value_iam_bindings_additive = merge([
+ for k, v in local.tag_values : {
+ for bk in v.iam_bindings_additive : "${k}:${bk}" => {
+ binding = bk
+ id = v.id
+ key = k
+ name = v.name
+ tag = v.tag
+ tag_id = v.id
+ }
+ }
+ ]...)
+ tag_values = {
+ for v in local._tag_values : v.key => v
}
tags = merge(var.tags, var.network_tags)
- tags_iam = {
- for t in local._tags_iam : "${t.tag}:${t.role}" => t
- }
}
# keys
@@ -82,7 +128,7 @@ resource "google_tags_tag_key" "default" {
}
resource "google_tags_tag_key_iam_binding" "default" {
- for_each = local.tags_iam
+ for_each = local.tag_iam
tag_key = (
each.value.tag_id == null
? google_tags_tag_key.default[each.value.tag].id
@@ -94,6 +140,30 @@ resource "google_tags_tag_key_iam_binding" "default" {
)
}
+resource "google_tags_tag_key_iam_binding" "bindings" {
+ for_each = local.tag_iam_bindings
+ tag_key = (
+ each.value.tag_id == null
+ ? google_tags_tag_key.default[each.value.tag].id
+ : each.value.tag_id
+ )
+ role = local.tags[each.value.tag]["iam_bindings"][each.value.binding].role
+ members = (
+ local.tags[each.value.tag]["iam_bindings"][each.value.binding].members
+ )
+}
+
+resource "google_tags_tag_key_iam_member" "bindings" {
+ for_each = local.tag_iam_bindings_additive
+ tag_key = (
+ each.value.tag_id == null
+ ? google_tags_tag_key.default[each.value.tag].id
+ : each.value.tag_id
+ )
+ role = local.tags[each.value.tag]["iam_bindings_additive"][each.value.binding].role
+ member = local.tags[each.value.tag]["iam_bindings_additive"][each.value.binding].member
+}
+
# values
resource "google_tags_tag_value" "default" {
@@ -108,7 +178,7 @@ resource "google_tags_tag_value" "default" {
}
resource "google_tags_tag_value_iam_binding" "default" {
- for_each = local.tag_values_iam
+ for_each = local.tag_value_iam
tag_value = (
each.value.id == null
? google_tags_tag_value.default[each.value.key].id
@@ -121,6 +191,36 @@ resource "google_tags_tag_value_iam_binding" "default" {
)
}
+resource "google_tags_tag_value_iam_binding" "bindings" {
+ for_each = local.tag_value_iam_bindings
+ tag_value = (
+ each.value.id == null
+ ? google_tags_tag_value.default[each.value.key].id
+ : each.value.id
+ )
+ role = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings"][each.value.binding].role
+ )
+ members = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings"][each.value.binding].members
+ )
+}
+
+resource "google_tags_tag_value_iam_member" "bindings" {
+ for_each = local.tag_value_iam_bindings_additive
+ tag_value = (
+ each.value.id == null
+ ? google_tags_tag_value.default[each.value.key].id
+ : each.value.id
+ )
+ role = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings_additive"][each.value.binding].role
+ )
+ member = (
+ local.tags[each.value.tag]["values"][each.value.name]["iam_bindings_additive"][each.value.binding].member
+ )
+}
+
# bindings
resource "google_tags_tag_binding" "binding" {
diff --git a/modules/project/variables-tags.tf b/modules/project/variables-tags.tf
index 8914aae6e6..ac73f03fd8 100644
--- a/modules/project/variables-tags.tf
+++ b/modules/project/variables-tags.tf
@@ -19,11 +19,47 @@ variable "network_tags" {
type = map(object({
description = optional(string, "Managed by the Terraform project module.")
iam = optional(map(list(string)), {})
- id = optional(string)
- network = string # project_id/vpc_name
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
+ network = string # project_id/vpc_name
values = optional(map(object({
description = optional(string, "Managed by the Terraform project module.")
iam = optional(map(list(string)), {})
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
})), {})
}))
nullable = false
@@ -45,7 +81,8 @@ variable "network_tags" {
variable "tag_bindings" {
description = "Tag bindings for this project, in key => tag value id format."
type = map(string)
- default = null
+ # we need default null here for the project factory module
+ default = null
}
variable "tags" {
@@ -53,11 +90,47 @@ variable "tags" {
type = map(object({
description = optional(string, "Managed by the Terraform project module.")
iam = optional(map(list(string)), {})
- id = optional(string)
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
values = optional(map(object({
description = optional(string, "Managed by the Terraform project module.")
iam = optional(map(list(string)), {})
- id = optional(string)
+ iam_bindings = optional(map(object({
+ members = list(string)
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ iam_bindings_additive = optional(map(object({
+ member = string
+ role = string
+ condition = optional(object({
+ expression = string
+ title = string
+ description = optional(string)
+ }))
+ })), {})
+ id = optional(string)
})), {})
}))
nullable = false
diff --git a/tests/modules/organization/examples/tags.yaml b/tests/modules/organization/examples/tags.yaml
index bed4b46188..cce3674346 100644
--- a/tests/modules/organization/examples/tags.yaml
+++ b/tests/modules/organization/examples/tags.yaml
@@ -23,11 +23,20 @@ values:
purpose_data: null
short_name: environment
timeouts: null
+ module.org.google_tags_tag_key_iam_binding.bindings["environment:viewer"]:
+ condition: []
+ members:
+ - group:gcp-support@example.org
+ role: roles/resourcemanager.tagViewer
module.org.google_tags_tag_key_iam_binding.default["environment:roles/resourcemanager.tagAdmin"]:
condition: []
members:
- group:organization-admins@example.org
role: roles/resourcemanager.tagAdmin
+ module.org.google_tags_tag_key_iam_member.bindings["environment:user_app1"]:
+ condition: []
+ member: group:app1-team@example.org
+ role: roles/resourcemanager.tagUser
module.org.google_tags_tag_value.default["environment/dev"]:
description: Managed by the Terraform organization module.
short_name: dev
@@ -36,14 +45,28 @@ values:
description: 'Environment: production.'
short_name: prod
timeouts: null
+ module.org.google_tags_tag_value_iam_binding.bindings["environment/prod:admin"]:
+ condition: []
+ members:
+ - group:gcp-support@example.org
+ role: roles/resourcemanager.tagAdmin
module.org.google_tags_tag_value_iam_binding.default["environment/prod:roles/resourcemanager.tagViewer"]:
condition: []
members:
- - group:organization-admins@example.org
+ - group:app1-team@example.org
role: roles/resourcemanager.tagViewer
+ module.org.google_tags_tag_value_iam_member.bindings["environment/dev:user_app2"]:
+ condition: []
+ member: group:app2-team@example.org
+ role: roles/resourcemanager.tagUser
counts:
google_tags_tag_binding: 1
google_tags_tag_key: 1
- google_tags_tag_key_iam_binding: 1
+ google_tags_tag_key_iam_binding: 2
+ google_tags_tag_key_iam_member: 1
google_tags_tag_value: 2
+ google_tags_tag_value_iam_binding: 2
+ google_tags_tag_value_iam_member: 1
+ modules: 1
+ resources: 10