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: {}