From 0b5ed8b7ef117d7c3b0a5cc3aacdc12549b22b63 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sun, 20 Feb 2022 10:14:18 +0000 Subject: [PATCH] Add support for resource management tags and tag bindings (#552) * organization module * folder module * project module * fix project binding * use id instead of name for references * kms module * compute-vm * fix compute-vm --- modules/compute-vm/README.md | 5 +- modules/compute-vm/tags.tf | 21 +++ modules/compute-vm/variables.tf | 8 +- modules/folder/README.md | 34 +++++ modules/folder/tags.tf | 21 +++ modules/folder/variables.tf | 6 + modules/kms/README.md | 1 + modules/kms/tags.tf | 21 +++ modules/kms/variables.tf | 6 + modules/organization/README.md | 41 +++++- modules/organization/outputs.tf | 23 +++- modules/organization/tags.tf | 121 ++++++++++++++++++ modules/organization/variables.tf | 19 +++ modules/project/README.md | 33 +++++ modules/project/tags.tf | 21 +++ modules/project/variables.tf | 6 + tests/modules/organization/fixture/main.tf | 12 +- .../modules/organization/fixture/variables.tf | 61 ++++----- tests/modules/organization/test_plan_tags.py | 77 +++++++++++ 19 files changed, 487 insertions(+), 50 deletions(-) create mode 100644 modules/compute-vm/tags.tf create mode 100644 modules/folder/tags.tf create mode 100644 modules/kms/tags.tf create mode 100644 modules/organization/tags.tf create mode 100644 modules/project/tags.tf create mode 100644 tests/modules/organization/test_plan_tags.py diff --git a/modules/compute-vm/README.md b/modules/compute-vm/README.md index fb116641..81cbee2c 100644 --- a/modules/compute-vm/README.md +++ b/modules/compute-vm/README.md @@ -301,7 +301,7 @@ module "instance-group" { | [name](variables.tf#L160) | Instance name. | string | ✓ | | | [network_interfaces](variables.tf#L174) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | list(object({…})) | ✓ | | | [project_id](variables.tf#L201) | Project id. | string | ✓ | | -| [zone](variables.tf#L254) | Compute zone. | string | ✓ | | +| [zone](variables.tf#L260) | Compute zone. | string | ✓ | | | [attached_disk_defaults](variables.tf#L17) | Defaults for attached disks options. | object({…}) | | {…} | | [attached_disks](variables.tf#L32) | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | list(object({…})) | | [] | | [boot_disk](variables.tf#L58) | Boot disk properties. | object({…}) | | {…} | @@ -326,7 +326,8 @@ module "instance-group" { | [service_account_create](variables.tf#L224) | Auto-create service account. | bool | | false | | [service_account_scopes](variables.tf#L232) | Scopes applied to service account. | list(string) | | [] | | [shielded_config](variables.tf#L238) | Shielded VM configuration of the instances. | object({…}) | | null | -| [tags](variables.tf#L248) | Instance tags. | list(string) | | [] | +| [tag_bindings](variables.tf#L248) | Tag bindings for this instance, in key => tag value id format. | map(string) | | null | +| [tags](variables.tf#L254) | Instance network tags for firewall rule targets. | list(string) | | [] | ## Outputs diff --git a/modules/compute-vm/tags.tf b/modules/compute-vm/tags.tf new file mode 100644 index 00000000..a9001323 --- /dev/null +++ b/modules/compute-vm/tags.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_tags_tag_binding" "binding" { + for_each = var.create_template ? {} : coalesce(var.tag_bindings, {}) + parent = "//compute.googleapis.com/${google_compute_instance.default.0.id}" + tag_value = each.value +} diff --git a/modules/compute-vm/variables.tf b/modules/compute-vm/variables.tf index f82d75b1..653c66da 100644 --- a/modules/compute-vm/variables.tf +++ b/modules/compute-vm/variables.tf @@ -245,8 +245,14 @@ variable "shielded_config" { default = null } +variable "tag_bindings" { + description = "Tag bindings for this instance, in key => tag value id format." + type = map(string) + default = null +} + variable "tags" { - description = "Instance tags." + description = "Instance network tags for firewall rule targets." type = list(string) default = [] } diff --git a/modules/folder/README.md b/modules/folder/README.md index 11a8c3f7..4f6898b9 100644 --- a/modules/folder/README.md +++ b/modules/folder/README.md @@ -216,6 +216,38 @@ module "folder2" { # tftest modules=2 resources=6 ``` +## Tags + +Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. + +```hcl +module "org" { + source = "./modules/organization" + organization_id = var.organization_id + tags = { + environment = { + description = "Environment specification." + iam = null + values = { + dev = null + prod = null + } + } + } +} + +module "folder" { + source = "./modules/folder" + name = "Test" + parent = module.org.organization_id + tag_bindings = { + env-prod = module.org.tag_values["environment/prod"].id + foo = "tagValues/12345678" + } +} +# tftest modules=2 resources=6 +``` + @@ -229,6 +261,7 @@ module "folder2" { | [main.tf](./main.tf) | Module-level locals and resources. | google_essential_contacts_contact · google_folder | | [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | google_folder_organization_policy | | [outputs.tf](./outputs.tf) | Module outputs. | | +| [tags.tf](./tags.tf) | None | google_tags_tag_binding | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -250,6 +283,7 @@ module "folder2" { | [parent](variables.tf#L118) | Parent in folders/folder_id or organizations/org_id format. | string | | null | | [policy_boolean](variables.tf#L128) | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | [policy_list](variables.tf#L135) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({…})) | | {} | +| [tag_bindings](variables.tf#L147) | Tag bindings for this folder, in key => tag value id format. | map(string) | | null | ## Outputs diff --git a/modules/folder/tags.tf b/modules/folder/tags.tf new file mode 100644 index 00000000..2cd2f2fd --- /dev/null +++ b/modules/folder/tags.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_tags_tag_binding" "binding" { + for_each = coalesce(var.tag_bindings, {}) + parent = "//cloudresourcemanager.googleapis.com/${local.folder.id}" + tag_value = each.value +} diff --git a/modules/folder/variables.tf b/modules/folder/variables.tf index 78857411..a3f32e37 100644 --- a/modules/folder/variables.tf +++ b/modules/folder/variables.tf @@ -143,3 +143,9 @@ variable "policy_list" { default = {} nullable = false } + +variable "tag_bindings" { + description = "Tag bindings for this folder, in key => tag value id format." + type = map(string) + default = null +} diff --git a/modules/kms/README.md b/modules/kms/README.md index 2052c531..af1f60e9 100644 --- a/modules/kms/README.md +++ b/modules/kms/README.md @@ -95,6 +95,7 @@ module "kms" { | [key_purpose_defaults](variables.tf#L53) | Defaults used for key purpose when not defined at the key level. If purpose is not `ENCRYPT_DECRYPT` (the default), `version_template.algorithm` is required. | object({…}) | | {…} | | [keyring_create](variables.tf#L78) | Set to false to manage keys and IAM bindings in an existing keyring. | bool | | true | | [keys](variables.tf#L84) | Key names and base attributes. Set attributes to null if not needed. | map(object({…})) | | {} | +| [tag_bindings](variables.tf#L98) | Tag bindings for this keyring, in key => tag value id format. | map(string) | | null | ## Outputs diff --git a/modules/kms/tags.tf b/modules/kms/tags.tf new file mode 100644 index 00000000..894c28aa --- /dev/null +++ b/modules/kms/tags.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_tags_tag_binding" "binding" { + for_each = coalesce(var.tag_bindings, {}) + parent = "//cloudresourcemanager.googleapis.com/${local.keyring.id}" + tag_value = each.value +} diff --git a/modules/kms/variables.tf b/modules/kms/variables.tf index 191f3846..f5f0fb1b 100644 --- a/modules/kms/variables.tf +++ b/modules/kms/variables.tf @@ -94,3 +94,9 @@ variable "project_id" { description = "Project id where the keyring will be created." type = string } + +variable "tag_bindings" { + description = "Tag bindings for this keyring, in key => tag value id format." + type = map(string) + default = null +} diff --git a/modules/organization/README.md b/modules/organization/README.md index a7f79d7b..7aee0151 100644 --- a/modules/organization/README.md +++ b/modules/organization/README.md @@ -220,6 +220,7 @@ module "org" { ``` ## Custom Roles + ```hcl module "org" { source = "./modules/organization" @@ -236,6 +237,39 @@ module "org" { # tftest modules=1 resources=2 ``` +## Tags + +Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. + +```hcl +module "org" { + source = "./modules/organization" + organization_id = var.organization_id + tags = { + environment = { + description = "Environment specification." + iam = { + "roles/resourcemanager.tagAdmin" = ["group:admins@example.com"] + } + values = { + dev = null + prod = { + description = "Environment: production." + iam = { + "roles/resourcemanager.tagViewer" = ["user:user1@example.com"] + } + } + } + } + } + tag_bindings = { + env-prod = module.org.tag_values["environment/prod"].id + foo = "tagValues/12345678" + } +} +# tftest modules=1 resources=7 +``` + @@ -249,6 +283,7 @@ module "org" { | [main.tf](./main.tf) | Module-level locals and resources. | google_essential_contacts_contact | | [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | google_organization_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 | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | @@ -273,6 +308,8 @@ module "org" { | [logging_sinks](variables.tf#L129) | Logging sinks to create for this organization. | map(object({…})) | | {} | | [policy_boolean](variables.tf#L160) | Map of boolean org policies and enforcement value, set value to null for policy restore. | map(bool) | | {} | | [policy_list](variables.tf#L167) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | map(object({…})) | | {} | +| [tag_bindings](variables.tf#L179) | Tag bindings for this organization, in key => tag value id format. | map(string) | | null | +| [tags](variables.tf#L185) | Tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | null | ## Outputs @@ -283,6 +320,8 @@ module "org" { | [firewall_policies](outputs.tf#L36) | Map of firewall policy resources created in the organization. | | | [firewall_policy_id](outputs.tf#L41) | Map of firewall policy ids created in the organization. | | | [organization_id](outputs.tf#L46) | Organization id dependent on module resources. | | -| [sink_writer_identities](outputs.tf#L60) | Writer identities created for each sink. | | +| [sink_writer_identities](outputs.tf#L64) | Writer identities created for each sink. | | +| [tag_keys](outputs.tf#L72) | Tag key resources. | | +| [tag_values](outputs.tf#L79) | Tag value resources. | | diff --git a/modules/organization/outputs.tf b/modules/organization/outputs.tf index c1f501d8..1679a1d7 100644 --- a/modules/organization/outputs.tf +++ b/modules/organization/outputs.tf @@ -53,13 +53,32 @@ output "organization_id" { google_organization_iam_member.additive, google_organization_iam_policy.authoritative, google_organization_policy.boolean, - google_organization_policy.list + google_organization_policy.list, + google_tags_tag_key.default, + google_tags_tag_key_iam_binding.default, + google_tags_tag_value.default, + google_tags_tag_value_iam_binding.default, ] } output "sink_writer_identities" { description = "Writer identities created for each sink." value = { - for name, sink in google_logging_organization_sink.sink : name => sink.writer_identity + for name, sink in google_logging_organization_sink.sink : + name => sink.writer_identity + } +} + +output "tag_keys" { + description = "Tag key resources." + value = { + for k, v in google_tags_tag_key.default : k => v + } +} + +output "tag_values" { + description = "Tag value resources." + value = { + for k, v in google_tags_tag_value.default : k => v } } diff --git a/modules/organization/tags.tf b/modules/organization/tags.tf new file mode 100644 index 00000000..7aa94cd1 --- /dev/null +++ b/modules/organization/tags.tf @@ -0,0 +1,121 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + _tag_values = flatten([ + for tag, attrs in local.tags : [ + for value, value_attrs in coalesce(attrs.values, {}) : { + description = coalesce( + value_attrs == null ? null : value_attrs.description, + "Managed by the Terraform organization module." + ) + key = "${tag}/${value}" + name = value + roles = keys(coalesce( + value_attrs == null ? null : value_attrs.iam, {} + )) + tag = tag + } + ] + ]) + _tag_values_iam = flatten([ + for key, value_attrs in local.tag_values : [ + for role in value_attrs.roles : { + key = value_attrs.key + name = value_attrs.name + role = role + tag = value_attrs.tag + } + ] + ]) + _tags_iam = flatten([ + for tag, attrs in local.tags : [ + for role in keys(coalesce(attrs.iam, {})) : { + role = role + tag = tag + } + ] + ]) + tag_values = { + for t in local._tag_values : t.key => t + } + tag_values_iam = { + for t in local._tag_values_iam : "${t.key}:${t.role}" => t + } + tags = { + for k, v in coalesce(var.tags, {}) : + k => v == null ? { description = null, iam = {}, values = null } : v + } + tags_iam = { + for t in local._tags_iam : "${t.tag}:${t.role}" => t + } +} + +# keys + +resource "google_tags_tag_key" "default" { + for_each = local.tags + parent = var.organization_id + short_name = each.key + description = coalesce( + each.value.description, + "Managed by the Terraform organization module." + ) + depends_on = [ + google_organization_iam_binding.authoritative, + google_organization_iam_member.additive, + google_organization_iam_policy.authoritative, + ] +} + +resource "google_tags_tag_key_iam_binding" "default" { + for_each = local.tags_iam + tag_key = google_tags_tag_key.default[each.value.tag].id + role = each.value.role + members = coalesce( + local.tags[each.value.tag]["iam"][each.value.role], [] + ) +} + +# values + +resource "google_tags_tag_value" "default" { + for_each = local.tag_values + parent = google_tags_tag_key.default[each.value.tag].id + short_name = each.value.name + description = coalesce( + each.value.description, + "Managed by the Terraform organization module." + ) +} + +resource "google_tags_tag_value_iam_binding" "default" { + for_each = local.tag_values_iam + tag_value = google_tags_tag_value.default[each.value.key].id + role = each.value.role + members = coalesce( + local.tags[each.value.tag]["values"][each.value.name]["iam"][each.value.role], + [] + ) +} + +# bindings + +resource "google_tags_tag_binding" "binding" { + for_each = coalesce(var.tag_bindings, {}) + parent = "//cloudresourcemanager.googleapis.com/${var.organization_id}" + tag_value = each.value +} diff --git a/modules/organization/variables.tf b/modules/organization/variables.tf index 1ed117cf..9ffce95c 100644 --- a/modules/organization/variables.tf +++ b/modules/organization/variables.tf @@ -175,3 +175,22 @@ variable "policy_list" { default = {} nullable = false } + +variable "tag_bindings" { + description = "Tag bindings for this organization, in key => tag value id format." + type = map(string) + default = null +} + +variable "tags" { + description = "Tags by key name. The `iam` attribute behaves like the similarly named one at module level." + type = map(object({ + description = string + iam = map(list(string)) + values = map(object({ + description = string + iam = map(list(string)) + })) + })) + default = null +} diff --git a/modules/project/README.md b/modules/project/README.md index 864b6c26..e4f21391 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -207,6 +207,37 @@ module "project" { # tftest modules=1 resources=7 ``` +## Tags + +Refer to the [Creating and managing tags](https://cloud.google.com/resource-manager/docs/tags/tags-creating-and-managing) documentation for details on usage. + +```hcl +module "org" { + source = "./modules/organization" + organization_id = var.organization_id + tags = { + environment = { + description = "Environment specification." + iam = null + values = { + dev = null + prod = null + } + } + } +} + +module "project" { + source = "./modules/project" + name = "test-project" + tag_bindings = { + env-prod = module.org.tag_values["environment/prod"].id + foo = "tagValues/12345678" + } +} +# tftest modules=2 resources=6 +``` + @@ -221,6 +252,7 @@ module "project" { | [outputs.tf](./outputs.tf) | Module outputs. | | | [service-accounts.tf](./service-accounts.tf) | Service identities and supporting resources. | google_kms_crypto_key_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_project_iam_member | +| [tags.tf](./tags.tf) | None | google_tags_tag_binding | | [variables.tf](./variables.tf) | Module variables. | | | [versions.tf](./versions.tf) | Version pins. | | | [vpc-sc.tf](./vpc-sc.tf) | VPC-SC project-level perimeter configuration. | google_access_context_manager_service_perimeter_resource | @@ -260,6 +292,7 @@ module "project" { | [shared_vpc_host_config](variables.tf#L230) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | object({…}) | | null | | [shared_vpc_service_config](variables.tf#L239) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | object({…}) | | null | | [skip_delete](variables.tf#L249) | Allows the underlying resources to be destroyed without destroying the project itself. | bool | | false | +| [tag_bindings](variables.tf#L255) | Tag bindings for this project, in key => tag value id format. | map(string) | | null | ## Outputs diff --git a/modules/project/tags.tf b/modules/project/tags.tf new file mode 100644 index 00000000..683143bd --- /dev/null +++ b/modules/project/tags.tf @@ -0,0 +1,21 @@ +/** + * Copyright 2022 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +resource "google_tags_tag_binding" "binding" { + for_each = coalesce(var.tag_bindings, {}) + parent = "//cloudresourcemanager.googleapis.com/projects/${local.project.number}" + tag_value = each.value +} diff --git a/modules/project/variables.tf b/modules/project/variables.tf index 7aa291f5..578f9d23 100644 --- a/modules/project/variables.tf +++ b/modules/project/variables.tf @@ -251,3 +251,9 @@ variable "skip_delete" { type = bool default = false } + +variable "tag_bindings" { + description = "Tag bindings for this project, in key => tag value id format." + type = map(string) + default = null +} diff --git a/tests/modules/organization/fixture/main.tf b/tests/modules/organization/fixture/main.tf index 3a57a790..04ae4adf 100644 --- a/tests/modules/organization/fixture/main.tf +++ b/tests/modules/organization/fixture/main.tf @@ -18,16 +18,18 @@ module "test" { source = "../../../../modules/organization" organization_id = "organizations/1234567890" custom_roles = var.custom_roles + firewall_policies = var.firewall_policies + firewall_policy_association = var.firewall_policy_association + firewall_policy_factory = var.firewall_policy_factory group_iam = var.group_iam iam = var.iam iam_additive = var.iam_additive iam_additive_members = var.iam_additive_members iam_audit_config = var.iam_audit_config - policy_boolean = var.policy_boolean - policy_list = var.policy_list - firewall_policies = var.firewall_policies - firewall_policy_association = var.firewall_policy_association - firewall_policy_factory = var.firewall_policy_factory logging_sinks = var.logging_sinks logging_exclusions = var.logging_exclusions + policy_boolean = var.policy_boolean + policy_list = var.policy_list + tag_bindings = var.tag_bindings + tags = var.tags } diff --git a/tests/modules/organization/fixture/variables.tf b/tests/modules/organization/fixture/variables.tf index 6881f15e..1d7ca88d 100644 --- a/tests/modules/organization/fixture/variables.tf +++ b/tests/modules/organization/fixture/variables.tf @@ -15,89 +15,62 @@ */ variable "custom_roles" { - type = map(list(string)) + type = any default = {} } variable "group_iam" { - type = map(list(string)) + type = any default = {} } variable "iam" { - type = map(list(string)) + type = any default = {} } variable "iam_additive" { - type = map(list(string)) + type = any default = {} } variable "iam_additive_members" { - type = map(list(string)) + type = any default = {} } variable "iam_audit_config" { - type = map(map(list(string))) + type = any default = {} } variable "policy_boolean" { - type = map(bool) + type = any default = {} } variable "policy_list" { - type = map(object({ - inherit_from_parent = bool - suggested_value = string - status = bool - values = list(string) - })) + type = any default = {} } variable "firewall_policies" { - type = map(map(object({ - description = string - direction = string - action = string - priority = number - ranges = list(string) - ports = map(list(string)) - target_service_accounts = list(string) - target_resources = list(string) - logging = bool - }))) + type = any default = {} } variable "firewall_policy_association" { - type = map(string) + type = any default = {} } variable "firewall_policy_factory" { - type = object({ - cidr_file = string - policy_name = string - rules_file = string - }) + type = any default = null } variable "logging_sinks" { - type = map(object({ - destination = string - type = string - filter = string - iam = bool - include_children = bool - bq_partitioned_table = bool - exclusions = map(string) - })) + type = any default = {} } @@ -105,3 +78,13 @@ variable "logging_exclusions" { type = map(string) default = {} } + +variable "tag_bindings" { + type = any + default = null +} + +variable "tags" { + type = any + default = null +} diff --git a/tests/modules/organization/test_plan_tags.py b/tests/modules/organization/test_plan_tags.py new file mode 100644 index 00000000..849502f4 --- /dev/null +++ b/tests/modules/organization/test_plan_tags.py @@ -0,0 +1,77 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def test_keys(plan_runner): + 'Test tag keys.' + tags = '''{ + foo = null + bar = { + description = null + iam = null + values = null + } + foobar = { + description = "Foobar tag." + iam = { + "roles/resourcemanager.tagAdmin" = [ + "user:user1@example.com", "user:user2@example.com" + ] + } + values = { + one = null + two = { + description = "Foobar 2." + iam = { + "roles/resourcemanager.tagViewer" = [ + "user:user3@example.com" + ] + } + } + three = { + description = "Foobar 3." + iam = { + "roles/resourcemanager.tagViewer" = [ + "user:user3@example.com" + ] + "roles/resourcemanager.tagAdmin" = [ + "user:user4@example.com" + ] + } + } + } + } + }''' + _, resources = plan_runner(tags=tags) + assert len(resources) == 10 + resource_values = {} + for r in resources: + resource_values.setdefault(r['type'], []).append(r['values']) + assert len(resource_values['google_tags_tag_key']) == 3 + assert len(resource_values['google_tags_tag_value']) == 3 + result = [ + r['role'] for r in resource_values['google_tags_tag_value_iam_binding'] + ] + expected = [ + 'roles/resourcemanager.tagAdmin', 'roles/resourcemanager.tagViewer', + 'roles/resourcemanager.tagViewer' + ] + assert result == expected + + +def test_bindings(plan_runner): + 'Test tag bindings.' + tag_bindings = '{foo = "tagValues/123456789012"}' + _, resources = plan_runner(tag_bindings=tag_bindings) + assert len(resources) == 1