Using Terraform's write-only arguments with GCP Secret Manager

Starting with Terraform v1.11, we now have a cleaner and more secure way to handle sensitive values - write-only arguments. This post walks through a simple example of using that feature with Google Cloud Secret Manager.

What’s the Problem?

Before this feature, if you wanted to create a secret version in GCP, your secret value had to go directly in your Terraform code or variable, and unfortunately it ended up stored in plain text in your state file (or plan file).

Example:

resource "google_secret_manager_secret_version" "version" {
  secret      = "some-secret-id"
  secret_data = "my secret"
}

That “my secret” string? It’d sit right there in your state. Not ideal.

Write-Only Arguments

Terraform 1.11+ introduces write-only fields — like secret_data_wo and secret_data_wo_version. These let you safely pass secrets at runtime without saving them in your state file.

Here’s how the new approach looks:

variable "secret_string" {
  type    = string
  default = "my-secret"
}

resource "google_secret_manager_secret_version" "version" {
  secret                 = "some-secret-id"
  secret_data_wo         = var.secret_string
  secret_data_wo_version = parseint(substr(sha256(var.secret_string), 0, 4), 16)
}

In this example:

  • secret_data_wo: passed at runtime, not saved in state
  • secret_data_wo_version: stored in state, used to track changes

You’re still triggering updates when the secret changes, but the secret itself never touches the state file. Nice.

But Wait! What’s That Hash Expression?

This line is doing some lightweight versioning:

parseint(substr(sha256(var.secret_string), 0, 4), 16)

Breakdown:

  • Hashes the secret string with SHA-256
  • Takes the first 4 hex chars (16 bits)
  • Parses it as an integer

It’s not cryptographically bulletproof, but it’s good enough for:

  • Triggering secret updates when content changes
  • Avoiding full-hash complexity
  • Preventing secrets from leaking into state

This approach is a pragmatic middle ground — especially when your secrets are already high-entropy, like API keys or JWTs.

Final Tip

If you’re using a file to pass in secrets (terraform.tfvars or similar), make sure to add it to .gitignore so you don’t accidentally commit your secrets.

TL;DR

  • Use *_wo fields (for supported resources) to avoid secrets leaking into state
  • Use a short hash-based version number to track changes
  • Works great for high-entropy secrets and keeps your infra code clean and safe

The example code is available in this my repository.

Bye-bye!

Written on June 3, 2025