# Google Secret Manager
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.
- [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)
- [IAM](#iam)
- [APIs](#apis)
## Global Secrets
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" {
source = "./fabric/modules/secret-manager"
project_id = var.project_id
secrets = {
test-auto = {}
test-auto-cmek = {
kms_key = "projects/test-0/locations/global/keyRings/test-g/cryptoKeys/sec"
}
test-user = {
global_replica_locations = {
europe-west1 = null
europe-west3 = null
}
}
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 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
```
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L44) | Project id where the keyring will be created. | string | ✓ | |
| [context](variables.tf#L17) | Context-specific interpolations. | object({…}) | | {} |
| [project_number](variables.tf#L49) | Project number of var.project_id. Set this to avoid permadiffs when creating tag bindings. | string | | null |
| [secrets](variables.tf#L55) | Map of secrets to manage. Defaults to global secrets unless region is set. | map(object({…})) | | {} |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [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. | ✓ |
## Requirements
These sections describe requirements for using this module.
## IAM
The following roles must be used to provision the resources of this module:
- Cloud KMS Admin: `roles/cloudkms.admin` or
- Owner: `roles/owner`
## APIs
A project with the following APIs enabled must be used to host the
resources of this module:
- Google Cloud Key Management Service: `cloudkms.googleapis.com`