Terraform Managed Lifecycle for Google Secret Manager Secrets

Infrastructure as code brings repeatable provisioning to Google Cloud, but secrets remain a distinct problem. Database passwords, API keys, certificates and service account keys must be stored somewhere safe and they must not appear in Terraform code or in the Terraform state file in plain text. Google Cloud Secret Manager provides a centralized, encrypted, access-controlled store for sensitive values and Terraform can manage the entire lifecycle of the store, its metadata, access policies and versions.

This guide covers creating and managing secrets with Terraform, controlling access, handling rotation, and integrating secrets with applications while keeping the actual sensitive values out of state as much as possible.

The Challenge of Secrets in Terraform

There is an inherent tension with secrets in Terraform. If you use the secretdata argument or a normal managed password resource, Terraform needs to know the secret value to store it in Secret Manager, and that value ends up in the Terraform state file. With Terraform 1.11 or later and a current Google provider, use write-only arguments such as secretdata_wo where possible. The state file itself still needs to be treated as sensitive, which means encrypted storage and restricted access.

That said, managing secret metadata with Terraform is straightforward and valuable. Managing the actual secret values requires more care.

The state file locking behavior is relevant to any team working on the same configuration. When a user or process wants to make changes to the infrastructure defined in the Terraform configuration, it first requests and acquires a lock on the state file. Once the lock is acquired, Terraform allows the user or process to proceed with changes, ensuring exclusive access to the state. To force unlock the state, use the below command:

terraform force-unlock LOCK_ID

Enabling Secret Manager API with Terraform

Secret Manager must be enabled on the project before any secret resources can be created. The API enablement is modeled as a project service resource.

```

apis.tf - Enable the Secret Manager API

resource "googleprojectservice" "secretmanager" {
project = var.projectid
service = "secretmanager.googleapis.com"
disable
on_destroy = false
}
```

With the API enabled, Terraform can create secrets, secret versions, IAM bindings and replication settings.

Modeling Secret Containers as Code

A Secret Manager secret is a container. The container holds metadata such as secret_id, project, replication and labels, while the actual sensitive value lives in a secret version.

A single resource can create multiple secrets with for_each:

= { labels = { service = "web-api", type = "third-party" } } "worker/database-password" = { labels = { service = "worker", type = "database" } } "worker/sendgrid-api-key" = { labels = { service = "worker", type = "third-party" } } } } resource "google_secret_manager_secret" "secrets" { for_each = local.secrets secret_id = replace(each.key, "/", "-") project = var.project_id replication { auto {} } labels = merge(each.value.labels, { environment = var.environment managed_by = "terraform" }) }

Explicit secret resources are also common:

```

secrets.tf - Create secrets in Secret Manager

Database password secret

resource "googlesecretmanagersecret" "dbpassword" {
secretid = "database-password"
project = var.project
id
replication {
auto {}
}
labels = {
environment = var.environment
managedby = "terraform"
application = "web-api"
}
depends
on = [googleprojectservice.secretmanager]
}

API key secret

resource "googlesecretmanagersecret" "apikey" {
secretid = "external-api-key"
project = var.project
id
replication {
auto {}
}
labels = {
environment = var.environment
managed_by = "terraform"
}
}

TLS certificate

resource "googlesecretmanagersecret" "tlscert" {
secretid = "tls-certificate"
project = var.project
id
replication {
auto {}
}
labels = {
environment = var.environment
type = "certificate"
}
}
```

Best Practices

  • Manage secret metadata with Terraform, but be careful with secret values

Creating Secret Versions Without Persisting Values

Secret versions hold the actual sensitive data. The recommended pattern uses ephemeral generation and write-only arguments so the value is not stored in state.

```

Generate a random password for the database

ephemeral "randompassword" "dbpassword" {
length = 32
special = true
overridespecial = "!@#$%&*()-=+"
}

Store the password as a secret version without persisting it in state

resource "googlesecretmanagersecretversion" "dbpassword" {
secret = google
secretmanagersecret.dbpassword.id
secret
datawo = ephemeral.randompassword.dbpassword.result
secret
datawoversion = 1
}
```

For secrets that come from external sources, like an API key you received from a third party, you can pass the value through a Terraform input with write-only handling rather than persisting it in state.

Table of secret components

| Component | Managed by Terraform | Storage location |
| Secret container metadata | Yes | Secret Manager metadata |
| Secret version payload | Yes, via write-only args | Secret Manager encrypted storage |
| Terraform state file | No, must be protected | Encrypted backend with restricted access |

Access Control, Rotation, and Application Integration

Secret Manager is a secure and convenient storage system for API keys, passwords, certificates, and other sensitive data. Secret Manager provides a central place and single source of truth to manage, access, and audit secrets across Google Cloud.

Secret Manager provides a scalable and robust solution for secrets management, ensuring encryption, access controls, and audit trails.

In simple words:

  • You can easily store and retrieve your secrets
  • You can rotate and version your secrets
  • It integrates well with other GCP services
  • It provides audit logs to track access to secrets
  • It encrypts your secret data before storing it
  • You can use customer-managed encryption
  • You can control who can access these secrets

Why use Secret Manager?

With Secret Manager, you can store secrets centrally, rather than hardcoding them into your code or configuration files.

A practical pattern is shown in a Java-based big data project where Maven and NPM dependencies were stored on Google Cloud Artifact Registry. A service account was created with access to Artifact Registry. The team used Gradle to build the application with a GitHub Actions workflow and needed the service account key to communicate with Artifact Registry for dependencies in the workflow. The easiest approach was to create a GitHub secret with the service account key and use it in the CI workflow, but it raised security concerns.

The service account key should be rotated within a limited time frame, and it should be automated. It was suggested to use Secret Manager to store the service account key and pull the secret with the CI workflow using gcloud commands. It was also recommended to set up a Cloud Function with Cloud Scheduler to regularly rotate the service account key.

In this blog, the approach is to deploy GCP Secret Manager with Terraform to store the service account key. The service account key is stored in Secret Manager and accessed in CI using gcloud secrets versions access.

Outputs, Locking, and Operational Safety

Outputs make secret references discoverable without exposing values.

Add these outputs to your outputs.tf to make the secrets accessible:

```

Secret Manager outputs

output "artifactregistrysecretname" {
value = google
secretmanagersecret.artifactregistrysecret.name
description = "Name of the Artifact Registry service account secret"
}
output "artifactregistrysecretid" {
value = google
secretmanagersecret.artifactregistrysecret.secretid
description = "Secret ID for Artifact Registry service account"
}
output "database
secretname" {
value = google
secretmanagersecret.databasepassword.name
description = "Name of the database password secret"
}
output "api
keyssecretname" {
value = googlesecretmanagersecret.appapi_keys.name
description = "Name of the API keys secret"
}

Service account outputs

output "artifactregistrysaemail" {
value = google
serviceaccount.artifactregistry_sa.email
description = "Email of the Artifact Registry service account"
}

Instructions for accessing secrets

output "secretaccesscommands" {
value = {
artifact_registry = "gcloud secrets versions access latest
```

Table of example outputs

| Output | Resource reference | Purpose |
| artifactregistrysecretname | googlesecretmanagersecret.artifactregistrysecret.name | Name of the Artifact Registry service account secret |
| artifactregistrysecretid | googlesecretmanagersecret.artifactregistrysecret.secretid | Secret ID for Artifact Registry service account |
| database
secretname | googlesecretmanagersecret.databasepassword.name | Name of the database password secret |
| api
keyssecretname | googlesecretmanagersecret.appapikeys.name | Name of the API keys secret |
| artifact
registrysaemail | googleserviceaccount.artifactregistrysa.email | Email of the Artifact Registry service account |

Conclusion

Terraform can manage the full Secret Manager lifecycle when secrets are treated as metadata and versions rather than plain values in code. Enabling the Secret Manager API, declaring secret containers with replication and labels, and creating versions with write-only arguments such as secretdatawo allows teams to keep sensitive material out of the state file while still benefiting from versioning and audit trails. The state file remains sensitive and must be encrypted and access controlled, and write-only arguments available with Terraform 1.11 or later and a current Google provider reduce exposure of secret values.

Outputs provide discoverability for secret names and service account identifiers without leaking payloads, and operational safeguards such as state locking and force-unlock commands support safe collaboration. With Secret Manager, secrets can be stored centrally rather than hardcoded, rotated over time, and accessed via gcloud or integrated services with fine-grained IAM and audit logging. Managing secret metadata with Terraform, but being careful with secret values, is the durable pattern for secure infrastructure as code.

Sources

  1. OneUptime
  2. LivingDevOps

Related Posts