add KMS keys interpolation to project factory (#3145)
This commit is contained in:
committed by
GitHub
parent
12b206a72f
commit
3c38bc643e
@@ -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. | <code title="object({ outputs_bucket = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
| [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object({ outputs_bucket = string })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||||
| [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. | <code title="object({ id = string is_org_level = optional(bool, true) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
| [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. | <code title="object({ id = string is_org_level = optional(bool, true) })">object({…})</code> | ✓ | | <code>0-bootstrap</code> |
|
||||||
| [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. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
| [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. | <code>string</code> | ✓ | | <code>0-bootstrap</code> |
|
||||||
| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | <code title="object({ folders_data_path = optional(string, "data/hierarchy") projects_data_path = optional(string, "data/projects") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) }), {}) })">object({…})</code> | | <code>{}</code> | |
|
| [factories_config](variables.tf#L17) | Configuration for YAML-based factories. | <code title="object({ folders_data_path = optional(string, "data/hierarchy") projects_data_path = optional(string, "data/projects") budgets = optional(object({ billing_account = string budgets_data_path = optional(string, "data/budgets") notification_channels = optional(map(any), {}) })) context = optional(object({ 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), {}) }), {}) })">object({…})</code> | | <code>{}</code> | |
|
||||||
| [folder_ids](variables-fast.tf#L39) | Folders created in the resource management stage. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
| [folder_ids](variables-fast.tf#L39) | Folders created in the resource management stage. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
||||||
| [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. | <code>map(string)</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
| [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. | <code>map(string)</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||||
| [host_project_ids](variables-fast.tf#L56) | Host project for the shared VPC. | <code>map(string)</code> | | <code>{}</code> | <code>2-networking</code> |
|
| [host_project_ids](variables-fast.tf#L56) | Host project for the shared VPC. | <code>map(string)</code> | | <code>{}</code> | <code>2-networking</code> |
|
||||||
| [locations](variables-fast.tf#L64) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ gcs = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
| [kms_keys](variables-fast.tf#L64) | KMS key ids. | <code>map(string)</code> | | <code>{}</code> | <code>2-security</code> |
|
||||||
| [org_policy_tags](variables-fast.tf#L82) | Optional organization policy tag values. | <code title="object({ key_name = optional(string, "org-policies") values = optional(map(string), {}) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
| [locations](variables-fast.tf#L72) | Optional locations for GCS, BigQuery, and logging buckets created here. | <code title="object({ gcs = optional(string) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||||
|
| [org_policy_tags](variables-fast.tf#L90) | Optional organization policy tag values. | <code title="object({ key_name = optional(string, "org-policies") values = optional(map(string), {}) })">object({…})</code> | | <code>{}</code> | <code>0-bootstrap</code> |
|
||||||
| [outputs_location](variables.tf#L39) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
| [outputs_location](variables.tf#L39) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | <code>string</code> | | <code>null</code> | |
|
||||||
| [perimeters](variables-fast.tf#L74) | Optional VPC-SC perimeter ids. | <code>map(string)</code> | | <code>{}</code> | <code>1-vpcsc</code> |
|
| [perimeters](variables-fast.tf#L82) | Optional VPC-SC perimeter ids. | <code>map(string)</code> | | <code>{}</code> | <code>1-vpcsc</code> |
|
||||||
| [service_accounts](variables-fast.tf#L103) | Automation service accounts in name => email format. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
| [service_accounts](variables-fast.tf#L111) | Automation service accounts in name => email format. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
||||||
| [stage_name](variables.tf#L45) | FAST stage name. Used to separate output files across different factories. | <code>string</code> | | <code>"2-project-factory"</code> | |
|
| [stage_name](variables.tf#L45) | FAST stage name. Used to separate output files across different factories. | <code>string</code> | | <code>"2-project-factory"</code> | |
|
||||||
| [tag_values](variables-fast.tf#L111) | FAST-managed resource manager tag values. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
| [tag_values](variables-fast.tf#L119) | FAST-managed resource manager tag values. | <code>map(string)</code> | | <code>{}</code> | <code>1-resman</code> |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ module "projects" {
|
|||||||
var.groups,
|
var.groups,
|
||||||
var.factories_config.context.iam_principals
|
var.factories_config.context.iam_principals
|
||||||
)
|
)
|
||||||
|
kms_keys = merge(
|
||||||
|
var.kms_keys,
|
||||||
|
var.factories_config.context.kms_keys
|
||||||
|
)
|
||||||
perimeters = var.perimeters
|
perimeters = var.perimeters
|
||||||
tag_values = merge(
|
tag_values = merge(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ variable "host_project_ids" {
|
|||||||
default = {}
|
default = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "kms_keys" {
|
||||||
|
# tfdoc:variable:source 2-security
|
||||||
|
description = "KMS key ids."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
variable "locations" {
|
variable "locations" {
|
||||||
# tfdoc:variable:source 0-bootstrap
|
# tfdoc:variable:source 0-bootstrap
|
||||||
description = "Optional locations for GCS, BigQuery, and logging buckets created here."
|
description = "Optional locations for GCS, BigQuery, and logging buckets created here."
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ variable "factories_config" {
|
|||||||
notification_channels = optional(map(any), {})
|
notification_channels = optional(map(any), {})
|
||||||
}))
|
}))
|
||||||
context = optional(object({
|
context = optional(object({
|
||||||
# TODO: add KMS keys
|
|
||||||
folder_ids = optional(map(string), {})
|
folder_ids = optional(map(string), {})
|
||||||
|
kms_keys = optional(map(string), {})
|
||||||
iam_principals = optional(map(string), {})
|
iam_principals = optional(map(string), {})
|
||||||
tag_values = optional(map(string), {})
|
tag_values = optional(map(string), {})
|
||||||
vpc_host_projects = optional(map(string), {})
|
vpc_host_projects = optional(map(string), {})
|
||||||
|
|||||||
@@ -279,6 +279,9 @@ module "project-factory" {
|
|||||||
default = "folders/5678901234"
|
default = "folders/5678901234"
|
||||||
teams = "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 = {
|
iam_principals = {
|
||||||
gcp-devops = "group:gcp-devops@example.org"
|
gcp-devops = "group:gcp-devops@example.org"
|
||||||
}
|
}
|
||||||
@@ -351,9 +354,12 @@ labels:
|
|||||||
team: team-a
|
team: team-a
|
||||||
parent: team-a/app-0
|
parent: team-a/app-0
|
||||||
service_encryption_key_ids:
|
service_encryption_key_ids:
|
||||||
storage.googleapis.com:
|
storage.googleapis.com:
|
||||||
- projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
|
- projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
|
||||||
|
compute.googleapis.com:
|
||||||
|
- compute-prod-ew1
|
||||||
services:
|
services:
|
||||||
|
- compute.googleapis.com
|
||||||
- container.googleapis.com
|
- container.googleapis.com
|
||||||
- storage.googleapis.com
|
- storage.googleapis.com
|
||||||
iam_by_principals:
|
iam_by_principals:
|
||||||
@@ -517,7 +523,7 @@ service_accounts:
|
|||||||
|
|
||||||
| name | description | type | required | default |
|
| name | description | type | required | default |
|
||||||
|---|---|:---:|:---:|:---:|
|
|---|---|:---:|:---:|:---:|
|
||||||
| [factories_config](variables.tf#L131) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) perimeters = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">object({…})</code> | ✓ | |
|
| [factories_config](variables.tf#L131) | Path to folder with YAML resource description data files. | <code title="object({ budgets = optional(object({ billing_account = string budgets_data_path = string notification_channels = optional(map(any), {}) })) context = optional(object({ 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), {}) notification_channels = optional(map(string), {}) }), {}) folders_data_path = optional(string) projects_data_path = optional(string) })">object({…})</code> | ✓ | |
|
||||||
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string)), {}) deletion_policy = optional(string) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) project_reuse = optional(object({ use_data_source = optional(bool, true) project_attributes = optional(object({ name = string number = number services_enabled = optional(list(string), []) })) })) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string network_users = optional(list(string), []) service_agent_iam = optional(map(list(string)), {}) service_agent_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })) storage_location = optional(string) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
| [data_defaults](variables.tf#L17) | Optional default values used when corresponding project data from files are missing. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string)), {}) deletion_policy = optional(string) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) project_reuse = optional(object({ use_data_source = optional(bool, true) project_attributes = optional(object({ name = string number = number services_enabled = optional(list(string), []) })) })) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string network_users = optional(list(string), []) service_agent_iam = optional(map(list(string)), {}) service_agent_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })) storage_location = optional(string) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||||
| [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`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
| [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`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||||
| [data_overrides](variables.tf#L92) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string))) deletion_policy = optional(string) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) storage_location = optional(string) tag_bindings = optional(map(string)) services = optional(list(string)) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) }))) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) }))) })">object({…})</code> | | <code>{}</code> |
|
| [data_overrides](variables.tf#L92) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object({ billing_account = optional(string) contacts = optional(map(list(string))) deletion_policy = optional(string) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) parent = optional(string) prefix = optional(string) service_encryption_key_ids = optional(map(list(string))) storage_location = optional(string) tag_bindings = optional(map(string)) services = optional(list(string)) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) }))) vpc_sc = optional(object({ perimeter_name = string perimeter_bridges = optional(list(string), []) is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) }))) })">object({…})</code> | | <code>{}</code> |
|
||||||
|
|||||||
@@ -90,10 +90,14 @@ module "projects" {
|
|||||||
))
|
))
|
||||||
notification_channels = try(each.value.notification_channels, null)
|
notification_channels = try(each.value.notification_channels, null)
|
||||||
org_policies = each.value.org_policies
|
org_policies = each.value.org_policies
|
||||||
service_encryption_key_ids = merge(
|
service_encryption_key_ids = {
|
||||||
each.value.service_encryption_key_ids,
|
for k, v in merge(
|
||||||
var.data_merges.service_encryption_key_ids
|
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(
|
services = distinct(concat(
|
||||||
each.value.services,
|
each.value.services,
|
||||||
var.data_merges.services
|
var.data_merges.services
|
||||||
|
|||||||
@@ -138,9 +138,9 @@ variable "factories_config" {
|
|||||||
notification_channels = optional(map(any), {})
|
notification_channels = optional(map(any), {})
|
||||||
}))
|
}))
|
||||||
context = optional(object({
|
context = optional(object({
|
||||||
# TODO: add KMS keys
|
|
||||||
folder_ids = optional(map(string), {})
|
folder_ids = optional(map(string), {})
|
||||||
iam_principals = optional(map(string), {})
|
iam_principals = optional(map(string), {})
|
||||||
|
kms_keys = optional(map(string), {})
|
||||||
perimeters = optional(map(string), {})
|
perimeters = optional(map(string), {})
|
||||||
tag_values = optional(map(string), {})
|
tag_values = optional(map(string), {})
|
||||||
vpc_host_projects = optional(map(string), {})
|
vpc_host_projects = optional(map(string), {})
|
||||||
|
|||||||
@@ -201,6 +201,10 @@ values:
|
|||||||
- ALL
|
- ALL
|
||||||
parent: projects/test-pf-dev-ta-app0-be
|
parent: projects/test-pf-dev-ta-app0-be
|
||||||
timeouts: null
|
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"]
|
? module.project-factory.module.projects["dev-ta-app0-be"].google_kms_crypto_key_iam_member.service_agent_cmek["key-0.gs-project-accounts"]
|
||||||
: condition: []
|
: condition: []
|
||||||
crypto_key_id: projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
|
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_essential_contacts_contact: 4
|
||||||
google_folder: 5
|
google_folder: 5
|
||||||
google_folder_iam_binding: 1
|
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_monitoring_notification_channel: 1
|
||||||
google_project: 4
|
google_project: 4
|
||||||
google_project_iam_binding: 6
|
google_project_iam_binding: 6
|
||||||
google_project_iam_member: 20
|
google_project_iam_member: 21
|
||||||
google_project_service: 12
|
google_project_service: 13
|
||||||
google_project_service_identity: 4
|
google_project_service_identity: 4
|
||||||
google_service_account: 6
|
google_service_account: 6
|
||||||
google_service_account_iam_binding: 1
|
google_service_account_iam_binding: 1
|
||||||
@@ -562,6 +566,6 @@ counts:
|
|||||||
google_tags_tag_value: 2
|
google_tags_tag_value: 2
|
||||||
google_tags_tag_value_iam_binding: 1
|
google_tags_tag_value_iam_binding: 1
|
||||||
modules: 21
|
modules: 21
|
||||||
resources: 80
|
resources: 83
|
||||||
|
|
||||||
outputs: {}
|
outputs: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user