Merge remote-tracking branch 'origin/master' into fast-dev
This commit is contained in:
@@ -280,7 +280,7 @@ module "cf-http" {
|
||||
project_id = var.project_number # use project_number to avoid perm-diff
|
||||
secret = reverse(split("/", module.secret-manager.secrets["credentials"].name))[0]
|
||||
versions = [
|
||||
"${module.secret-manager.version_versions["credentials:v1"]}:/ver1"
|
||||
"${module.secret-manager.version_versions["credentials/v1"]}:/ver1"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,7 @@ module "cf-http" {
|
||||
google_project_iam_member.bucket_default_compute_account_grant,
|
||||
]
|
||||
}
|
||||
# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e
|
||||
# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
### Using CMEK to encrypt function resources
|
||||
|
||||
@@ -293,7 +293,7 @@ module "cf-http" {
|
||||
project_id = var.project_id
|
||||
secret = reverse(split("/", module.secret-manager.secrets["credentials"].name))[0]
|
||||
versions = [
|
||||
"${module.secret-manager.version_versions["credentials:v1"]}:ver1"
|
||||
"${module.secret-manager.version_versions["credentials/v1"]}:ver1"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -302,7 +302,7 @@ module "cf-http" {
|
||||
]
|
||||
}
|
||||
|
||||
# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e
|
||||
# tftest fixtures=fixtures/secret-credentials.tf,fixtures/functions-default-sa-iam-grants.tf inventory=secrets.yaml e2e skip-tofu
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
@@ -46,7 +46,7 @@ module "cloud_run" {
|
||||
env_from_key = {
|
||||
SECRET1 = {
|
||||
secret = module.secret-manager.secrets["credentials"].name
|
||||
version = module.secret-manager.version_versions["credentials:v1"]
|
||||
version = module.secret-manager.version_versions["credentials/v1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ module "cloud_run" {
|
||||
}
|
||||
deletion_protection = false
|
||||
}
|
||||
# tftest fixtures=fixtures/secret-credentials.tf inventory=service-iam-env.yaml e2e
|
||||
# tftest fixtures=fixtures/secret-credentials.tf inventory=service-iam-env.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
## Mounting secrets as volumes
|
||||
@@ -86,7 +86,7 @@ module "cloud_run" {
|
||||
}
|
||||
deletion_protection = false
|
||||
}
|
||||
# tftest fixtures=fixtures/secret-credentials.tf inventory=service-volume-secretes.yaml e2e
|
||||
# tftest fixtures=fixtures/secret-credentials.tf inventory=service-volume-secretes.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
## Mounting GCS buckets
|
||||
@@ -491,18 +491,17 @@ module "secrets" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
otel-config = {}
|
||||
}
|
||||
iam = {
|
||||
otel-config = {
|
||||
"roles/secretmanager.secretAccessor" = [
|
||||
"serviceAccount:${var.project_number}-compute@developer.gserviceaccount.com",
|
||||
]
|
||||
}
|
||||
}
|
||||
versions = {
|
||||
otel-config = {
|
||||
v1 = { enabled = true, data = file("${path.module}/config/otel-config.yaml") }
|
||||
iam = {
|
||||
"roles/secretmanager.secretAccessor" = [
|
||||
"serviceAccount:${var.project_number}-compute@developer.gserviceaccount.com"
|
||||
]
|
||||
}
|
||||
versions = {
|
||||
v1 = {
|
||||
data = file("${path.module}/config/otel-config.yaml")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,7 +554,7 @@ module "cloud_run" {
|
||||
}
|
||||
deletion_protection = false
|
||||
}
|
||||
# tftest files=otel-config inventory=service-otel-sidecar.yaml e2e
|
||||
# tftest files=otel-config inventory=service-otel-sidecar.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
## Eventarc triggers
|
||||
@@ -754,6 +753,7 @@ Unsupported variables / attributes:
|
||||
- containers.resources.startup_cpu_boost
|
||||
|
||||
Additional configuration can be passwed as `job_config`:
|
||||
|
||||
- max_retries - maximum of retries per task
|
||||
- task_count - desired number of tasks
|
||||
- timeout - max allowed time per task, in seconds with up to nine fractional digits, ending with 's'. Example: `3.5s`
|
||||
|
||||
@@ -29,11 +29,12 @@ module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
credentials = {}
|
||||
}
|
||||
iam = {
|
||||
credentials = {
|
||||
"roles/secretmanager.secretAccessor" = [module.cloud_run.service_account_iam_email]
|
||||
iam = {
|
||||
"roles/secretmanager.secretAccessor" = [
|
||||
module.cloud_run.service_account_iam_email
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +64,7 @@ module "cloud_run" {
|
||||
}
|
||||
service_account_create = true
|
||||
}
|
||||
# tftest modules=2 resources=5 inventory=simple.yaml e2e
|
||||
# tftest modules=2 resources=5 inventory=simple.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
## Mounting secrets as volumes
|
||||
@@ -73,16 +74,15 @@ module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
credentials = {}
|
||||
}
|
||||
versions = {
|
||||
credentials = {
|
||||
v1 = { enabled = true, data = "foo bar baz" }
|
||||
}
|
||||
}
|
||||
iam = {
|
||||
credentials = {
|
||||
"roles/secretmanager.secretAccessor" = [module.cloud_run.service_account_iam_email]
|
||||
iam = {
|
||||
"roles/secretmanager.secretAccessor" = [
|
||||
module.cloud_run.service_account_iam_email
|
||||
]
|
||||
}
|
||||
versions = {
|
||||
v1 = { data = "foo bar baz" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ module "cloud_run" {
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=5 inventory=secrets.yaml e2e
|
||||
# tftest modules=2 resources=5 inventory=secrets.yaml e2e skip-tofu
|
||||
```
|
||||
|
||||
## Revision annotations
|
||||
|
||||
@@ -941,10 +941,10 @@ module "sole-tenancy" {
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [name](variables.tf#L270) | Instance name. | <code>string</code> | ✓ | |
|
||||
| [network_interfaces](variables.tf#L282) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | <code title="list(object({ network = string subnetwork = string alias_ips = optional(map(string), {}) nat = optional(bool, false) nic_type = optional(string) stack_type = optional(string) addresses = optional(object({ internal = optional(string) external = optional(string) }), null) network_tier = optional(string) }))">list(object({…}))</code> | ✓ | |
|
||||
| [project_id](variables.tf#L367) | Project id. | <code>string</code> | ✓ | |
|
||||
| [zone](variables.tf#L480) | Compute zone. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L277) | Instance name. | <code>string</code> | ✓ | |
|
||||
| [network_interfaces](variables.tf#L289) | Network interfaces configuration. Use self links for Shared VPC, set addresses to null if not needed. | <code title="list(object({ network = string subnetwork = string alias_ips = optional(map(string), {}) nat = optional(bool, false) nic_type = optional(string) stack_type = optional(string) addresses = optional(object({ internal = optional(string) external = optional(string) }), null) network_tier = optional(string) }))">list(object({…}))</code> | ✓ | |
|
||||
| [project_id](variables.tf#L374) | Project id. | <code>string</code> | ✓ | |
|
||||
| [zone](variables.tf#L487) | Compute zone. | <code>string</code> | ✓ | |
|
||||
| [attached_disk_defaults](variables.tf#L17) | Defaults for attached disks options. | <code title="object({ auto_delete = optional(bool, false) mode = string replica_zone = string type = string })">object({…})</code> | | <code title="{ auto_delete = true mode = "READ_WRITE" replica_zone = null type = "pd-balanced" }">{…}</code> |
|
||||
| [attached_disks](variables.tf#L37) | Additional disks, if options is null defaults will be used in its place. Source type is one of 'image' (zonal disks in vms and template), 'snapshot' (vm), 'existing', and null. | <code title="list(object({ name = optional(string) device_name = optional(string) size = string snapshot_schedule = optional(list(string)) source = optional(string) source_type = optional(string) options = optional( object({ auto_delete = optional(bool, false) mode = optional(string, "READ_WRITE") replica_zone = optional(string) type = optional(string, "pd-balanced") }), { auto_delete = true mode = "READ_WRITE" replica_zone = null type = "pd-balanced" } ) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [boot_disk](variables.tf#L82) | Boot disk properties. Initialize params are ignored when source is set. | <code title="object({ auto_delete = optional(bool, true) snapshot_schedule = optional(list(string)) source = optional(string) initialize_params = optional(object({ image = optional(string, "projects/debian-cloud/global/images/family/debian-11") size = optional(number, 10) type = optional(string, "pd-balanced") }), {}) use_independent_disk = optional(bool, false) })">object({…})</code> | | <code title="{ initialize_params = {} }">{…}</code> |
|
||||
@@ -962,18 +962,19 @@ module "sole-tenancy" {
|
||||
| [instance_type](variables.tf#L246) | Instance type. | <code>string</code> | | <code>"f1-micro"</code> |
|
||||
| [labels](variables.tf#L252) | Instance labels. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [metadata](variables.tf#L258) | Instance metadata. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [min_cpu_platform](variables.tf#L264) | Minimum CPU platform. | <code>string</code> | | <code>null</code> |
|
||||
| [network_attached_interfaces](variables.tf#L275) | Network interfaces using network attachments. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [network_tag_bindings](variables.tf#L303) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance only for networking purposes, and modifiable without impacting the main resource lifecycle. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [options](variables.tf#L310) | Instance options. | <code title="object({ advanced_machine_features = optional(object({ enable_nested_virtualization = optional(bool) enable_turbo_mode = optional(bool) enable_uefi_networking = optional(bool) performance_monitoring_unit = optional(string) threads_per_core = optional(number) visible_core_count = optional(number) })) allow_stopping_for_update = optional(bool, true) deletion_protection = optional(bool, false) graceful_shutdown = optional(object({ enabled = optional(bool, false) max_duration_secs = optional(number) })) max_run_duration = optional(object({ nanos = optional(number) seconds = number })) node_affinities = optional(map(object({ values = list(string) in = optional(bool, true) })), {}) spot = optional(bool, false) termination_action = optional(string) })">object({…})</code> | | <code title="{ allow_stopping_for_update = true deletion_protection = false spot = false termination_action = null }">{…}</code> |
|
||||
| [project_number](variables.tf#L372) | Project number. Used in tag bindings to avoid a permadiff. | <code>string</code> | | <code>null</code> |
|
||||
| [scratch_disks](variables.tf#L378) | Scratch disks configuration. | <code title="object({ count = number interface = string })">object({…})</code> | | <code title="{ count = 0 interface = "NVME" }">{…}</code> |
|
||||
| [service_account](variables.tf#L390) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | <code title="object({ auto_create = optional(bool, false) email = optional(string) scopes = optional(list(string)) })">object({…})</code> | | <code>{}</code> |
|
||||
| [shielded_config](variables.tf#L400) | Shielded VM configuration of the instances. | <code title="object({ enable_secure_boot = bool enable_vtpm = bool enable_integrity_monitoring = bool })">object({…})</code> | | <code>null</code> |
|
||||
| [snapshot_schedules](variables.tf#L410) | Snapshot schedule resource policies that can be attached to disks. | <code title="map(object({ schedule = object({ daily = optional(object({ days_in_cycle = number start_time = string })) hourly = optional(object({ hours_in_cycle = number start_time = string })) weekly = optional(list(object({ day = string start_time = string }))) }) description = optional(string) retention_policy = optional(object({ max_retention_days = number on_source_disk_delete_keep = optional(bool) })) snapshot_properties = optional(object({ chain_name = optional(string) guest_flush = optional(bool) labels = optional(map(string)) storage_locations = optional(list(string)) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L453) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance and zonal disks, and modifiable without impacting the main resource lifecycle. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [tag_bindings_immutable](variables.tf#L460) | Immutable resource manager tag bindings, in tagKeys/id => tagValues/id format. These are set on the instance or instance template at creation time, and trigger recreation if changed. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L474) | Instance network tags for firewall rule targets. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [metadata_startup_script](variables.tf#L264) | Instance startup script. Will trigger recreation on change, even after importing. | <code>string</code> | | <code>null</code> |
|
||||
| [min_cpu_platform](variables.tf#L271) | Minimum CPU platform. | <code>string</code> | | <code>null</code> |
|
||||
| [network_attached_interfaces](variables.tf#L282) | Network interfaces using network attachments. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [network_tag_bindings](variables.tf#L310) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance only for networking purposes, and modifiable without impacting the main resource lifecycle. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [options](variables.tf#L317) | Instance options. | <code title="object({ advanced_machine_features = optional(object({ enable_nested_virtualization = optional(bool) enable_turbo_mode = optional(bool) enable_uefi_networking = optional(bool) performance_monitoring_unit = optional(string) threads_per_core = optional(number) visible_core_count = optional(number) })) allow_stopping_for_update = optional(bool, true) deletion_protection = optional(bool, false) graceful_shutdown = optional(object({ enabled = optional(bool, false) max_duration_secs = optional(number) })) max_run_duration = optional(object({ nanos = optional(number) seconds = number })) node_affinities = optional(map(object({ values = list(string) in = optional(bool, true) })), {}) spot = optional(bool, false) termination_action = optional(string) })">object({…})</code> | | <code title="{ allow_stopping_for_update = true deletion_protection = false spot = false termination_action = null }">{…}</code> |
|
||||
| [project_number](variables.tf#L379) | Project number. Used in tag bindings to avoid a permadiff. | <code>string</code> | | <code>null</code> |
|
||||
| [scratch_disks](variables.tf#L385) | Scratch disks configuration. | <code title="object({ count = number interface = string })">object({…})</code> | | <code title="{ count = 0 interface = "NVME" }">{…}</code> |
|
||||
| [service_account](variables.tf#L397) | Service account email and scopes. If email is null, the default Compute service account will be used unless auto_create is true, in which case a service account will be created. Set the variable to null to avoid attaching a service account. | <code title="object({ auto_create = optional(bool, false) email = optional(string) scopes = optional(list(string)) })">object({…})</code> | | <code>{}</code> |
|
||||
| [shielded_config](variables.tf#L407) | Shielded VM configuration of the instances. | <code title="object({ enable_secure_boot = bool enable_vtpm = bool enable_integrity_monitoring = bool })">object({…})</code> | | <code>null</code> |
|
||||
| [snapshot_schedules](variables.tf#L417) | Snapshot schedule resource policies that can be attached to disks. | <code title="map(object({ schedule = object({ daily = optional(object({ days_in_cycle = number start_time = string })) hourly = optional(object({ hours_in_cycle = number start_time = string })) weekly = optional(list(object({ day = string start_time = string }))) }) description = optional(string) retention_policy = optional(object({ max_retention_days = number on_source_disk_delete_keep = optional(bool) })) snapshot_properties = optional(object({ chain_name = optional(string) guest_flush = optional(bool) labels = optional(map(string)) storage_locations = optional(list(string)) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L460) | Resource manager tag bindings in arbitrary key => tag key or value id format. Set on both the instance and zonal disks, and modifiable without impacting the main resource lifecycle. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [tag_bindings_immutable](variables.tf#L467) | Immutable resource manager tag bindings, in tagKeys/id => tagValues/id format. These are set on the instance or instance template at creation time, and trigger recreation if changed. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L481) | Instance network tags for firewall rule targets. | <code>list(string)</code> | | <code>[]</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ resource "google_compute_instance" "default" {
|
||||
enable_display = var.enable_display
|
||||
labels = var.labels
|
||||
metadata = var.metadata
|
||||
metadata_startup_script = var.metadata_startup_script
|
||||
resource_policies = local.ischedule_attach
|
||||
|
||||
dynamic "advanced_machine_features" {
|
||||
|
||||
@@ -20,19 +20,20 @@ locals {
|
||||
}
|
||||
|
||||
resource "google_compute_instance_template" "default" {
|
||||
provider = google-beta
|
||||
count = local.template_create && !local.template_regional ? 1 : 0
|
||||
project = var.project_id
|
||||
region = local.region
|
||||
name_prefix = "${var.name}-"
|
||||
description = var.description
|
||||
tags = var.tags
|
||||
machine_type = var.instance_type
|
||||
min_cpu_platform = var.min_cpu_platform
|
||||
can_ip_forward = var.can_ip_forward
|
||||
metadata = var.metadata
|
||||
labels = var.labels
|
||||
resource_manager_tags = var.tag_bindings_immutable
|
||||
provider = google-beta
|
||||
count = local.template_create && !local.template_regional ? 1 : 0
|
||||
project = var.project_id
|
||||
region = local.region
|
||||
name_prefix = "${var.name}-"
|
||||
description = var.description
|
||||
tags = var.tags
|
||||
machine_type = var.instance_type
|
||||
min_cpu_platform = var.min_cpu_platform
|
||||
can_ip_forward = var.can_ip_forward
|
||||
metadata = var.metadata
|
||||
metadata_startup_script = var.metadata_startup_script
|
||||
labels = var.labels
|
||||
resource_manager_tags = var.tag_bindings_immutable
|
||||
|
||||
dynamic "advanced_machine_features" {
|
||||
for_each = local.advanced_mf != null ? [""] : []
|
||||
@@ -211,19 +212,20 @@ resource "google_compute_instance_template" "default" {
|
||||
}
|
||||
|
||||
resource "google_compute_region_instance_template" "default" {
|
||||
provider = google-beta
|
||||
count = local.template_create && local.template_regional ? 1 : 0
|
||||
project = var.project_id
|
||||
region = local.region
|
||||
name_prefix = "${var.name}-"
|
||||
description = var.description
|
||||
tags = var.tags
|
||||
machine_type = var.instance_type
|
||||
min_cpu_platform = var.min_cpu_platform
|
||||
can_ip_forward = var.can_ip_forward
|
||||
metadata = var.metadata
|
||||
labels = var.labels
|
||||
resource_manager_tags = var.tag_bindings_immutable
|
||||
provider = google-beta
|
||||
count = local.template_create && local.template_regional ? 1 : 0
|
||||
project = var.project_id
|
||||
region = local.region
|
||||
name_prefix = "${var.name}-"
|
||||
description = var.description
|
||||
tags = var.tags
|
||||
machine_type = var.instance_type
|
||||
min_cpu_platform = var.min_cpu_platform
|
||||
can_ip_forward = var.can_ip_forward
|
||||
metadata = var.metadata
|
||||
metadata_startup_script = var.metadata_startup_script
|
||||
labels = var.labels
|
||||
resource_manager_tags = var.tag_bindings_immutable
|
||||
|
||||
dynamic "advanced_machine_features" {
|
||||
for_each = local.advanced_mf != null ? [""] : []
|
||||
|
||||
@@ -261,6 +261,13 @@ variable "metadata" {
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "metadata_startup_script" {
|
||||
description = "Instance startup script. Will trigger recreation on change, even after importing."
|
||||
type = string
|
||||
nullable = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "min_cpu_platform" {
|
||||
description = "Minimum CPU platform."
|
||||
type = string
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
This module allows managing a dataform repository, allows adding IAM permissions. Also enables attaching a remote repository.
|
||||
|
||||
## TODO
|
||||
[] Add validation rules to variable.
|
||||
<!-- BEGIN TOC -->
|
||||
- [Examples](#examples)
|
||||
- [Simple dataform repository with access configuration](#simple-dataform-repository-with-access-configuration)
|
||||
- [Repository with an attached remote repository](#repository-with-an-attached-remote-repository)
|
||||
- [Variables](#variables)
|
||||
<!-- END TOC -->
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -34,11 +38,9 @@ module "secret" {
|
||||
project_id = "fast-bi-fabric"
|
||||
secrets = {
|
||||
my-secret = {
|
||||
}
|
||||
}
|
||||
versions = {
|
||||
my-secret = {
|
||||
v1 = { enabled = true, data = "MYTOKEN" }
|
||||
versions = {
|
||||
v1 = { data = "MYTOKEN" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,10 +53,10 @@ module "dataform" {
|
||||
remote_repository_settings = {
|
||||
url = "my-url"
|
||||
secret_name = "my-secret"
|
||||
token = module.secret.version_ids["my-secret:v1"]
|
||||
token = module.secret.version_ids["my-secret/v1"]
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=3
|
||||
# tftest modules=2 resources=3 skip-tofu
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
@@ -17,6 +17,7 @@ Due to the complexity of the underlying resources, changes to the configuration
|
||||
- [Hybrid NEG creation](#hybrid-neg-creation)
|
||||
- [Serverless NEG creation](#serverless-neg-creation)
|
||||
- [Private Service Connect NEG creation](#private-service-connect-neg-creation)
|
||||
- [Private Service Connect NEG creation with Cross-project PSC and back-end](#private-service-connect-neg-creation-with-cross-project-psc-and-back-end)
|
||||
- [Internet NEG creation](#internet-neg-creation)
|
||||
- [URL Map](#url-map)
|
||||
- [SSL Certificates](#ssl-certificates)
|
||||
@@ -443,6 +444,45 @@ module "ilb-l7" {
|
||||
# tftest modules=1 resources=5 e2e
|
||||
```
|
||||
|
||||
#### Private Service Connect NEG creation with Cross-project PSC and back-end
|
||||
|
||||
This example shows how to create the load balancer in one project `prj-host` while using a shared VPC deployed in the `prj-svc` project. Please note that the load balancer and its front-end will be created in the `prj-host` project and the back-end will be created in the `prj-svc` project. This is useful for situations where a shared VPC is being used that has been deployed in another project. Two subnetworks are needed, one for the loab balancer and another one for the PSC endpoint.
|
||||
|
||||
```hcl
|
||||
module "ilb-l7" {
|
||||
source = "./fabric/modules/net-lb-app-int"
|
||||
name = "ilb-test"
|
||||
project_id = "prj-host"
|
||||
region = "us-central1"
|
||||
|
||||
backend_service_configs = {
|
||||
default = {
|
||||
project_id = "prj-svc"
|
||||
backends = [{
|
||||
group = "neg-01"
|
||||
}]
|
||||
health_check_configs = {}
|
||||
neg_configs = {
|
||||
neg-01 = {
|
||||
project_id = "prj-svc"
|
||||
description = "Network Endpoint Group for service accessed using Private Service Connect"
|
||||
psc = {
|
||||
region = "us-central1"
|
||||
target_service = "projects/producer_project/regions/us-central1/serviceAttachments/project_id"
|
||||
network = var.vpc.self_link
|
||||
subnetwork = "projects/prj-svc/regions/us-central1/subnetworks/psc_subnet"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vpc_config = {
|
||||
network = var.vpc.self_link
|
||||
subnetwork = var.subnet.self_link
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Internet NEG creation
|
||||
|
||||
This example shows how to create and manage internet NEGs:
|
||||
|
||||
@@ -207,9 +207,13 @@ resource "google_compute_region_network_endpoint_group" "default" {
|
||||
|
||||
resource "google_compute_region_network_endpoint_group" "psc" {
|
||||
for_each = local.neg_regional_psc
|
||||
project = var.project_id
|
||||
region = each.value.psc.region
|
||||
name = "${var.name}-${each.key}"
|
||||
project = (
|
||||
each.value.project_id == null
|
||||
? var.project_id
|
||||
: each.value.project_id
|
||||
)
|
||||
region = each.value.psc.region
|
||||
name = "${var.name}-${each.key}"
|
||||
//description = coalesce(each.value.description, var.description)
|
||||
network_endpoint_type = "PRIVATE_SERVICE_CONNECT"
|
||||
psc_target_service = each.value.psc.target_service
|
||||
@@ -219,7 +223,6 @@ resource "google_compute_region_network_endpoint_group" "psc" {
|
||||
# ignore until https://github.com/hashicorp/terraform-provider-google/issues/20576 is fixed
|
||||
ignore_changes = [psc_data]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
locals {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
# Google Secret Manager Module
|
||||
# Google Secret Manager
|
||||
|
||||
Simple Secret Manager module that allows managing one or more secrets, their versions, and IAM bindings.
|
||||
|
||||
Secret Manager locations are available via the `gcloud secrets locations list` command.
|
||||
|
||||
**Warning:** managing versions will persist their data (the actual secret you want to protect) in the Terraform state in unencrypted form, accessible to any identity able to read or pull the state file.
|
||||
This module allows managing one or more secrets with versions and IAM bindings. For global secrets, this module optionally supports [write-only attributes](https://developer.hashicorp.com/terraform/language/manage-sensitive-data/write-only) for versions, which do not save data in state. Write-only attributes are not yet supported in OpenTofu, so this module is only compatible with Terraform until [OpenTofu support](https://github.com/opentofu/opentofu/issues/2834) has been released.
|
||||
|
||||
<!-- BEGIN TOC -->
|
||||
- [Secrets](#secrets)
|
||||
- [Secret IAM bindings](#secret-iam-bindings)
|
||||
- [Secret versions](#secret-versions)
|
||||
- [Secret with customer managed encryption key](#secret-with-customer-managed-encryption-key)
|
||||
- [Global Secrets](#global-secrets)
|
||||
- [Regional Secrets](#regional-secrets)
|
||||
- [IAM Bindings](#iam-bindings)
|
||||
- [Secret Versions](#secret-versions)
|
||||
- [Context Interpolations](#context-interpolations)
|
||||
- [Variables](#variables)
|
||||
- [Outputs](#outputs)
|
||||
- [Requirements](#requirements)
|
||||
@@ -18,9 +15,11 @@ Secret Manager locations are available via the `gcloud secrets locations list` c
|
||||
- [APIs](#apis)
|
||||
<!-- END TOC -->
|
||||
|
||||
## Secrets
|
||||
## Global Secrets
|
||||
|
||||
The secret replication policy is automatically managed if no location is set, or manually managed if a list of locations is passed to the secret.
|
||||
Secrets are created as global by default, with auto replication policy. For auto managed replication secrets the `kms_key` attribute can be used to configure CMEK via a global key.
|
||||
|
||||
To configure a secret for user managed replication configure the `global_replica_locations` attribute. Non-auto secrets ignore the `kms_key` attribute, but use each element of the locations map to configure keys.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
@@ -28,183 +27,207 @@ module "secret-manager" {
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test-auto = {}
|
||||
test-manual = {
|
||||
expire_time = "2025-10-02T15:01:23Z"
|
||||
locations = [var.regions.primary, var.regions.secondary]
|
||||
test-auto-cmek = {
|
||||
kms_key = "projects/test-0/locations/global/keyRings/test-g/cryptoKeys/sec"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=secret.yaml e2e
|
||||
```
|
||||
|
||||
## Secret IAM bindings
|
||||
|
||||
IAM bindings can be set per secret in the same way as for most other modules supporting IAM, using the `iam` variable.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test-auto = {}
|
||||
test-manual = {
|
||||
locations = [var.regions.primary, var.regions.secondary]
|
||||
}
|
||||
}
|
||||
iam = {
|
||||
test-auto = {
|
||||
"roles/secretmanager.secretAccessor" = ["group:${var.group_email}"]
|
||||
}
|
||||
test-manual = {
|
||||
"roles/secretmanager.secretAccessor" = ["group:${var.group_email}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=iam.yaml e2e
|
||||
```
|
||||
|
||||
## Secret versions
|
||||
|
||||
As mentioned above, please be aware that **version data will be stored in state in unencrypted form**.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test-auto = {}
|
||||
test-manual = {
|
||||
locations = [var.regions.primary, var.regions.secondary]
|
||||
}
|
||||
}
|
||||
versions = {
|
||||
test-auto = {
|
||||
v1 = { enabled = false, data = "auto foo bar baz" }
|
||||
v2 = { enabled = true, data = "auto foo bar spam" }
|
||||
},
|
||||
test-manual = {
|
||||
v1 = { enabled = true, data = "manual foo bar spam" }
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=5 inventory=versions.yaml e2e
|
||||
```
|
||||
|
||||
## Secret with customer managed encryption key
|
||||
|
||||
CMEK will be used if an encryption key is set in the `keys` field of `secrets` object for the secret region. For secrets with auto-replication, a global key must be specified.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./fabric/modules/project"
|
||||
name = "sec-mgr"
|
||||
billing_account = var.billing_account_id
|
||||
prefix = var.prefix
|
||||
parent = var.folder_id
|
||||
services = [
|
||||
"cloudkms.googleapis.com",
|
||||
"secretmanager.googleapis.com",
|
||||
]
|
||||
}
|
||||
|
||||
module "kms-global" {
|
||||
source = "./fabric/modules/kms"
|
||||
project_id = module.project.project_id
|
||||
keyring = {
|
||||
location = "global"
|
||||
name = "${var.prefix}-keyring-global"
|
||||
}
|
||||
keys = {
|
||||
"key-global" = {
|
||||
}
|
||||
}
|
||||
iam = {
|
||||
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
|
||||
module.project.service_agents.secretmanager.iam_email
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module "kms-primary-region" {
|
||||
source = "./fabric/modules/kms"
|
||||
project_id = module.project.project_id
|
||||
keyring = {
|
||||
location = var.regions.primary
|
||||
name = "${var.prefix}-keyring-regional"
|
||||
}
|
||||
keys = {
|
||||
"key-regional" = {
|
||||
}
|
||||
}
|
||||
iam = {
|
||||
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
|
||||
module.project.service_agents.secretmanager.iam_email
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module "kms-secondary-region" {
|
||||
source = "./fabric/modules/kms"
|
||||
project_id = module.project.project_id
|
||||
keyring = {
|
||||
location = var.regions.secondary
|
||||
name = "${var.prefix}-keyring-regional"
|
||||
}
|
||||
keys = {
|
||||
"key-regional" = {
|
||||
}
|
||||
}
|
||||
iam = {
|
||||
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
|
||||
module.project.service_agents.secretmanager.iam_email
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = module.project.project_id
|
||||
secrets = {
|
||||
test-auto = {
|
||||
keys = {
|
||||
global = module.kms-global.keys.key-global.id
|
||||
test-user = {
|
||||
global_replica_locations = {
|
||||
europe-west1 = null
|
||||
europe-west3 = null
|
||||
}
|
||||
}
|
||||
test-auto-nokeys = {}
|
||||
test-manual = {
|
||||
locations = [var.regions.primary, var.regions.secondary]
|
||||
keys = {
|
||||
"${var.regions.primary}" = module.kms-primary-region.keys.key-regional.id
|
||||
"${var.regions.secondary}" = module.kms-secondary-region.keys.key-regional.id
|
||||
test-user-cmek = {
|
||||
global_replica_locations = {
|
||||
europe-west1 = "projects/test-0/locations/europe-west1/keyRings/test-g/cryptoKeys/sec-ew1"
|
||||
europe-west3 = "projects/test-0/locations/europe-west3/keyRings/test-g/cryptoKeys/sec-ew3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest inventory=secret-cmek.yaml e2e
|
||||
# tftest modules=1 resources=4 inventory=secret.yaml skip-tofu
|
||||
```
|
||||
|
||||
## Regional Secrets
|
||||
|
||||
Regional secrets are identified by having the `location` attribute defined, and share the same interface with a few exceptions: the `global_replica_locations` is of course ignored, and versions only support a subset of attributes and can't use write-only attributes.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test = {
|
||||
location = "europe-west1"
|
||||
}
|
||||
test-cmek = {
|
||||
location = "europe-west1"
|
||||
kms_key = "projects/test-0/locations/global/keyRings/test-g/cryptoKeys/sec"
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=secret-regional.yaml skip-tofu
|
||||
```
|
||||
|
||||
## IAM Bindings
|
||||
|
||||
This module supports the same IAM interface as all other modules in this repository. IAM bindings are defined per secret, if you need cross-secret IAM bindings use project-level ones.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test = {
|
||||
iam = {
|
||||
"roles/secretmanager.admin" = [
|
||||
"user:test-0@example.com"
|
||||
]
|
||||
}
|
||||
iam_bindings = {
|
||||
test = {
|
||||
role = "roles/secretmanager.secretAccessor"
|
||||
members = [
|
||||
"user:test-1@example.com"
|
||||
]
|
||||
condition = {
|
||||
title = "Test."
|
||||
expression = "resource.matchTag('1234567890/environment', 'test')"
|
||||
}
|
||||
}
|
||||
}
|
||||
iam_bindings_additive = {
|
||||
test = {
|
||||
role = "roles/secretmanager.viewer"
|
||||
member = "user:test-2@example.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4 inventory=iam.yaml skip-tofu
|
||||
```
|
||||
|
||||
## Secret Versions
|
||||
|
||||
Versions are defined per secret via the `versions` attribute, and by default they accept string data which is stored in state. The `data_config` attributes allow configuring each secret:
|
||||
|
||||
- `data_config.is_file` instructs the module to read version data from a file (`data` is then used as the file path)
|
||||
- `data_config.is_base64` instructs the provider to treat data as Base64
|
||||
- `data_config.write_only_version` instructs the module to **use write-only attributes so that data is not set in state**, each time the write-only version is changed data is reuploaded to the secret version
|
||||
|
||||
As mentioned before write-only attributes are only available for global secrets. Regional secrets still use the potentially insecure way of storing data.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
project_id = var.project_id
|
||||
secrets = {
|
||||
test = {
|
||||
versions = {
|
||||
a = {
|
||||
# potentially unsafe
|
||||
data = "foo"
|
||||
}
|
||||
b = {
|
||||
# potentially unsafe, reads from file
|
||||
data = "test-data/secret-b.txt"
|
||||
data_config = {
|
||||
is_file = true
|
||||
}
|
||||
}
|
||||
c = {
|
||||
# uses safer write-only attribute
|
||||
data = "bar"
|
||||
data_config = {
|
||||
# bump this version when data needs updating
|
||||
write_only_version = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest files=0 modules=1 resources=4 inventory=versions.yaml skip-tofu
|
||||
```
|
||||
|
||||
```txt
|
||||
foo-secret
|
||||
# tftest-file id=0 path=test-data/secret-b.txt
|
||||
```
|
||||
|
||||
## Context Interpolations
|
||||
|
||||
Similarly to other core modules in this repository, this module also supports context-based interpolations, which are populated via the `context` variable.
|
||||
|
||||
This is a summary table of the available contexts, which can be used whenever an attribute expects the relevant information. Refer to the [project factory module](../project-factory/README.md#context-based-interpolation) for more details on context replacements.
|
||||
|
||||
- `$custom_roles:my_role`
|
||||
- `$iam_principals:my_principal`
|
||||
- `$kms_keys:my_key`
|
||||
- `$locations:my_location`
|
||||
- `$project_ids:my_project`
|
||||
- `$tag_keys:my_key`
|
||||
- `$tag_values:my_value`
|
||||
- custom template variables used in IAM conditions
|
||||
|
||||
This is a simple example that uses context interpolation.
|
||||
|
||||
```hcl
|
||||
module "secret-manager" {
|
||||
source = "./fabric/modules/secret-manager"
|
||||
context = {
|
||||
iam_principals = {
|
||||
mysa = "serviceAccount:test@foo-prod-test-0.iam.gserviceaccount.com"
|
||||
myuser = "user:test@example.com"
|
||||
}
|
||||
kms_keys = {
|
||||
primary = "projects/test-0/locations/europe-west1/keyRings/test-g/cryptoKeys/sec-ew1"
|
||||
secondary = "projects/test-0/locations/europe-west3/keyRings/test-g/cryptoKeys/sec-ew3"
|
||||
}
|
||||
locations = {
|
||||
primary = "europe-west1"
|
||||
secondary = "europe-west3"
|
||||
}
|
||||
project_ids = {
|
||||
test = "foo-prod-test-0"
|
||||
}
|
||||
}
|
||||
project_id = "$project_ids:test"
|
||||
secrets = {
|
||||
test-user-cmek = {
|
||||
global_replica_locations = {
|
||||
"$locations:primary" = "$kms_keys:primary"
|
||||
"$locations:secondary" = "$kms_keys:secondary"
|
||||
}
|
||||
iam = {
|
||||
"roles/secretmanager.viewer" = [
|
||||
"$iam_principals:mysa", "$iam_principals:myuser"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2 inventory=context.yaml skip-tofu
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [project_id](variables.tf#L29) | Project id where the keyring will be created. | <code>string</code> | ✓ | |
|
||||
| [iam](variables.tf#L17) | IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format. | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L23) | Optional labels for each secret. | <code>map(map(string))</code> | | <code>{}</code> |
|
||||
| [project_number](variables.tf#L34) | Project number of var.project_id. Set this to avoid permadiffs when creating tag bindings. | <code>string</code> | | <code>null</code> |
|
||||
| [secrets](variables.tf#L40) | Map of secrets to manage, their optional expire time, version destroy ttl, locations and KMS keys in {LOCATION => KEY} format. {GLOBAL => KEY} format enables CMEK for automatic managed secrets. If locations is null, automatic management will be set. | <code title="map(object({ expire_time = optional(string) locations = optional(list(string)) keys = optional(map(string)) tag_bindings = optional(map(string)) version_destroy_ttl = optional(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [versions](variables.tf#L52) | Optional versions to manage for each secret. Version names are only used internally to track individual versions. | <code title="map(map(object({ enabled = bool data = string })))">map(map(object({…})))</code> | | <code>{}</code> |
|
||||
| [project_id](variables.tf#L40) | Project id where the keyring will be created. | <code>string</code> | ✓ | |
|
||||
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object({ condition_vars = optional(map(map(string)), {}) custom_roles = optional(map(string), {}) iam_principals = optional(map(string), {}) kms_keys = optional(map(string), {}) locations = optional(map(string), {}) project_ids = optional(map(string), {}) tag_keys = optional(map(string), {}) tag_values = optional(map(string), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [project_number](variables.tf#L45) | Project number of var.project_id. Set this to avoid permadiffs when creating tag bindings. | <code>string</code> | | <code>null</code> |
|
||||
| [secrets](variables.tf#L51) | Map of secrets to manage. Defaults to global secrets unless region is set. | <code title="map(object({ annotations = optional(map(string), {}) deletion_protection = optional(bool) kms_key = optional(string) labels = optional(map(string), {}) global_replica_locations = optional(map(string)) location = optional(string) tag_bindings = optional(map(string)) tags = optional(map(string), {}) expiration_config = optional(object({ time = optional(string) ttl = optional(string) })) iam = optional(map(list(string)), {}) iam_bindings = optional(map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) version_config = optional(object({ aliases = optional(map(number)) destroy_ttl = optional(string) }), {}) versions = optional(map(object({ data = string deletion_policy = optional(string) enabled = optional(bool) data_config = optional(object({ is_base64 = optional(bool, false) is_file = optional(bool, false) write_only_version = optional(number) })) })), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [ids](outputs.tf#L17) | Fully qualified secret ids. | |
|
||||
| [secrets](outputs.tf#L27) | Secret resources. | |
|
||||
| [version_ids](outputs.tf#L36) | Version ids keyed by secret name : version name. | |
|
||||
| [version_versions](outputs.tf#L46) | Version versions keyed by secret name : version name. | |
|
||||
| [versions](outputs.tf#L56) | Secret versions. | ✓ |
|
||||
| [ids](outputs.tf#L28) | Fully qualified secret ids. | |
|
||||
| [secrets](outputs.tf#L41) | Secret resources. | |
|
||||
| [version_ids](outputs.tf#L54) | Fully qualified version ids. | |
|
||||
| [version_versions](outputs.tf#L67) | Version versions. | |
|
||||
| [versions](outputs.tf#L80) | Version resources. | ✓ |
|
||||
<!-- END TFDOC -->
|
||||
## Requirements
|
||||
|
||||
|
||||
131
modules/secret-manager/global.tf
Normal file
131
modules/secret-manager/global.tf
Normal file
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
_tag_template_global = (
|
||||
"//secretmanager.googleapis.com/projects/%s/secrets/%s"
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret" "default" {
|
||||
for_each = { for k, v in var.secrets : k => v if v.location == null }
|
||||
project = local.project_id
|
||||
secret_id = each.key
|
||||
labels = each.value.labels
|
||||
annotations = each.value.annotations
|
||||
version_aliases = try(each.value.version_config.aliases, null)
|
||||
version_destroy_ttl = try(each.value.version_config.destroy_ttl, null)
|
||||
expire_time = try(each.value.expiration_config.time, null)
|
||||
ttl = try(each.value.expiration_config.ttl, null)
|
||||
tags = {
|
||||
for k, v in each.value.tags :
|
||||
lookup(local.ctx.tag_keys, k, k) => lookup(local.ctx.tag_values, v, v)
|
||||
}
|
||||
deletion_protection = each.value.deletion_protection
|
||||
dynamic "replication" {
|
||||
for_each = try(each.value.global_replica_locations, null) == null ? [""] : []
|
||||
content {
|
||||
auto {
|
||||
dynamic "customer_managed_encryption" {
|
||||
for_each = each.value.kms_key == null ? [] : [""]
|
||||
content {
|
||||
kms_key_name = lookup(
|
||||
local.ctx.kms_keys, each.value.kms_key, each.value.kms_key
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dynamic "replication" {
|
||||
for_each = try(each.value.global_replica_locations, null) != null ? [""] : []
|
||||
content {
|
||||
user_managed {
|
||||
dynamic "replicas" {
|
||||
for_each = each.value.global_replica_locations
|
||||
content {
|
||||
location = lookup(local.ctx.locations, replicas.key, replicas.key)
|
||||
dynamic "customer_managed_encryption" {
|
||||
for_each = replicas.value == null ? [] : [""]
|
||||
content {
|
||||
kms_key_name = lookup(
|
||||
local.ctx.kms_keys, replicas.value, replicas.value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# dynamic "rotation" {
|
||||
# for_each = try(each.value.rotation_config, null) == null ? [] : [""]
|
||||
# content {
|
||||
# next_rotation_time = each.value.rotation_config.next_time
|
||||
# rotation_period = each.value.rotation_config.period
|
||||
# }
|
||||
# }
|
||||
# topics
|
||||
lifecycle {
|
||||
ignore_changes = [
|
||||
rotation[0].next_rotation_time
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_version" "default" {
|
||||
for_each = {
|
||||
for v in local.versions :
|
||||
"${v.secret}/${v.version}" => v if v.location == null
|
||||
}
|
||||
secret = google_secret_manager_secret.default[each.value.secret].id
|
||||
deletion_policy = each.value.deletion_policy
|
||||
enabled = each.value.enabled
|
||||
is_secret_data_base64 = try(
|
||||
each.value.data_config.is_base64, null
|
||||
)
|
||||
secret_data_wo_version = try(
|
||||
each.value.data_config.write_only_version, null
|
||||
)
|
||||
secret_data = (
|
||||
try(each.value.data_config.write_only_version, null) != null
|
||||
? null
|
||||
: (
|
||||
try(each.value.data_config.is_file, null) == true
|
||||
? file(each.value.data)
|
||||
: each.value.data
|
||||
)
|
||||
)
|
||||
secret_data_wo = (
|
||||
try(each.value.data_config.write_only_version, null) == null
|
||||
? null
|
||||
: (
|
||||
try(each.value.data_config.is_file, null) == true
|
||||
? file(each.value.data)
|
||||
: each.value.data
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_tags_tag_binding" "binding" {
|
||||
for_each = { for k, v in local.tag_bindings : k => v if v.location == null }
|
||||
parent = format(
|
||||
local._tag_template_global,
|
||||
local.tag_project,
|
||||
google_secret_manager_secret.default[each.value.secret].secret_id
|
||||
)
|
||||
tag_value = lookup(local.ctx.tag_values, each.value.tag, each.value.tag)
|
||||
}
|
||||
72
modules/secret-manager/iam-regional.tf
Normal file
72
modules/secret-manager/iam-regional.tf
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
resource "google_secret_manager_regional_secret_iam_binding" "authoritative" {
|
||||
for_each = {
|
||||
for binding in local.secret_iam :
|
||||
"${binding.secret}.${binding.role}" => binding if !binding.global
|
||||
}
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
secret_id = google_secret_manager_regional_secret.default[each.value.secret].id
|
||||
location = google_secret_manager_regional_secret.default[each.value.secret].location
|
||||
members = [
|
||||
for v in each.value.members :
|
||||
lookup(local.ctx.iam_principals, v, v)
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_secret_manager_regional_secret_iam_binding" "bindings" {
|
||||
for_each = {
|
||||
for k, v in local.secret_iam_bindings : k => v if !v.global
|
||||
}
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
secret_id = google_secret_manager_regional_secret.default[each.value.secret].id
|
||||
location = google_secret_manager_regional_secret.default[each.value.secret].location
|
||||
members = [
|
||||
for v in each.value.members :
|
||||
lookup(local.ctx.iam_principals, v, v)
|
||||
]
|
||||
dynamic "condition" {
|
||||
for_each = each.value.condition == null ? [] : [""]
|
||||
content {
|
||||
expression = templatestring(
|
||||
each.value.condition.expression, var.context.condition_vars
|
||||
)
|
||||
title = each.value.condition.title
|
||||
description = each.value.condition.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_regional_secret_iam_member" "members" {
|
||||
for_each = {
|
||||
for k, v in local.secret_iam_bindings_additive : k => v if !v.global
|
||||
}
|
||||
secret_id = google_secret_manager_regional_secret.default[each.value.secret].id
|
||||
location = google_secret_manager_regional_secret.default[each.value.secret].location
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
member = lookup(local.ctx.iam_principals, each.value.member, each.value.member)
|
||||
dynamic "condition" {
|
||||
for_each = each.value.condition == null ? [] : [""]
|
||||
content {
|
||||
expression = templatestring(
|
||||
each.value.condition.expression, var.context.condition_vars
|
||||
)
|
||||
title = each.value.condition.title
|
||||
description = each.value.condition.description
|
||||
}
|
||||
}
|
||||
}
|
||||
106
modules/secret-manager/iam.tf
Normal file
106
modules/secret-manager/iam.tf
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
secret_iam = flatten([
|
||||
for k, v in var.secrets : [
|
||||
for role, members in v.iam : {
|
||||
secret = k
|
||||
role = role
|
||||
members = members
|
||||
global = v.location == null
|
||||
}
|
||||
]
|
||||
])
|
||||
secret_iam_bindings = merge([
|
||||
for k, v in var.secrets : {
|
||||
for binding_key, data in v.iam_bindings :
|
||||
"${k}-${binding_key}" => {
|
||||
secret = k
|
||||
role = data.role
|
||||
members = data.members
|
||||
condition = data.condition
|
||||
global = v.location == null
|
||||
}
|
||||
}
|
||||
]...)
|
||||
secret_iam_bindings_additive = merge([
|
||||
for k, v in var.secrets : {
|
||||
for binding_key, data in v.iam_bindings_additive :
|
||||
"${k}-${binding_key}" => {
|
||||
secret = k
|
||||
role = data.role
|
||||
member = data.member
|
||||
condition = data.condition
|
||||
global = v.location == null
|
||||
}
|
||||
}
|
||||
]...)
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_iam_binding" "authoritative" {
|
||||
for_each = {
|
||||
for binding in local.secret_iam :
|
||||
"${binding.secret}.${binding.role}" => binding if binding.global
|
||||
}
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
secret_id = google_secret_manager_secret.default[each.value.secret].id
|
||||
members = [
|
||||
for v in each.value.members :
|
||||
lookup(local.ctx.iam_principals, v, v)
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_iam_binding" "bindings" {
|
||||
for_each = {
|
||||
for k, v in local.secret_iam_bindings : k => v if v.global
|
||||
}
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
secret_id = google_secret_manager_secret.default[each.value.secret].id
|
||||
members = [
|
||||
for v in each.value.members :
|
||||
lookup(local.ctx.iam_principals, v, v)
|
||||
]
|
||||
dynamic "condition" {
|
||||
for_each = each.value.condition == null ? [] : [""]
|
||||
content {
|
||||
expression = templatestring(
|
||||
each.value.condition.expression, var.context.condition_vars
|
||||
)
|
||||
title = each.value.condition.title
|
||||
description = each.value.condition.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_iam_member" "members" {
|
||||
for_each = {
|
||||
for k, v in local.secret_iam_bindings_additive : k => v if v.global
|
||||
}
|
||||
secret_id = google_secret_manager_secret.default[each.value.secret].id
|
||||
role = lookup(local.ctx.custom_roles, each.value.role, each.value.role)
|
||||
member = lookup(local.ctx.iam_principals, each.value.member, each.value.member)
|
||||
dynamic "condition" {
|
||||
for_each = each.value.condition == null ? [] : [""]
|
||||
content {
|
||||
expression = templatestring(
|
||||
each.value.condition.expression, var.context.condition_vars
|
||||
)
|
||||
title = each.value.condition.title
|
||||
description = each.value.condition.description
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,99 +15,40 @@
|
||||
*/
|
||||
|
||||
locals {
|
||||
# distinct is needed to make the expanding function argument work
|
||||
iam = flatten([
|
||||
for secret, roles in var.iam : [
|
||||
for role, members in roles : {
|
||||
secret = secret
|
||||
role = role
|
||||
members = members
|
||||
}
|
||||
]
|
||||
])
|
||||
ctx = {
|
||||
for k, v in var.context : k => {
|
||||
for kk, vv in v : "${local.ctx_p}${k}:${kk}" => vv
|
||||
} if k != "condition_vars"
|
||||
}
|
||||
ctx_p = "$"
|
||||
project_id = lookup(local.ctx.project_ids, var.project_id, var.project_id)
|
||||
tag_project = coalesce(var.project_number, var.project_id)
|
||||
tag_bindings = merge([
|
||||
for k, v in var.secrets : {
|
||||
for kk, vv in v.tag_bindings : "${k}/${kk}" => {
|
||||
parent = "//secretmanager.googleapis.com/projects/${coalesce(var.project_number, var.project_id)}/secrets/${google_secret_manager_secret.default[k].secret_id}"
|
||||
tag_value = vv
|
||||
location = v.location
|
||||
secret = k
|
||||
tag = vv
|
||||
}
|
||||
}
|
||||
if v.tag_bindings != null
|
||||
]...)
|
||||
version_pairs = flatten([
|
||||
for secret, versions in var.versions : [
|
||||
for name, attrs in versions : merge(attrs, { name = name, secret = secret })
|
||||
versions = flatten([
|
||||
for k, v in var.secrets : [
|
||||
for sk, sv in v.versions : merge(sv, {
|
||||
secret = k
|
||||
version = sk
|
||||
location = v.location
|
||||
})
|
||||
]
|
||||
])
|
||||
version_keypairs = {
|
||||
for pair in local.version_pairs : "${pair.secret}:${pair.name}" => pair
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret" "default" {
|
||||
for_each = var.secrets
|
||||
project = var.project_id
|
||||
secret_id = each.key
|
||||
labels = lookup(var.labels, each.key, null)
|
||||
expire_time = each.value.expire_time
|
||||
version_destroy_ttl = each.value.version_destroy_ttl
|
||||
|
||||
dynamic "replication" {
|
||||
for_each = each.value.locations == null ? [""] : []
|
||||
content {
|
||||
auto {
|
||||
dynamic "customer_managed_encryption" {
|
||||
for_each = try(lookup(each.value.keys, "global", null) == null ? [] : [""], [])
|
||||
content {
|
||||
kms_key_name = each.value.keys["global"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "replication" {
|
||||
for_each = each.value.locations == null ? [] : [""]
|
||||
content {
|
||||
user_managed {
|
||||
dynamic "replicas" {
|
||||
for_each = each.value.locations
|
||||
iterator = location
|
||||
content {
|
||||
location = location.value
|
||||
dynamic "customer_managed_encryption" {
|
||||
for_each = try(lookup(each.value.keys, location.value, null) == null ? [] : [""], [])
|
||||
content {
|
||||
kms_key_name = each.value.keys[location.value]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_version" "default" {
|
||||
provider = google-beta
|
||||
for_each = local.version_keypairs
|
||||
secret = google_secret_manager_secret.default[each.value.secret].id
|
||||
enabled = each.value.enabled
|
||||
secret_data = each.value.data
|
||||
}
|
||||
|
||||
resource "google_secret_manager_secret_iam_binding" "default" {
|
||||
provider = google-beta
|
||||
for_each = {
|
||||
for binding in local.iam : "${binding.secret}.${binding.role}" => binding
|
||||
}
|
||||
role = each.value.role
|
||||
secret_id = google_secret_manager_secret.default[each.value.secret].id
|
||||
members = each.value.members
|
||||
}
|
||||
|
||||
resource "google_tags_tag_binding" "binding" {
|
||||
for_each = local.tag_bindings
|
||||
parent = each.value.parent
|
||||
tag_value = each.value.tag_value
|
||||
}
|
||||
# resource "google_kms_key_handle" "my_key_handle" {
|
||||
# provider = google-beta
|
||||
# for_each = var.kms_autokey_config
|
||||
# project = var.project_id
|
||||
# name = each.key
|
||||
# location = each.value
|
||||
# resource_type_selector = "secretmanager.googleapis.com/Secret"
|
||||
# }
|
||||
|
||||
@@ -14,50 +14,79 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
o_secrets = merge(
|
||||
google_secret_manager_secret.default,
|
||||
google_secret_manager_regional_secret.default
|
||||
)
|
||||
o_versions = merge(
|
||||
google_secret_manager_secret_version.default,
|
||||
google_secret_manager_regional_secret_version.default
|
||||
)
|
||||
}
|
||||
|
||||
output "ids" {
|
||||
description = "Fully qualified secret ids."
|
||||
value = {
|
||||
for k, v in google_secret_manager_secret.default : v.secret_id => v.id
|
||||
}
|
||||
value = { for k, v in local.o_secrets : k => v.id }
|
||||
depends_on = [
|
||||
google_secret_manager_secret_iam_binding.default
|
||||
google_secret_manager_secret_iam_binding.authoritative,
|
||||
google_secret_manager_secret_iam_binding.bindings,
|
||||
google_secret_manager_secret_iam_member.members,
|
||||
google_secret_manager_regional_secret_iam_binding.authoritative,
|
||||
google_secret_manager_regional_secret_iam_binding.bindings,
|
||||
google_secret_manager_regional_secret_iam_member.members
|
||||
]
|
||||
}
|
||||
|
||||
output "secrets" {
|
||||
description = "Secret resources."
|
||||
value = google_secret_manager_secret.default
|
||||
value = local.o_secrets
|
||||
depends_on = [
|
||||
google_secret_manager_secret_iam_binding.default
|
||||
google_secret_manager_secret_iam_binding.authoritative,
|
||||
google_secret_manager_secret_iam_binding.bindings,
|
||||
google_secret_manager_secret_iam_member.members,
|
||||
google_secret_manager_regional_secret_iam_binding.authoritative,
|
||||
google_secret_manager_regional_secret_iam_binding.bindings,
|
||||
google_secret_manager_regional_secret_iam_member.members
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
output "version_ids" {
|
||||
description = "Version ids keyed by secret name : version name."
|
||||
value = {
|
||||
for k, v in google_secret_manager_secret_version.default : k => v.id
|
||||
}
|
||||
description = "Fully qualified version ids."
|
||||
value = { for k, v in local.o_versions : k => v.id }
|
||||
depends_on = [
|
||||
google_secret_manager_secret_iam_binding.default
|
||||
google_secret_manager_secret_iam_binding.authoritative,
|
||||
google_secret_manager_secret_iam_binding.bindings,
|
||||
google_secret_manager_secret_iam_member.members,
|
||||
google_secret_manager_regional_secret_iam_binding.authoritative,
|
||||
google_secret_manager_regional_secret_iam_binding.bindings,
|
||||
google_secret_manager_regional_secret_iam_member.members
|
||||
]
|
||||
}
|
||||
|
||||
output "version_versions" {
|
||||
description = "Version versions keyed by secret name : version name."
|
||||
value = {
|
||||
for k, v in google_secret_manager_secret_version.default : k => v.version
|
||||
}
|
||||
description = "Version versions."
|
||||
value = { for k, v in local.o_versions : k => v.version }
|
||||
depends_on = [
|
||||
google_secret_manager_secret_iam_binding.default
|
||||
google_secret_manager_secret_iam_binding.authoritative,
|
||||
google_secret_manager_secret_iam_binding.bindings,
|
||||
google_secret_manager_secret_iam_member.members,
|
||||
google_secret_manager_regional_secret_iam_binding.authoritative,
|
||||
google_secret_manager_regional_secret_iam_binding.bindings,
|
||||
google_secret_manager_regional_secret_iam_member.members
|
||||
]
|
||||
}
|
||||
|
||||
output "versions" {
|
||||
description = "Secret versions."
|
||||
value = google_secret_manager_secret_version.default
|
||||
description = "Version resources."
|
||||
value = local.o_versions
|
||||
sensitive = true
|
||||
depends_on = [
|
||||
google_secret_manager_secret_iam_binding.default
|
||||
google_secret_manager_secret_iam_binding.authoritative,
|
||||
google_secret_manager_secret_iam_binding.bindings,
|
||||
google_secret_manager_secret_iam_member.members,
|
||||
google_secret_manager_regional_secret_iam_binding.authoritative,
|
||||
google_secret_manager_regional_secret_iam_binding.bindings,
|
||||
google_secret_manager_regional_secret_iam_member.members
|
||||
]
|
||||
}
|
||||
|
||||
91
modules/secret-manager/regional.tf
Normal file
91
modules/secret-manager/regional.tf
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright 2025 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
locals {
|
||||
_tag_template_regional = (
|
||||
"//secretmanager.googleapis.com/projects/%s/locations/%s/secrets/%s"
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_secret_manager_regional_secret" "default" {
|
||||
for_each = { for k, v in var.secrets : k => v if v.location != null }
|
||||
project = local.project_id
|
||||
location = lookup(
|
||||
local.ctx.locations, each.value.location, each.value.location
|
||||
)
|
||||
secret_id = each.key
|
||||
labels = each.value.labels
|
||||
annotations = each.value.annotations
|
||||
version_aliases = try(each.value.version_config.aliases, null)
|
||||
version_destroy_ttl = try(each.value.version_config.destroy_ttl, null)
|
||||
expire_time = try(each.value.expiration_config.time, null)
|
||||
ttl = try(each.value.expiration_config.ttl, null)
|
||||
tags = each.value.tags
|
||||
deletion_protection = each.value.deletion_protection
|
||||
dynamic "customer_managed_encryption" {
|
||||
for_each = each.value.kms_key == null ? [] : [""]
|
||||
content {
|
||||
kms_key_name = lookup(
|
||||
local.ctx.kms_keys, each.value.kms_key, each.value.kms_key
|
||||
)
|
||||
}
|
||||
}
|
||||
# dynamic "rotation" {
|
||||
# for_each = try(each.value.rotation_config, null) == null ? [] : [""]
|
||||
# content {
|
||||
# next_rotation_time = each.value.rotation_config.next_time
|
||||
# rotation_period = each.value.rotation_config.period
|
||||
# }
|
||||
# }
|
||||
# topics
|
||||
lifecycle {
|
||||
ignore_changes = [
|
||||
rotation[0].next_rotation_time
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_secret_manager_regional_secret_version" "default" {
|
||||
for_each = {
|
||||
for v in local.versions :
|
||||
"${v.secret}/${v.version}" => v if v.location != null
|
||||
}
|
||||
secret = google_secret_manager_regional_secret.default[each.value.secret].id
|
||||
deletion_policy = each.value.deletion_policy
|
||||
enabled = each.value.enabled
|
||||
is_secret_data_base64 = try(
|
||||
each.value.data_config.is_base64, null
|
||||
)
|
||||
secret_data = (
|
||||
try(each.value.data_config.is_file, null) == true
|
||||
? file(each.value.data)
|
||||
: each.value.data
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
resource "google_tags_location_tag_binding" "binding" {
|
||||
for_each = { for k, v in local.tag_bindings : k => v if v.location != null }
|
||||
parent = format(
|
||||
local._tag_template_regional,
|
||||
local.tag_project,
|
||||
lookup(local.ctx.locations, each.value.location, each.value.location),
|
||||
google_secret_manager_regional_secret.default[each.value.secret].secret_id
|
||||
)
|
||||
location = lookup(local.ctx.locations, each.value.location, each.value.location)
|
||||
tag_value = lookup(local.ctx.tag_values, each.value.tag, each.value.tag)
|
||||
}
|
||||
|
||||
@@ -14,17 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "iam" {
|
||||
description = "IAM bindings in {SECRET => {ROLE => [MEMBERS]}} format."
|
||||
type = map(map(list(string)))
|
||||
default = {}
|
||||
variable "context" {
|
||||
description = "Context-specific interpolations."
|
||||
type = object({
|
||||
condition_vars = optional(map(map(string)), {})
|
||||
custom_roles = optional(map(string), {})
|
||||
iam_principals = optional(map(string), {})
|
||||
kms_keys = optional(map(string), {})
|
||||
locations = optional(map(string), {})
|
||||
project_ids = optional(map(string), {})
|
||||
tag_keys = optional(map(string), {})
|
||||
tag_values = optional(map(string), {})
|
||||
})
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "labels" {
|
||||
description = "Optional labels for each secret."
|
||||
type = map(map(string))
|
||||
default = {}
|
||||
}
|
||||
# variable "kms_autokey_config" {
|
||||
# description = "Key handle definitions for KMS autokey, in name => location format. Injected in the context $kms_keys:autokey/ namespace."
|
||||
# type = map(string)
|
||||
# nullable = false
|
||||
# default = {}
|
||||
# }
|
||||
|
||||
variable "project_id" {
|
||||
description = "Project id where the keyring will be created."
|
||||
@@ -38,22 +49,83 @@ variable "project_number" {
|
||||
}
|
||||
|
||||
variable "secrets" {
|
||||
description = "Map of secrets to manage, their optional expire time, version destroy ttl, locations and KMS keys in {LOCATION => KEY} format. {GLOBAL => KEY} format enables CMEK for automatic managed secrets. If locations is null, automatic management will be set."
|
||||
description = "Map of secrets to manage. Defaults to global secrets unless region is set."
|
||||
type = map(object({
|
||||
expire_time = optional(string)
|
||||
locations = optional(list(string))
|
||||
keys = optional(map(string))
|
||||
tag_bindings = optional(map(string))
|
||||
version_destroy_ttl = optional(string)
|
||||
annotations = optional(map(string), {})
|
||||
deletion_protection = optional(bool)
|
||||
kms_key = optional(string)
|
||||
labels = optional(map(string), {})
|
||||
global_replica_locations = optional(map(string))
|
||||
location = optional(string)
|
||||
tag_bindings = optional(map(string))
|
||||
tags = optional(map(string), {})
|
||||
expiration_config = optional(object({
|
||||
time = optional(string)
|
||||
ttl = optional(string)
|
||||
}))
|
||||
iam = optional(map(list(string)), {})
|
||||
iam_bindings = optional(map(object({
|
||||
members = list(string)
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
})), {})
|
||||
iam_bindings_additive = optional(map(object({
|
||||
member = string
|
||||
role = string
|
||||
condition = optional(object({
|
||||
expression = string
|
||||
title = string
|
||||
description = optional(string)
|
||||
}))
|
||||
})), {})
|
||||
version_config = optional(object({
|
||||
aliases = optional(map(number))
|
||||
destroy_ttl = optional(string)
|
||||
}), {})
|
||||
versions = optional(map(object({
|
||||
data = string
|
||||
deletion_policy = optional(string)
|
||||
enabled = optional(bool)
|
||||
data_config = optional(object({
|
||||
is_base64 = optional(bool, false)
|
||||
is_file = optional(bool, false)
|
||||
write_only_version = optional(number)
|
||||
}))
|
||||
})), {})
|
||||
# rotation_config = optional(object({
|
||||
# next_time = string
|
||||
# period = number
|
||||
# }))
|
||||
# topics
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "versions" {
|
||||
description = "Optional versions to manage for each secret. Version names are only used internally to track individual versions."
|
||||
type = map(map(object({
|
||||
enabled = bool
|
||||
data = string
|
||||
})))
|
||||
default = {}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for k, v in var.secrets :
|
||||
try(v.expiration_config.time, null) == null ||
|
||||
try(v.expiration_config.ttl, null) == null
|
||||
])
|
||||
error_message = "Only one of time and ttl can be set in expiration config."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for k, v in var.secrets :
|
||||
v.location == null || v.global_replica_locations == null
|
||||
])
|
||||
error_message = "Global replication cannot be configured on regional secrets."
|
||||
}
|
||||
validation {
|
||||
condition = alltrue(flatten([
|
||||
for k, v in var.secrets : [
|
||||
for sk, sv in v.versions : contains(
|
||||
["DELETE", "DISABLE", "ABANDON"], coalesce(sv.deletion_policy, "DELETE")
|
||||
)
|
||||
]
|
||||
]))
|
||||
error_message = "Invalid version deletion policy."
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user