Join our Newsletter — 33% off our NHI Course
Home› Guides› How to find secrets in Terraform state: Locate…
Guide Secrets Management

How to find secrets in Terraform state: Locate every copy, list exposed values and rotate

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 11 November 2026 13 min read
On this page

Terraform state files hold the real values of database passwords, generated keys and API tokens, and marking a variable sensitive does not keep any of them out. HashiCorp's documentation is explicit that sensitive values are stored in both state and plan files, and that terraform show -json prints them in plain text. Most teams also have more copies of state than they think: old object versions in the backend bucket, local terraform.tfstate files committed to Git, saved plan files in CI artefacts. This runbook shows how to find every copy, list which attributes hold secrets, scan for credentials that are still live, and then rotate and clean up in an order that does not break deployments. It is for platform, cloud security and DevOps engineers. For how to secure the backend and pipeline credentials going forward, see the Terraform and IaC Pipeline Credential Security Guide.

This is an independent guide from NHI Mgmt Group, based on HashiCorp and AWS documentation read on 11 November 2026. Commands are read-only until the remediation steps.

Key takeaways

  • sensitive is display masking, not protection. It redacts CLI and UI output; the value is still in state, and terraform output -json or -raw shows it.
  • Find the copies before the contents. Backend objects, prior versions, workspace paths, .tfstate.backup files, Git history and plan artefacts each need checking.
  • Use the JSON format to list what is sensitive. Each resource in terraform show -json carries a sensitive_values object marking secret attributes with true, which gives an inventory without printing values.
  • Read access to state is credential access. Anyone who can s3:GetObject the state key, download a state version, or use terraform_remote_state can read every secret in it.
  • Rotation comes before deletion. Old state versions and Git history keep the old value, so a secret found in state is treated as exposed and rotated, then the copies are cleaned up.

When you need this

  • Access review: you are reviewing who can read state buckets or HCP Terraform workspaces and need to know what that access really exposes.
  • Leak or misconfiguration: a state bucket was public, a CI log printed state, or a repository with committed state was made public.
  • Migration: before adopting ephemeral resources and write-only arguments, you need the list of attributes to move.
  • Audit evidence: an assessor asks where infrastructure secrets are stored and who can read them. NIST SP 800-53 SC-28 (Protection of Information at Rest) and IA-5(7) are the controls usually in play.

Leaks from infrastructure tooling follow the same pattern as other leaked secrets: the credential is found by someone else first. The Sisense Breach is reported to have started with a credential in a code repository that led to storage holding customer tokens.

Before you start

NeedDetail
Read access to stateS3 backend: s3:ListBucket on the bucket and s3:GetObject on state keys (plus kms:Decrypt if kms_key_id is set). HCP Terraform: read access to state versions for each workspace
A safe workstationOutput of these steps contains live secrets. Work on a hardened host or isolated runner, write output to an encrypted location, and delete it when done
ToolsTerraform CLI matching the configurations, jq, and a secret scanner that can scan files (for example gitleaks dir or TruffleHog filesystem)
ApprovalA ticket naming the scope. Reading every state file is itself a privileged action and should be logged as such
OwnersA list of workspace or stack owners, because rotation will need them

Where state and its copies live

LocationHow to find itWhat it can contain
S3 backend, default workspaceThe bucket and key in each backend "s3" blockCurrent state for that configuration
S3 backend, other workspacesObjects under workspace_key_prefix (default env:) as env:/<workspace>/<key>One state per workspace, often forgotten test or feature workspaces
Prior object versionsBucket versioning, which HashiCorp strongly recommends for recoveryEvery earlier state, including secrets that were later rotated or removed
HCP Terraform or Terraform EnterpriseGET /state-versions filtered by workspace and organisation; GET /workspaces/:workspace_id/current-state-versionCurrent and historical state versions per workspace
Local stateterraform.tfstate and terraform.tfstate.backup on laptops, jump hosts and runnersPlaintext state, often from before a backend was configured
Git historySearch repositories and history for *.tfstate and *.tfstate.backupState committed by mistake, still in history after a later delete
Saved plans and CI artefactsPipeline artefacts and caches holding terraform plan -out files or JSON plan outputPlan files carry sensitive values just as state does
Remote state consumersSearch configurations for data "terraform_remote_state"Not a copy, but a read path to the entire snapshot for every consumer

Step-by-step

Phase 1: inventory the copies (read-only)

  1. List backends from code: search all infrastructure repositories for backend "s3", backend "gcs", backend "azurerm", cloud { and backend "remote" blocks and record bucket, key, prefix and workspace or organisation names.
  2. List objects in each state bucket, including all keys under the workspace prefix and, where versioning is on, all object versions. Count versions per key: a key with hundreds of versions carries hundreds of historical secrets.
  3. List HCP Terraform state versions per workspace with GET /state-versions?filter[workspace][name]=<ws>&filter[organization][name]=<org>. Each version returns hosted-state-download-url and, for state written by Terraform 1.3 or later, hosted-json-state-download-url.
  4. Search repositories and history for committed state and plan files. A scanner in Git mode (for example gitleaks git with --log-opts="--all") reads all branches and history, not just the working tree.
  5. Search CI systems for retained artefacts named *.tfplan, plan.out or tfplan.json, and for job logs that ran terraform show -json or terraform output -json.
  6. List remote state consumers: every terraform_remote_state data source and the identity it runs as. HashiCorp warns that this data source needs access to the entire state snapshot and should not be used where resources handle sensitive data.

Phase 2: list which attributes are sensitive (without printing values)

  1. Pull state for each configuration with terraform state pull (raw format, written to stdout) or download the JSON state version from HCP Terraform, saving to the encrypted working area.
  2. Convert to the documented JSON format with terraform show -json <state file>. Treat the output as secret: HashiCorp's documentation states that sensitive values are displayed in plain text with -json.
  3. Walk the module tree: values.root_module.resources[] and, recursively, child_modules[].resources[]. For each resource record address, type and the keys inside sensitive_values that are true. A jq filter such as .values.root_module.resources[] | select(.sensitive_values != {}) | {address, type, sensitive: .sensitive_values} lists these without touching values; repeat for child modules.
  4. Check outputs: values.outputs maps each root output to value, type and sensitive. Any output with sensitive: true is readable by anyone who can read outputs.
  5. Do not trust the flag alone. Providers decide which attributes are marked sensitive. Attributes such as user data, connection strings, environment variable maps and templated configuration files often contain secrets without being flagged.

Phase 3: scan for credentials, including unflagged ones

  1. Scan the pulled files with a file scanner, for example gitleaks dir <path> --redact --report-format json --report-path <report> or trufflehog filesystem <path>. Use redaction so the report does not become another copy of every secret.
  2. Scan the bucket directly if pulling is impractical: TruffleHog documents an s3 source (trufflehog s3 --bucket=<bucket> --results=verified,unknown) and a gcs source.
  3. Decide on verification deliberately. TruffleHog's "verified" status means it tested the credential against the provider's API. That confirms exposure quickly but is a live authentication from your scanning host; agree it with the owners and expect it to appear in the provider's logs.
  4. Merge the results into one table per secret: location(s), resource address, secret type, owner, whether live, and which identities can read the location.

Phase 4: remediate

  1. Rotate first, starting with live credentials in locations with broad read access. For database and service passwords set by Terraform, use the provider's write-only argument (names usually end in _wo, with a _wo_version to trigger change) where available, so the new value never enters state. See Credential Rotation at Scale.
  2. Move secrets out of state: read secrets at run time through ephemeral resources (Terraform 1.10 or later) or have the service generate and store its own secret in a secrets manager. Ephemeral values cannot be root module outputs, so redesign consumers to read from the secrets manager instead.
  3. Replace terraform_remote_state where state is sensitive, with tfe_outputs on HCP Terraform or with values published to a parameter store that has its own access control.
  4. Clean up copies only after rotation: remove committed state from Git history, expire old object versions with a lifecycle rule that keeps enough history for recovery, delete stale local and CI copies. Keep at least the most recent good versions so you can roll back state.
  5. Restrict read access to state keys to the pipeline identity and a break-glass role, and enable logging of object reads (see below).

Choosing how far to go

OptionEffortEffect on stateChoose when
Restrict and log read access onlyLowSecrets remain; fewer people can read themSmall estates, or as the first week's action
Rotate exposed values, keep designMediumNew values still land in stateAfter a confirmed exposure, when providers lack write-only support
Write-only arguments and ephemeral resourcesMedium to high; needs Terraform 1.10 or 1.11 and provider supportValues never written to state or planDatabase passwords, generated credentials, provider secrets
Secret generated and held outside TerraformHighState holds only a referenceShared or high-value secrets, regulated environments

Small teams with one state bucket can finish all four phases in a day. Large estates with hundreds of workspaces should automate Phases 1 to 3 per workspace, prioritise production and shared-services state, and track remediation as a backlog with owners.

Verify it worked

  1. Re-run the Phase 2 inventory and confirm rotated attributes now hold new values, or are gone where write-only arguments or ephemeral resources were adopted.
  2. Re-run the scanner on current state and confirm no verified findings remain.
  3. Confirm old values fail authentication at the provider, which proves rotation rather than just a state change.
  4. Run terraform plan for each changed configuration and confirm no unexpected changes.

Detection: enable S3 server access logging on state buckets (delivery is best effort and can take hours) or object-level CloudTrail data events for GetObject, and alert on any reader other than the pipeline role and break-glass role. In HCP Terraform, review who holds state read permission per workspace. Alert on new *.tfstate files appearing in repositories.

Metrics:

  • Workspaces scanned: share of workspaces and state buckets covered by the inventory.
  • Live secrets in state: count of verified credentials in current state, trending to zero for production.
  • Sensitive attributes moved: share of flagged attributes migrated to write-only or ephemeral patterns.
  • State readers: number of identities able to read each production state; target two or fewer.
  • Stray copies: state or plan files found in Git or CI artefacts per quarter.

Rollback and common errors

  • State corrupted during clean-up: restore the last good object version (or state version in HCP Terraform). This is why lifecycle rules should never delete the most recent versions.
  • Rotation broke a dependent service: the consumer read the secret from state or an output. Roll forward by pointing the consumer at the secrets manager rather than restoring the old value.
  • terraform show -json errors on schema: providers newer than the state were installed. Use the Terraform and provider versions pinned by that configuration.
  • Write-only change not applied: changing a write-only value requires incrementing its _wo_version.
  • Scanner reports flood: base64 and JSON-encoded blobs in user data trigger generic rules. Triage by resource type and suppress by fingerprint, not by turning rules off.

Common failure patterns

  • Relying on sensitive = true and assuming state is clean, then granting broad state read to developers.
  • Cleaning current state but not versions: bucket versioning keeps every earlier secret until lifecycle rules expire it.
  • Forgotten workspaces: feature and test workspaces under env:/ hold real credentials long after the environment is gone.
  • Remote state as an integration API: dozens of configurations read a shared networking state that also holds database passwords.
  • Scanner output as a new leak: unredacted reports stored in CI artefacts or tickets. The same applies to CI job logs that ran terraform output -json.

Framework mapping

FrameworkReferenceRelevance
NIST SP 800-53 Rev 5SC-28 Protection of Information at Rest; IA-5(7) No Embedded Unencrypted Static Authenticators; AU-6 Audit Record ReviewSecrets at rest in state; review of state reads
NIST CSF 2.0PR.DS-01; PR.AA-01Data-at-rest protection; managed service credentials
CIS Controls v8.13.11 Encrypt Sensitive Data at RestBackend encryption and access to state
ISO/IEC 27001:2022 Annex AA.5.17 Authentication information; A.8.24 Use of cryptographyHandling of secrets and their encryption
MITRE ATT&CKT1552.001 Credentials In Files; T1530 Data from Cloud StorageState files in buckets are a known credential source

Where identity fits

Almost every secret found in state belongs to a non-human identity: a database account, a cloud access key, a service principal secret, an API token. Record each finding against its identity owner, and prefer designs where the pipeline itself authenticates with short-lived federated credentials so there is no long-lived provider key to land in state. See the Terraform Cloud and HCP Security Guide and the OWASP NHI Top 10 NHI2 Secret Leakage Guide.

Practitioner checklist

  • Inventory every backend, workspace, object version, local copy, Git copy and plan artefact.
  • Work on an isolated host and redact scanner output.
  • List sensitive attributes from sensitive_values and sensitive outputs per configuration.
  • Scan state for unflagged secrets in user data, maps and templates.
  • Rotate live findings before deleting any copy.
  • Adopt write-only arguments and ephemeral resources where providers support them.
  • Replace terraform_remote_state for sensitive state.
  • Limit state read to the pipeline role and break-glass, and log every read.
  • Expire old object versions with a lifecycle rule that keeps recent versions for recovery.

Standards and references

Related NHI Mgmt Group resources: Terraform and IaC Pipeline Credential Security Guide · Terraform Cloud and HCP Security Guide · Atlantis Security · HashiCorp credentials · Leaked Credential Response Playbook

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 11 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org