add fourth folder level to project factory module (#3467)

This commit is contained in:
Ludovico Magnocavallo
2025-10-26 10:34:02 +01:00
committed by GitHub
parent 08e6c4196a
commit 56b213a047
3 changed files with 147 additions and 6 deletions

View File

@@ -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:

View File

@@ -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
})
}

View File

@@ -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