diff --git a/fast/stages/0-org-setup/schemas/folder.schema.json b/fast/stages/0-org-setup/schemas/folder.schema.json index 84a7071c5..c75ee7705 100644 --- a/fast/stages/0-org-setup/schemas/folder.schema.json +++ b/fast/stages/0-org-setup/schemas/folder.schema.json @@ -165,6 +165,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/0-org-setup/schemas/folder.schema.md b/fast/stages/0-org-setup/schemas/folder.schema.md index 996034e8b..3bf4e3635 100644 --- a/fast/stages/0-org-setup/schemas/folder.schema.md +++ b/fast/stages/0-org-setup/schemas/folder.schema.md @@ -54,6 +54,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **autokey_config**: *object*
*additional properties: false* - **project**: *string* diff --git a/fast/stages/0-org-setup/schemas/project.schema.json b/fast/stages/0-org-setup/schemas/project.schema.json index 3632daa1f..ff390091d 100644 --- a/fast/stages/0-org-setup/schemas/project.schema.json +++ b/fast/stages/0-org-setup/schemas/project.schema.json @@ -146,6 +146,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/0-org-setup/schemas/project.schema.md b/fast/stages/0-org-setup/schemas/project.schema.md index 3def29e9c..c7c8d88f8 100644 --- a/fast/stages/0-org-setup/schemas/project.schema.md +++ b/fast/stages/0-org-setup/schemas/project.schema.md @@ -49,6 +49,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **billing_account**: *string* - **billing_budgets**: *array* - items: *string* diff --git a/fast/stages/2-networking/schemas/folder.schema.json b/fast/stages/2-networking/schemas/folder.schema.json index 84a7071c5..c75ee7705 100644 --- a/fast/stages/2-networking/schemas/folder.schema.json +++ b/fast/stages/2-networking/schemas/folder.schema.json @@ -165,6 +165,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-networking/schemas/folder.schema.md b/fast/stages/2-networking/schemas/folder.schema.md index 996034e8b..3bf4e3635 100644 --- a/fast/stages/2-networking/schemas/folder.schema.md +++ b/fast/stages/2-networking/schemas/folder.schema.md @@ -54,6 +54,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **autokey_config**: *object*
*additional properties: false* - **project**: *string* diff --git a/fast/stages/2-networking/schemas/project.schema.json b/fast/stages/2-networking/schemas/project.schema.json index 3632daa1f..ff390091d 100644 --- a/fast/stages/2-networking/schemas/project.schema.json +++ b/fast/stages/2-networking/schemas/project.schema.json @@ -146,6 +146,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-networking/schemas/project.schema.md b/fast/stages/2-networking/schemas/project.schema.md index 3def29e9c..c7c8d88f8 100644 --- a/fast/stages/2-networking/schemas/project.schema.md +++ b/fast/stages/2-networking/schemas/project.schema.md @@ -49,6 +49,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **billing_account**: *string* - **billing_budgets**: *array* - items: *string* diff --git a/fast/stages/2-project-factory/schemas/folder.schema.json b/fast/stages/2-project-factory/schemas/folder.schema.json index 84a7071c5..c75ee7705 100644 --- a/fast/stages/2-project-factory/schemas/folder.schema.json +++ b/fast/stages/2-project-factory/schemas/folder.schema.json @@ -165,6 +165,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-project-factory/schemas/folder.schema.md b/fast/stages/2-project-factory/schemas/folder.schema.md index 996034e8b..3bf4e3635 100644 --- a/fast/stages/2-project-factory/schemas/folder.schema.md +++ b/fast/stages/2-project-factory/schemas/folder.schema.md @@ -54,6 +54,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **autokey_config**: *object*
*additional properties: false* - **project**: *string* diff --git a/fast/stages/2-project-factory/schemas/project.schema.json b/fast/stages/2-project-factory/schemas/project.schema.json index 3632daa1f..ff390091d 100644 --- a/fast/stages/2-project-factory/schemas/project.schema.json +++ b/fast/stages/2-project-factory/schemas/project.schema.json @@ -146,6 +146,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-project-factory/schemas/project.schema.md b/fast/stages/2-project-factory/schemas/project.schema.md index 3def29e9c..c7c8d88f8 100644 --- a/fast/stages/2-project-factory/schemas/project.schema.md +++ b/fast/stages/2-project-factory/schemas/project.schema.md @@ -49,6 +49,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **billing_account**: *string* - **billing_budgets**: *array* - items: *string* diff --git a/fast/stages/2-security/schemas/folder.schema.json b/fast/stages/2-security/schemas/folder.schema.json index 84a7071c5..c75ee7705 100644 --- a/fast/stages/2-security/schemas/folder.schema.json +++ b/fast/stages/2-security/schemas/folder.schema.json @@ -165,6 +165,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-security/schemas/folder.schema.md b/fast/stages/2-security/schemas/folder.schema.md index 996034e8b..3bf4e3635 100644 --- a/fast/stages/2-security/schemas/folder.schema.md +++ b/fast/stages/2-security/schemas/folder.schema.md @@ -54,6 +54,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **autokey_config**: *object*
*additional properties: false* - **project**: *string* diff --git a/fast/stages/2-security/schemas/project.schema.json b/fast/stages/2-security/schemas/project.schema.json index 3632daa1f..ff390091d 100644 --- a/fast/stages/2-security/schemas/project.schema.json +++ b/fast/stages/2-security/schemas/project.schema.json @@ -146,6 +146,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/fast/stages/2-security/schemas/project.schema.md b/fast/stages/2-security/schemas/project.schema.md index 3def29e9c..c7c8d88f8 100644 --- a/fast/stages/2-security/schemas/project.schema.md +++ b/fast/stages/2-security/schemas/project.schema.md @@ -49,6 +49,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **billing_account**: *string* - **billing_budgets**: *array* - items: *string* diff --git a/modules/iam-service-account/main.tf b/modules/iam-service-account/main.tf index e69290a28..6cbf78dd3 100644 --- a/modules/iam-service-account/main.tf +++ b/modules/iam-service-account/main.tf @@ -101,6 +101,6 @@ resource "google_service_account" "service_account" { resource "google_tags_tag_binding" "binding" { for_each = local.tag_bindings - parent = "//iam.googleapis.com/projects/${coalesce(var.project_number, var.project_id)}/serviceAccounts/${local.service_account.unique_id}" + parent = "//iam.googleapis.com/projects/${coalesce(var.project_number, local.project_id)}/serviceAccounts/${local.service_account.unique_id}" tag_value = templatestring(local._tag_bindings[each.key], var.context.tag_vars) } diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 4180e7e22..a26bc6a03 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -968,7 +968,7 @@ module "project-factory" { basepath = "data" } } -# tftest modules=7 resources=31 files=test-0,test-1,test-2 inventory=test-1.yaml +# tftest modules=10 resources=36 files=test-0,test-1,test-2 inventory=test-1.yaml ``` ```yaml @@ -995,6 +995,16 @@ tags: roles/resourcemanager.tagUser: - $iam_principals:tag-test - $iam_principals:service_accounts/test-1/tag-test +service_accounts: + tag-test: + tag_bindings: + project-level: $tag_values:test-0/context/project-factory +automation: + project: test-0 + service_accounts: + auto-tag-test: + tag_bindings: + project-level: $tag_values:test-0/context/project-factory # tftest-file id=test-0 path=data/projects/test-0.yaml ``` diff --git a/modules/project-factory/automation.tf b/modules/project-factory/automation.tf index 8d458a706..46f98caca 100644 --- a/modules/project-factory/automation.tf +++ b/modules/project-factory/automation.tf @@ -126,9 +126,18 @@ module "automation-bucket" { } module "automation-service-accounts" { - source = "../iam-service-account" - for_each = local.automation_sas - project_id = each.value.automation_project + source = "../iam-service-account" + for_each = local.automation_sas + project_id = each.value.automation_project + project_number = ( + each.value.automation_project == null + ? null + : lookup( + local.ctx_project_numbers, + trimprefix(each.value.automation_project, "$project_ids:"), + null + ) + ) prefix = each.value.prefix name = each.value.name description = lookup(each.value, "description", null) @@ -143,6 +152,11 @@ module "automation-service-accounts" { local.ctx.iam_principals, local.projects_sas_iam_emails ) + tag_vars = { + projects = merge(try(local.ctx.tag_vars.projects, {}), local.tag_vars_projects) + organization = try(local.ctx.tag_vars.organization, {}) + } + tag_values = local.ctx_tag_values }) iam = lookup(each.value, "iam", {}) iam_bindings = lookup(each.value, "iam_bindings", {}) @@ -154,6 +168,7 @@ module "automation-service-accounts" { # iam_sa_roles = lookup(each.value, "iam_sa_roles", {}) # we don't interpolate buckets here as we can't use a dynamic key iam_storage_roles = lookup(each.value, "iam_storage_roles", {}) + tag_bindings = lookup(each.value, "tag_bindings", {}) } module "automation-service-accounts-iam" { diff --git a/modules/project-factory/projects-service-accounts.tf b/modules/project-factory/projects-service-accounts.tf index 17f1c4de4..53e0c8df3 100644 --- a/modules/project-factory/projects-service-accounts.tf +++ b/modules/project-factory/projects-service-accounts.tf @@ -80,10 +80,11 @@ module "service-accounts" { for k in local.projects_service_accounts : "${k.project_key}/${k.name}" => k } - project_id = module.projects[each.value.project_key].project_id - name = each.value.name - description = each.value.description - display_name = each.value.display_name + project_id = module.projects[each.value.project_key].project_id + project_number = module.projects[each.value.project_key].number + name = each.value.name + description = each.value.description + display_name = each.value.display_name context = merge(local.ctx, { tag_vars = { projects = merge(try(local.ctx.tag_vars.projects, {}), local.tag_vars_projects) diff --git a/modules/project-factory/schemas/folder.schema.json b/modules/project-factory/schemas/folder.schema.json index 84a7071c5..c75ee7705 100644 --- a/modules/project-factory/schemas/folder.schema.json +++ b/modules/project-factory/schemas/folder.schema.json @@ -165,6 +165,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/modules/project-factory/schemas/folder.schema.md b/modules/project-factory/schemas/folder.schema.md index 996034e8b..3bf4e3635 100644 --- a/modules/project-factory/schemas/folder.schema.md +++ b/modules/project-factory/schemas/folder.schema.md @@ -54,6 +54,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **autokey_config**: *object*
*additional properties: false* - **project**: *string* diff --git a/modules/project-factory/schemas/project.schema.json b/modules/project-factory/schemas/project.schema.json index 3632daa1f..ff390091d 100644 --- a/modules/project-factory/schemas/project.schema.json +++ b/modules/project-factory/schemas/project.schema.json @@ -146,6 +146,9 @@ }, "iam_storage_roles": { "$ref": "#/$defs/iam_storage_roles" + }, + "tag_bindings": { + "$ref": "#/$defs/tag_bindings" } } } diff --git a/modules/project-factory/schemas/project.schema.md b/modules/project-factory/schemas/project.schema.md index 3def29e9c..c7c8d88f8 100644 --- a/modules/project-factory/schemas/project.schema.md +++ b/modules/project-factory/schemas/project.schema.md @@ -49,6 +49,7 @@ - **iam_project_roles**: *reference([iam_project_roles](#refs-iam_project_roles))* - **iam_sa_roles**: *reference([iam_sa_roles](#refs-iam_sa_roles))* - **iam_storage_roles**: *reference([iam_storage_roles](#refs-iam_storage_roles))* + - **tag_bindings**: *reference([tag_bindings](#refs-tag_bindings))* - **billing_account**: *string* - **billing_budgets**: *array* - items: *string* diff --git a/tests/modules/project_factory/examples/test-1.yaml b/tests/modules/project_factory/examples/test-1.yaml index dc66b6c71..af381697d 100644 --- a/tests/modules/project_factory/examples/test-1.yaml +++ b/tests/modules/project_factory/examples/test-1.yaml @@ -1,10 +1,10 @@ -# Copyright 2025 Google LLC +# Copyright 2026 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 +# https://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, @@ -13,6 +13,47 @@ # limitations under the License. values: + module.project-factory.module.automation-bucket["test-0/automation/tf-state"].google_storage_bucket.bucket[0]: + autoclass: [] + cors: [] + custom_placement_config: [] + default_event_based_hold: null + deletion_policy: DELETE + effective_labels: + goog-terraform-provisioned: 'true' + enable_object_retention: null + encryption: [] + force_destroy: false + hierarchical_namespace: [] + ip_filter: [] + labels: null + lifecycle_rule: [] + location: EU + logging: [] + name: foo-test-0-tf-state + project: test-0 + requester_pays: null + retention_policy: [] + storage_class: STANDARD + terraform_labels: + goog-terraform-provisioned: 'true' + timeouts: null + uniform_bucket_level_access: true + versioning: + - enabled: false + ? module.project-factory.module.automation-service-accounts["test-0/automation/auto-tag-test"].google_service_account.service_account[0] + : account_id: test-0-auto-tag-test + create_ignore_already_exists: null + deletion_policy: DELETE + description: null + disabled: false + display_name: Service account auto-tag-test for test-0. + email: test-0-auto-tag-test@test-0.iam.gserviceaccount.com + member: serviceAccount:test-0-auto-tag-test@test-0.iam.gserviceaccount.com + project: test-0 + timeouts: null + ? module.project-factory.module.automation-service-accounts["test-0/automation/auto-tag-test"].google_tags_tag_binding.binding["project-level"] + : timeouts: null module.project-factory.module.projects-iam["test-0"].google_project_iam_member.bindings["test_context"]: condition: - description: null @@ -31,8 +72,6 @@ values: tag_value: tagValues/1234567890 timeouts: null module.project-factory.module.projects-iam["test-1"].google_tags_tag_binding.binding["project-level"]: - # tag_value is undefined at plan time as it depends on the tag - # tag_value: $tag_values:test-0/context/project-factory timeouts: null module.project-factory.module.projects["test-0"].google_project.project[0]: auto_create_network: false @@ -65,24 +104,28 @@ values: project: foo-test-0 role: roles/container.defaultNodeServiceAgent module.project-factory.module.projects["test-0"].google_project_service.project_services["compute.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: foo-test-0 service: compute.googleapis.com timeouts: null ? module.project-factory.module.projects["test-0"].google_project_service.project_services["contactcenteraiplatform.googleapis.com"] - : disable_dependent_services: false + : deletion_policy: DELETE + disable_dependent_services: false disable_on_destroy: false project: foo-test-0 service: contactcenteraiplatform.googleapis.com timeouts: null module.project-factory.module.projects["test-0"].google_project_service.project_services["container.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: foo-test-0 service: container.googleapis.com timeouts: null module.project-factory.module.projects["test-0"].google_project_service.project_services["iam.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: foo-test-0 @@ -131,18 +174,21 @@ values: project: test-1 role: roles/compute.serviceAgent module.project-factory.module.projects["test-1"].google_project_service.project_services["compute.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: test-1 service: compute.googleapis.com timeouts: null ? module.project-factory.module.projects["test-1"].google_project_service.project_services["contactcenteraiplatform.googleapis.com"] - : disable_dependent_services: false + : deletion_policy: DELETE + disable_dependent_services: false disable_on_destroy: false project: test-1 service: contactcenteraiplatform.googleapis.com timeouts: null module.project-factory.module.projects["test-1"].google_project_service.project_services["iam.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: test-1 @@ -178,26 +224,43 @@ values: project: bar-test-2 role: roles/compute.serviceAgent module.project-factory.module.projects["test-2"].google_project_service.project_services["compute.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: bar-test-2 service: compute.googleapis.com timeouts: null module.project-factory.module.projects["test-2"].google_project_service.project_services["iam.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: bar-test-2 service: iam.googleapis.com timeouts: null module.project-factory.module.projects["test-2"].google_project_service.project_services["storage.googleapis.com"]: + deletion_policy: DELETE disable_dependent_services: false disable_on_destroy: false project: bar-test-2 service: storage.googleapis.com timeouts: null + module.project-factory.module.service-accounts["test-0/tag-test"].google_service_account.service_account[0]: + account_id: tag-test + create_ignore_already_exists: null + deletion_policy: DELETE + description: null + disabled: false + display_name: Terraform-managed. + email: tag-test@foo-test-0.iam.gserviceaccount.com + member: serviceAccount:tag-test@foo-test-0.iam.gserviceaccount.com + project: foo-test-0 + timeouts: null + module.project-factory.module.service-accounts["test-0/tag-test"].google_tags_tag_binding.binding["project-level"]: + timeouts: null module.project-factory.module.service-accounts["test-1/tag-test"].google_service_account.service_account[0]: account_id: tag-test create_ignore_already_exists: null + deletion_policy: DELETE description: null disabled: false display_name: Terraform-managed. @@ -219,12 +282,15 @@ counts: google_project_iam_member: 6 google_project_service: 10 google_project_service_identity: 3 - google_service_account: 1 + google_service_account: 3 + google_storage_bucket: 1 google_storage_project_service_account: 1 - google_tags_tag_binding: 2 + google_tags_tag_binding: 4 google_tags_tag_key: 1 google_tags_tag_value: 1 google_tags_tag_value_iam_binding: 1 - modules: 7 - resources: 31 + modules: 10 + resources: 36 terraform_data: 2 + +outputs: {}