diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index dcc705abd..ad25c47b3 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -50,7 +50,7 @@ The code is meant to be executed by a high level service account with powerful p ## Folder hierarchy -The hierarchy supports up to three levels of folders, which are defined via filesystem directories each including a `.config.yaml` files detailing their attributes. +The hierarchy supports up to four levels of folders, which are defined via filesystem directories each including a `.config.yaml` files detailing their attributes. The filesystem tree containing folder definitions is configured via the `factories_config.folders` variable, which sets the the path containing the YAML definitions for folders. It's also possible to configure the hierarchy via the `folders` variable, which is internally merged in with the factory definitions. @@ -462,7 +462,7 @@ module "project-factory" { } } } -# tftest files=t0,0,1,2,3,4,5,6,7,8,9,10 inventory=example.yaml +# tftest files=t0,0,1,2,2.1,2.2,2.3,3,4,5,6,7,8,9,10 inventory=example.yaml ``` A project template for GKE projects: @@ -508,6 +508,21 @@ parent: folders/5678901234 # tftest-file id=2 path=data/hierarchy/team-c/.config.yaml schema=folder.schema.json ``` +```yaml +name: Apps +# tftest-file id=2.1 path=data/hierarchy/team-c/apps/.config.yaml schema=folder.schema.json +``` + +```yaml +name: Test +# tftest-file id=2.2 path=data/hierarchy/team-c/apps/test/.config.yaml schema=folder.schema.json +``` + +```yaml +name: App X +# tftest-file id=2.3 path=data/hierarchy/team-c/apps/test/app-x/.config.yaml schema=folder.schema.json +``` + ```yaml name: App 0 factories_config: diff --git a/modules/project-factory/folders.tf b/modules/project-factory/folders.tf index 163f1107f..7e4f262ba 100644 --- a/modules/project-factory/folders.tf +++ b/modules/project-factory/folders.tf @@ -38,7 +38,8 @@ locals { folder_ids = merge( { for k, v in module.folder-1 : k => v.id }, { for k, v in module.folder-2 : k => v.id }, - { for k, v in module.folder-3 : k => v.id } + { for k, v in module.folder-3 : k => v.id }, + { for k, v in module.folder-4 : k => v.id } ) folders_input = { for key, data in local._folders_raw : key => merge(data, { @@ -184,3 +185,51 @@ module "folder-3-iam" { iam_principals = local.ctx_iam_principals }) } + +module "folder-4" { + source = "../folder" + for_each = { + for k, v in local.folders_input : k => v if v.level == 4 + } + parent = coalesce( + each.value.parent, "$folder_ids:${each.value.parent_key}" + ) + name = each.value.name + factories_config = { + org_policies = try(each.value.factories_config.org_policies, null) + scc_sha_custom_modules = try(each.value.factories_config.scc_sha_custom_modules, null) + } + org_policies = lookup(each.value, "org_policies", {}) + pam_entitlements = lookup(each.value, "pam_entitlements", {}) + tag_bindings = lookup(each.value, "tag_bindings", {}) + logging_data_access = lookup(each.value, "logging_data_access", {}) + context = merge(local.ctx, { + folder_ids = merge(local.ctx.folder_ids, { + for k, v in module.folder-3 : k => v.id + }) + }) + depends_on = [module.folder-3] +} + +module "folder-4-iam" { + source = "../folder" + for_each = { + for k, v in local.folders_input : k => v if v.level == 4 + } + id = module.folder-4[each.key].id + factories_config = { + # we do anything that can refer to IAM and custom roles in this call + pam_entitlements = try(each.value.factories_config.pam_entitlements, null) + } + folder_create = false + iam = lookup(each.value, "iam", {}) + iam_bindings = lookup(each.value, "iam_bindings", {}) + iam_bindings_additive = lookup(each.value, "iam_bindings_additive", {}) + iam_by_principals = lookup(each.value, "iam_by_principals", {}) + context = merge(local.ctx, { + folder_ids = merge(local.ctx.folder_ids, { + for k, v in module.folder-3 : k => v.id + }) + iam_principals = local.ctx_iam_principals + }) +} diff --git a/tests/modules/project_factory/examples/example.yaml b/tests/modules/project_factory/examples/example.yaml index 5b0e780b8..efb86f20c 100644 --- a/tests/modules/project_factory/examples/example.yaml +++ b/tests/modules/project_factory/examples/example.yaml @@ -37,6 +37,7 @@ values: storage_class: STANDARD terraform_labels: goog-terraform-provisioned: 'true' + timeouts: null uniform_bucket_level_access: true versioning: - enabled: false @@ -46,6 +47,7 @@ values: members: - serviceAccount:dev-tb-app0-0-rw@test-pf-teams-iac-0.iam.gserviceaccount.com role: roles/storage.objectCreator + timeouts: null ? module.project-factory.module.automation-bucket["dev-tb-app0-0/automation/tf-state"].google_storage_bucket_iam_binding.authoritative["roles/storage.objectViewer"] : bucket: test-pf-dev-tb-app0-0-tf-state condition: [] @@ -55,6 +57,7 @@ values: - serviceAccount:dev-tb-app0-0-ro@test-pf-teams-iac-0.iam.gserviceaccount.com - serviceAccount:dev-tb-app0-0-rw@test-pf-teams-iac-0.iam.gserviceaccount.com role: roles/storage.objectViewer + timeouts: null ? module.project-factory.module.automation-service-accounts["dev-tb-app0-0/automation/ro"].google_service_account.service_account[0] : account_id: dev-tb-app0-0-ro create_ignore_already_exists: null @@ -64,6 +67,7 @@ values: email: dev-tb-app0-0-ro@test-pf-teams-iac-0.iam.gserviceaccount.com member: serviceAccount:dev-tb-app0-0-ro@test-pf-teams-iac-0.iam.gserviceaccount.com project: test-pf-teams-iac-0 + timeouts: null ? module.project-factory.module.automation-service-accounts["dev-tb-app0-0/automation/rw"].google_service_account.service_account[0] : account_id: dev-tb-app0-0-rw create_ignore_already_exists: null @@ -73,6 +77,7 @@ values: email: dev-tb-app0-0-rw@test-pf-teams-iac-0.iam.gserviceaccount.com member: serviceAccount:dev-tb-app0-0-rw@test-pf-teams-iac-0.iam.gserviceaccount.com project: test-pf-teams-iac-0 + timeouts: null module.project-factory.module.billing-budgets[0].google_billing_budget.default["test-100"]: all_updates_rule: - disable_default_iam_recipients: true @@ -101,6 +106,7 @@ values: threshold_percent: 0.5 - spend_basis: CURRENT_SPEND threshold_percent: 0.75 + timeouts: null module.project-factory.module.billing-budgets[0].google_monitoring_notification_channel.default["billing-default"]: description: null display_name: Budget email notification billing-default. @@ -110,6 +116,7 @@ values: email_address: gcp-billing-admins@example.org project: foo-billing-audit sensitive_labels: [] + timeouts: null type: email user_labels: null module.project-factory.module.folder-1-iam["team-a"].google_folder_iam_binding.authoritative["roles/viewer"]: @@ -123,20 +130,24 @@ values: display_name: Team A parent: folders/5678901234 tags: null + timeouts: null module.project-factory.module.folder-1["team-b"].google_folder.folder[0]: deletion_protection: false display_name: Team B parent: folders/5678901234 tags: null + timeouts: null module.project-factory.module.folder-1["team-c"].google_folder.folder[0]: deletion_protection: false display_name: Team C parent: folders/5678901234 tags: null + timeouts: null module.project-factory.module.folder-2["team-a/app-0"].google_folder.folder[0]: deletion_protection: false display_name: App 0 tags: null + timeouts: null module.project-factory.module.folder-2["team-a/app-0"].google_org_policy_policy.default["compute.disableSerialPortAccess"]: dry_run_spec: [] spec: @@ -149,6 +160,7 @@ values: enforce: 'FALSE' parameters: null values: [] + timeouts: null ? module.project-factory.module.folder-2["team-a/app-0"].google_privileged_access_manager_entitlement.default["app-0-admins"] : additional_notification_targets: [] approval_workflow: @@ -176,16 +188,35 @@ values: - not_mandatory: [] unstructured: - {} + timeouts: null module.project-factory.module.folder-2["team-b/app-0"].google_folder.folder[0]: deletion_protection: false display_name: App 0 tags: null + timeouts: null module.project-factory.module.folder-2["team-b/app-0"].google_tags_tag_binding.binding["drs-allow-all"]: tag_value: tagValues/123456 + timeouts: null + module.project-factory.module.folder-2["team-c/apps"].google_folder.folder[0]: + deletion_protection: false + display_name: Apps + tags: null + timeouts: null + module.project-factory.module.folder-3["team-c/apps/test"].google_folder.folder[0]: + deletion_protection: false + display_name: Test + tags: null + timeouts: null + module.project-factory.module.folder-4["team-c/apps/test/app-x"].google_folder.folder[0]: + deletion_protection: false + display_name: App X + tags: null + timeouts: null ? module.project-factory.module.projects-iam["dev-ta-app0-be"].google_compute_shared_vpc_service_project.shared_vpc_service[0] : deletion_policy: null host_project: $project_ids:dev-spoke-0 service_project: test-pf-dev-ta-app0-be + timeouts: null ? module.project-factory.module.projects-iam["dev-ta-app0-be"].google_kms_crypto_key_iam_member.service_agent_cmek["key-0.compute-system"] : condition: [] crypto_key_id: projects/kms-central-prj/locations/europe-west1/keyRings/my-keyring/cryptoKeys/ew1-compute @@ -225,6 +256,7 @@ values: - not_mandatory: [] unstructured: - {} + timeouts: null ? module.project-factory.module.projects-iam["dev-ta-app0-be"].google_project_iam_binding.authoritative["roles/cloudkms.cryptoKeyEncrypterDecrypter"] : condition: [] project: test-pf-dev-ta-app0-be @@ -250,6 +282,7 @@ values: role: roles/container.hostServiceAgentUser module.project-factory.module.projects-iam["dev-tb-app0-0"].google_compute_shared_vpc_host_project.shared_vpc_host[0]: project: test-pf-dev-tb-app0-0 + timeouts: null module.project-factory.module.projects-iam["dev-tb-app0-0"].google_project_iam_binding.authoritative["roles/owner"]: condition: [] members: @@ -274,12 +307,16 @@ values: - serviceAccount:app-0-be@test-pf-dev-tb-app0-1.iam.gserviceaccount.com project: test-pf-dev-tb-app0-1 role: roles/run.developer + module.project-factory.module.projects["dev-ta-app0-be"].data.google_storage_project_service_account.gcs_sa[0]: + project: test-pf-dev-ta-app0-be + user_project: null module.project-factory.module.projects["dev-ta-app0-be"].google_essential_contacts_contact.contact["admin@example.org"]: email: admin@example.org language_tag: en notification_category_subscriptions: - ALL parent: projects/test-pf-dev-ta-app0-be + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_project.project[0]: auto_create_network: false billing_account: 012345-67890A-BCDEF0 @@ -302,6 +339,7 @@ values: environment: test goog-terraform-provisioned: 'true' team: team-a + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_project_iam_member.service_agents["compute-system"]: condition: [] project: test-pf-dev-ta-app0-be @@ -319,38 +357,47 @@ values: disable_on_destroy: false project: test-pf-dev-ta-app0-be service: compute.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-ta-app0-be"].google_project_service.project_services["container.googleapis.com"] : disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-ta-app0-be service: container.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-ta-app0-be"].google_project_service.project_services["stackdriver.googleapis.com"] : disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-ta-app0-be service: stackdriver.googleapis.com + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_project_service.project_services["storage.googleapis.com"]: disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-ta-app0-be service: storage.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-ta-app0-be"].google_project_service_identity.default["container.googleapis.com"] : project: test-pf-dev-ta-app0-be service: container.googleapis.com + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_tags_tag_binding.binding["context"]: tag_value: tagValues/654321 + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_tags_tag_key.default["my-tag-key-1"]: description: Managed by the Terraform project-factory module. parent: projects/test-pf-dev-ta-app0-be purpose: null purpose_data: null short_name: my-tag-key-1 + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_tags_tag_value.default["my-tag-key-1/my-value-1"]: description: My value 1 short_name: my-value-1 + timeouts: null module.project-factory.module.projects["dev-ta-app0-be"].google_tags_tag_value.default["my-tag-key-1/my-value-2"]: description: My value 3 short_name: my-value-2 + timeouts: null ? module.project-factory.module.projects["dev-ta-app0-be"].google_tags_tag_value_iam_binding.default["my-tag-key-1/my-value-2:roles/resourcemanager.tagUser"] : condition: [] members: @@ -365,6 +412,7 @@ values: notification_category_subscriptions: - ALL parent: projects/test-pf-dev-tb-app0-0 + timeouts: null module.project-factory.module.projects["dev-tb-app0-0"].google_project.project[0]: auto_create_network: false billing_account: 123456-123456-123456 @@ -381,6 +429,7 @@ values: terraform_labels: environment: test goog-terraform-provisioned: 'true' + timeouts: null module.project-factory.module.projects["dev-tb-app0-0"].google_project_iam_member.service_agents["serverless-robot-prod"]: condition: [] project: test-pf-dev-tb-app0-0 @@ -390,25 +439,33 @@ values: disable_on_destroy: false project: test-pf-dev-tb-app0-0 service: run.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-tb-app0-0"].google_project_service.project_services["stackdriver.googleapis.com"] : disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-tb-app0-0 service: stackdriver.googleapis.com + timeouts: null module.project-factory.module.projects["dev-tb-app0-0"].google_project_service.project_services["storage.googleapis.com"]: disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-tb-app0-0 service: storage.googleapis.com + timeouts: null module.project-factory.module.projects["dev-tb-app0-0"].google_project_service_identity.default["run.googleapis.com"]: project: test-pf-dev-tb-app0-0 service: run.googleapis.com + timeouts: null + module.project-factory.module.projects["dev-tb-app0-1"].data.google_storage_project_service_account.gcs_sa[0]: + project: test-pf-dev-tb-app0-1 + user_project: null module.project-factory.module.projects["dev-tb-app0-1"].google_essential_contacts_contact.contact["admin@example.org"]: email: admin@example.org language_tag: en notification_category_subscriptions: - ALL parent: projects/test-pf-dev-tb-app0-1 + timeouts: null module.project-factory.module.projects["dev-tb-app0-1"].google_project.project[0]: auto_create_network: false billing_account: 012345-67890A-BCDEF0 @@ -431,6 +488,7 @@ values: environment: test goog-terraform-provisioned: 'true' team: team-b + timeouts: null module.project-factory.module.projects["dev-tb-app0-1"].google_project_iam_member.service_agents["container-engine-robot"]: condition: [] project: test-pf-dev-tb-app0-1 @@ -444,25 +502,33 @@ values: disable_on_destroy: false project: test-pf-dev-tb-app0-1 service: container.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-tb-app0-1"].google_project_service.project_services["stackdriver.googleapis.com"] : disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-tb-app0-1 service: stackdriver.googleapis.com + timeouts: null module.project-factory.module.projects["dev-tb-app0-1"].google_project_service.project_services["storage.googleapis.com"]: disable_dependent_services: false disable_on_destroy: false project: test-pf-dev-tb-app0-1 service: storage.googleapis.com + timeouts: null ? module.project-factory.module.projects["dev-tb-app0-1"].google_project_service_identity.default["container.googleapis.com"] : project: test-pf-dev-tb-app0-1 service: container.googleapis.com + timeouts: null + module.project-factory.module.projects["teams-iac-0"].data.google_storage_project_service_account.gcs_sa[0]: + project: test-pf-teams-iac-0 + user_project: null module.project-factory.module.projects["teams-iac-0"].google_essential_contacts_contact.contact["admin@example.org"]: email: admin@example.org language_tag: en notification_category_subscriptions: - ALL parent: projects/test-pf-teams-iac-0 + timeouts: null module.project-factory.module.projects["teams-iac-0"].google_org_policy_policy.default["compute.disableSerialPortAccess"]: dry_run_spec: [] name: projects/test-pf-teams-iac-0/policies/compute.disableSerialPortAccess @@ -477,6 +543,7 @@ values: enforce: 'FALSE' parameters: null values: [] + timeouts: null module.project-factory.module.projects["teams-iac-0"].google_project.project[0]: auto_create_network: false billing_account: 012345-67890A-BCDEF0 @@ -494,6 +561,7 @@ values: terraform_labels: environment: test goog-terraform-provisioned: 'true' + timeouts: null module.project-factory.module.projects["teams-iac-0"].google_project_iam_member.service_agents["container-engine-robot"]: condition: [] project: test-pf-teams-iac-0 @@ -507,19 +575,23 @@ values: disable_on_destroy: false project: test-pf-teams-iac-0 service: container.googleapis.com + timeouts: null ? module.project-factory.module.projects["teams-iac-0"].google_project_service.project_services["stackdriver.googleapis.com"] : disable_dependent_services: false disable_on_destroy: false project: test-pf-teams-iac-0 service: stackdriver.googleapis.com + timeouts: null module.project-factory.module.projects["teams-iac-0"].google_project_service.project_services["storage.googleapis.com"]: disable_dependent_services: false disable_on_destroy: false project: test-pf-teams-iac-0 service: storage.googleapis.com + timeouts: null module.project-factory.module.projects["teams-iac-0"].google_project_service_identity.default["container.googleapis.com"]: project: test-pf-teams-iac-0 service: container.googleapis.com + timeouts: null ? module.project-factory.module.service-accounts["dev-ta-app0-be/app-0-be"].google_project_iam_member.project-roles["$project_ids:dev-spoke-0-roles/compute.networkUser"] : condition: [] project: $project_ids:dev-spoke-0 @@ -541,6 +613,7 @@ values: email: app-0-be@test-pf-dev-ta-app0-be.iam.gserviceaccount.com member: serviceAccount:app-0-be@test-pf-dev-ta-app0-be.iam.gserviceaccount.com project: test-pf-dev-ta-app0-be + timeouts: null ? module.project-factory.module.service-accounts["dev-ta-app0-be/app-0-fe"].google_project_iam_member.project-roles["$project_ids:dev-spoke-0-roles/compute.networkUser"] : condition: [] project: $project_ids:dev-spoke-0 @@ -562,6 +635,7 @@ values: email: app-0-fe@test-pf-dev-ta-app0-be.iam.gserviceaccount.com member: serviceAccount:app-0-fe@test-pf-dev-ta-app0-be.iam.gserviceaccount.com project: test-pf-dev-ta-app0-be + timeouts: null ? module.project-factory.module.service-accounts["dev-tb-app0-0/vm-default"].google_project_iam_member.project-roles["$project_ids:dev-tb-app0-0-roles/logging.logWriter"] : condition: [] project: test-pf-dev-tb-app0-0 @@ -579,6 +653,7 @@ values: email: vm-default@test-pf-dev-tb-app0-0.iam.gserviceaccount.com member: serviceAccount:vm-default@test-pf-dev-tb-app0-0.iam.gserviceaccount.com project: test-pf-dev-tb-app0-0 + timeouts: null ? module.project-factory.module.service-accounts["dev-tb-app0-1/app-0-be"].google_project_iam_member.project-roles["$project_ids:dev-tb-app0-1-roles/logging.logWriter"] : condition: [] project: test-pf-dev-tb-app0-1 @@ -596,6 +671,7 @@ values: email: app-0-be@test-pf-dev-tb-app0-1.iam.gserviceaccount.com member: serviceAccount:app-0-be@test-pf-dev-tb-app0-1.iam.gserviceaccount.com project: test-pf-dev-tb-app0-1 + timeouts: null ? module.project-factory.module.service_accounts-iam["dev-tb-app0-0/vm-default"].google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"] : condition: [] members: @@ -605,12 +681,13 @@ values: input: null output: null triggers_replace: null + counts: google_billing_budget: 1 google_compute_shared_vpc_host_project: 1 google_compute_shared_vpc_service_project: 1 google_essential_contacts_contact: 4 - google_folder: 5 + google_folder: 8 google_folder_iam_binding: 1 google_kms_crypto_key_iam_member: 2 google_monitoring_notification_channel: 1 @@ -630,6 +707,6 @@ counts: google_tags_tag_key: 1 google_tags_tag_value: 2 google_tags_tag_value_iam_binding: 1 - modules: 23 - resources: 89 + modules: 26 + resources: 92 terraform_data: 1