diff --git a/fast/stages/2-project-factory/README.md b/fast/stages/2-project-factory/README.md index 2d0741463..e62ec48f6 100644 --- a/fast/stages/2-project-factory/README.md +++ b/fast/stages/2-project-factory/README.md @@ -354,18 +354,19 @@ The approach is not shown here but reasonably easy to implement. The main projec |---|---|:---:|:---:|:---:|:---:| | [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables-fast.tf#L26) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables-fast.tf#L93) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | -| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | +| [prefix](variables-fast.tf#L101) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | string | ✓ | | 0-bootstrap | +| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | object({…}) | | {} | | | [folder_ids](variables-fast.tf#L39) | Folders created in the resource management stage. | map(string) | | {} | 1-resman | | [groups](variables-fast.tf#L47) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | map(string) | | {} | 0-bootstrap | | [host_project_ids](variables-fast.tf#L56) | Host project for the shared VPC. | map(string) | | {} | 2-networking | -| [locations](variables-fast.tf#L64) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | -| [org_policy_tags](variables-fast.tf#L82) | Optional organization policy tag values. | object({…}) | | {} | 0-bootstrap | +| [kms_keys](variables-fast.tf#L64) | KMS key ids. | map(string) | | {} | 2-security | +| [locations](variables-fast.tf#L72) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | 0-bootstrap | +| [org_policy_tags](variables-fast.tf#L90) | Optional organization policy tag values. | object({…}) | | {} | 0-bootstrap | | [outputs_location](variables.tf#L39) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [perimeters](variables-fast.tf#L74) | Optional VPC-SC perimeter ids. | map(string) | | {} | 1-vpcsc | -| [service_accounts](variables-fast.tf#L103) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | +| [perimeters](variables-fast.tf#L82) | Optional VPC-SC perimeter ids. | map(string) | | {} | 1-vpcsc | +| [service_accounts](variables-fast.tf#L111) | Automation service accounts in name => email format. | map(string) | | {} | 1-resman | | [stage_name](variables.tf#L45) | FAST stage name. Used to separate output files across different factories. | string | | "2-project-factory" | | -| [tag_values](variables-fast.tf#L111) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | +| [tag_values](variables-fast.tf#L119) | FAST-managed resource manager tag values. | map(string) | | {} | 1-resman | ## Outputs diff --git a/fast/stages/2-project-factory/main.tf b/fast/stages/2-project-factory/main.tf index c811e06df..3054cfada 100644 --- a/fast/stages/2-project-factory/main.tf +++ b/fast/stages/2-project-factory/main.tf @@ -45,6 +45,10 @@ module "projects" { var.groups, var.factories_config.context.iam_principals ) + kms_keys = merge( + var.kms_keys, + var.factories_config.context.kms_keys + ) perimeters = var.perimeters tag_values = merge( { diff --git a/fast/stages/2-project-factory/variables-fast.tf b/fast/stages/2-project-factory/variables-fast.tf index 0f99ddbe4..e114b9a5d 100644 --- a/fast/stages/2-project-factory/variables-fast.tf +++ b/fast/stages/2-project-factory/variables-fast.tf @@ -61,6 +61,14 @@ variable "host_project_ids" { default = {} } +variable "kms_keys" { + # tfdoc:variable:source 2-security + description = "KMS key ids." + type = map(string) + nullable = false + default = {} +} + variable "locations" { # tfdoc:variable:source 0-bootstrap description = "Optional locations for GCS, BigQuery, and logging buckets created here." diff --git a/fast/stages/2-project-factory/variables.tf b/fast/stages/2-project-factory/variables.tf index 56bcc15a1..29c9a9b35 100644 --- a/fast/stages/2-project-factory/variables.tf +++ b/fast/stages/2-project-factory/variables.tf @@ -25,8 +25,8 @@ variable "factories_config" { notification_channels = optional(map(any), {}) })) context = optional(object({ - # TODO: add KMS keys folder_ids = optional(map(string), {}) + kms_keys = optional(map(string), {}) iam_principals = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) diff --git a/modules/project-factory/README.md b/modules/project-factory/README.md index 777187a3f..45c738c63 100644 --- a/modules/project-factory/README.md +++ b/modules/project-factory/README.md @@ -279,6 +279,9 @@ module "project-factory" { default = "folders/5678901234" teams = "folders/5678901234" } + kms_keys = { + compute-prod-ew1 = "projects/kms-central-prj/locations/europe-west1/keyRings/my-keyring/cryptoKeys/ew1-compute" + } iam_principals = { gcp-devops = "group:gcp-devops@example.org" } @@ -351,9 +354,12 @@ labels: team: team-a parent: team-a/app-0 service_encryption_key_ids: - storage.googleapis.com: - - projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce + storage.googleapis.com: + - projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce + compute.googleapis.com: + - compute-prod-ew1 services: + - compute.googleapis.com - container.googleapis.com - storage.googleapis.com iam_by_principals: @@ -517,7 +523,7 @@ service_accounts: | name | description | type | required | default | |---|---|:---:|:---:|:---:| -| [factories_config](variables.tf#L131) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | +| [factories_config](variables.tf#L131) | Path to folder with YAML resource description data files. | object({…}) | ✓ | | | [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | object({…}) | | {} | | [data_merges](variables.tf#L73) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | object({…}) | | {} | | [data_overrides](variables.tf#L92) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | object({…}) | | {} | diff --git a/modules/project-factory/main.tf b/modules/project-factory/main.tf index 3f7ebb63f..af32a8362 100644 --- a/modules/project-factory/main.tf +++ b/modules/project-factory/main.tf @@ -90,10 +90,14 @@ module "projects" { )) notification_channels = try(each.value.notification_channels, null) org_policies = each.value.org_policies - service_encryption_key_ids = merge( - each.value.service_encryption_key_ids, - var.data_merges.service_encryption_key_ids - ) + service_encryption_key_ids = { + for k, v in merge( + each.value.service_encryption_key_ids, + var.data_merges.service_encryption_key_ids + ) : k => [ + for key in v : lookup(var.factories_config.context.kms_keys, key, key) + ] + } services = distinct(concat( each.value.services, var.data_merges.services diff --git a/modules/project-factory/variables.tf b/modules/project-factory/variables.tf index b65afd253..452c0210a 100644 --- a/modules/project-factory/variables.tf +++ b/modules/project-factory/variables.tf @@ -138,9 +138,9 @@ variable "factories_config" { notification_channels = optional(map(any), {}) })) context = optional(object({ - # TODO: add KMS keys folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) + kms_keys = optional(map(string), {}) perimeters = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) diff --git a/tests/modules/project_factory/examples/example.yaml b/tests/modules/project_factory/examples/example.yaml index ec5854bde..881813b7a 100644 --- a/tests/modules/project_factory/examples/example.yaml +++ b/tests/modules/project_factory/examples/example.yaml @@ -201,6 +201,10 @@ values: - ALL parent: projects/test-pf-dev-ta-app0-be timeouts: null + ? module.project-factory.module.projects["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 + role: roles/cloudkms.cryptoKeyEncrypterDecrypter ? module.project-factory.module.projects["dev-ta-app0-be"].google_kms_crypto_key_iam_member.service_agent_cmek["key-0.gs-project-accounts"] : condition: [] crypto_key_id: projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce @@ -545,12 +549,12 @@ counts: google_essential_contacts_contact: 4 google_folder: 5 google_folder_iam_binding: 1 - google_kms_crypto_key_iam_member: 1 + google_kms_crypto_key_iam_member: 2 google_monitoring_notification_channel: 1 google_project: 4 google_project_iam_binding: 6 - google_project_iam_member: 20 - google_project_service: 12 + google_project_iam_member: 21 + google_project_service: 13 google_project_service_identity: 4 google_service_account: 6 google_service_account_iam_binding: 1 @@ -562,6 +566,6 @@ counts: google_tags_tag_value: 2 google_tags_tag_value_iam_binding: 1 modules: 21 - resources: 80 + resources: 83 outputs: {}