Study guide
9.2 Sensitive Data Handling
HashiCorp Certified: Terraform Associate 004
Track your progress as you read
Finished sections get a check mark.
1 Introduction & Exam Overview
1.1 Current Exam Facts 1.2 Format, Delivery, Retakes, and Recertification 1.3 004 Changes, Terraform 1.12, and Study Plan
2 Infrastructure as Code with Terraform
2.1 What Infrastructure as Code Is 2.2 Advantages of IaC Patterns 2.3 Multi-Cloud, Hybrid, and Service-Agnostic Workflows
3 Terraform Fundamentals: Providers & State
3.1 Install and Version Terraform Providers 3.2 How Terraform Uses Providers 3.3 Configuration with Multiple Providers 3.4 How Terraform Uses and Manages State
4 Core Terraform Workflow: Init & Validate
4.1 The Terraform Write–Plan–Apply Workflow 4.2 Initialize a Working Directory 4.3 Validate a Configuration
5 Plan, Apply, Destroy, and Formatting
5.1 Generate and Review an Execution Plan 5.2 Apply Changes to Infrastructure 5.3 Destroy Terraform-Managed Infrastructure 5.4 Formatting and Style Adjustments
6 Resources, Data Sources, and References
6.1 Resource Blocks vs Data Blocks 6.2 Resource Attributes and Cross-Resource References 6.3 Resource Addressing and Interpolation Patterns
7 Variables, Outputs, and Complex Types
7.1 Input Variables, tfvars, and Precedence 7.2 Output Values 7.3 Complex Types: list, map, object, tuple, set
8 Dynamic Configuration and Dependencies
8.1 Expressions, count, for_each, and dynamic Blocks 8.2 Built-in Functions 8.3 Dependencies, depends_on, and Lifecycle Rules
9 Conditions, Secrets, and Sensitive Data
9.1 Custom Conditions: validation, precondition, postcondition, check 9.2 Sensitive Data Handling 9.3 Vault, Ephemeral Values, and Write-Only Arguments
10 Terraform Modules
10.1 How Terraform Sources Modules 10.2 Variable Scope within Modules 10.3 Using Modules in Configuration 10.4 Managing Module Versions
11 Terraform State Management
11.1 The Local Backend 11.2 State Locking 11.3 Remote State and the backend Block 11.4 Resource Drift and State Operations
12 Maintain Infrastructure with Terraform
12.1 Import Existing Infrastructure 12.2 Inspect State with the CLI 12.3 Verbose Logging and Diagnostics
13 HCP Terraform
13.1 Create Infrastructure with HCP Terraform 13.2 Collaboration and Governance Features 13.3 Workspaces and Projects 13.4 HCP Terraform Integrations
9.2 Sensitive Data Handling
Key Takeaways Explain This Topic AI
sensitive = true on a variable or output redacts the value in the Terraform CLI and HCP Terraform UI as (sensitive value)
Marking a value sensitive does not encrypt it and does not omit it from state or plan files — anyone with those files can read the plaintext
Expressions that reference a sensitive value become sensitive; a root output that includes one must itself set sensitive = true
terraform output redacts by default, but terraform output -json and terraform output -raw print sensitive values in plain text
Do not commit terraform.tfstate or secret .tfvars; prefer TF_VAR_ environment variables, a secret manager, and provider credentials from the environment
Last updated: August 2026
9.2 Sensitive Data Handling
Quick Answer: sensitive = true on a variable or output redacts the value in CLI logs and the HCP Terraform UI. Terraform still writes that value to state and plan files . Marking sensitive does not encrypt state. Do not commit terraform.tfstate or secret .tfvars . Prefer environment variables and a secret manager. terraform output -json and -raw print the plaintext. Section 9.3 is the newer path that omits values from state entirely.
Objective 4h on Terraform Associate (004) is: understand best practices for managing sensitive data, including secrets management with Vault. This section is the classic half — sensitive = true — on Terraform 1.12 . Official pages: Manage sensitive data , the variable sensitive argument , and the Protect sensitive input variables tutorial. HashiCorp is blunt: Terraform stores values with the sensitive argument in both state and plan files , and anyone who can access those files can access the sensitive values.
sensitive on variables and outputs shipped in 0.15 . It is still the default exam answer for "hide this from the terminal." It is not the answer for "keep this out of state." That is ephemeral / write-only, next section.
- What sensitive = true actually does variable "database_password" {
- description = "Password for the database instance"
- type = string
- sensitive = true
- }
- output "connection_string" {
- description = "Database connection string"
- value = "postgresql://${var.db_username}:${var.database_password}@${aws_db_instance.main.endpoint}/mydb"
- sensitive = true
- }
After apply, the CLI prints connection_string = (sensitive value) , not the URL. Any expression that references a sensitive variable or output is itself treated as sensitive. If aws_db_instance.main.password = var.database_password , the plan shows password = (sensitive value) even on the resource argument.
If you forget sensitive = true on an output that includes a sensitive variable, Terraform errors : "Output refers to sensitive values" / expressions in outputs can only refer to sensitive values if the output's sensitive attribute is true. That is a confirmation prompt in configuration form — you must admit you are exporting a secret.
Behavior Happens with sensitive = true ?
- Redacted in terraform plan / apply / destroy logs Yes — (sensitive value)
- Redacted in the HCP Terraform UI Yes
- Derived expressions also redacted Yes
- Root output must also be marked if it includes the value Yes, or Terraform errors
- Omitted from terraform.tfstate No
- Omitted from saved plan files No
- Encrypted in local state No — local state is plaintext
- Encrypted just because you set the flag No
Local state is a JSON file. HashiCorp's sensitive-variables tutorial has you grep the outputs and read "value": "notasecurepassword" sitting next to "sensitive": true . The flag is metadata. The secret is still there.
terraform output still needs care
Default terraform output (and terraform output connection_string ) respects the flag and prints (sensitive value) . Two flags punch through that protection, and the 1.12 manage-sensitive-data page names them:
terraform output -json — every output, including sensitive ones, as plain text JSON.
terraform output -raw NAME — the raw string, plain text .
terraform show -json has the same warning: sensitive values in state display in plain text. Automation that "just dumps outputs to the build log" is a leak. Treat those flags as explicit reveal , not as a safer machine format.
Do not commit state or secret tfvars
If you develop locally, Terraform writes terraform.tfstate (and terraform.tfstate.backup ) as plaintext , including variable values you flagged sensitive. Official guidance: treat the state file as sensitive data, exclude it from Git , and follow state-security practice. # typical Terraform .gitignore fragments
- *.tfstate
- *.tfstate.*
- *.tfvars
- *.tfvars.json
- crash.log
- .terraform/
GitHub's recommended Terraform gitignore ignores *.tfvars for this reason. A secret.tfvars that holds db_password = "..." is convenient and still a file on disk . Share it only with people who should see the secret, and never commit it. HashiCorp's tutorial uses -var-file="secret.tfvars" as a teaching step, then immediately warns you about version control.
Remote state changes the where , not the whether . With a remote backend, Terraform holds state in memory while a run is active. Encryption at rest is a property of the backend (HCP Terraform encrypts state at rest and uses TLS in transit; S3 depends on the bucket; Terraform itself did not encrypt the secret just because you set sensitive ). Anyone who can terraform state pull still sees the plaintext values.
Prefer env vars and secret managers over plaintext in files
HashiCorp's decision tree on the 1.12 manage-sensitive-data page: first decide whether you only need to hide a value in the CLI/UI, or whether you need Terraform to omit it from state and plan. This section is hide. The operational habit is still: do not put the secret in .tf source.
How the secret arrives Exam reading
Hard-coded in a resource or provider block Worst. It is in git history forever.
default on a variable Still in git. sensitive does not help.
secret.tfvars / *.auto.tfvars on disk Better separation, still a file; gitignore it.
TF_VAR_db_password in the environment Official tutorial pattern. Not in git. Still in your shell history and the process environment.
HCP Terraform workspace variable marked Sensitive Encrypted in HCP; still injected into the run and still lands in state if the resource stores it.
Vault / cloud secret manager (section 9.3) Preferred long-term. Read at runtime; pair with ephemeral / write-only so state never keeps a copy. export TF_VAR_db_username=admin
- export TF_VAR_db_password='a-long-random-secret'
- terraform apply
Terraform looks for TF_VAR_<NAME> and assigns it to var.<NAME> . That keeps the password out of .tf and out of a committed tfvars file. It does not keep the password out of state once a managed resource such as aws_db_instance.password stores it.
Provider credentials belong in the environment, not in committed files
004 still loves a provider "aws" block with access_key and secret_key written in HCL. That is a failing answer. Provider plugins read standard environment variables:
- Provider Typical env vars — not committed files
- AWS AWS_ACCESS_KEY_ID , AWS_SECRET_ACCESS_KEY , AWS_SESSION_TOKEN , or an instance/OIDC role
- Azure ARM_CLIENT_ID , ARM_CLIENT_SECRET , ARM_SUBSCRIPTION_ID , ARM_TENANT_ID
- Google GOOGLE_CREDENTIALS / Application Default Credentials
- Vault VAULT_ADDR , VAULT_TOKEN (HashiCorp's Vault-with-Terraform tutorials insist on this)
A provider block in git should name the region , the alias , maybe an assume-role ARN — not a secret. If you must pass a short-lived token into configuration, mark that variable sensitive = true and (on 1.12) prefer ephemeral = true so the token never hits state. That combination is the bridge into section 9.3.
Providers can also declare their own attributes as sensitive (the AWS provider already redacts aws_db_instance.password ). You should still mark the variable , because the same value may appear in an output, a local, or another resource that the provider does not redact for you.
Contrast with ephemeral — what this section is not
- Concern sensitive = true (this section) Ephemeral / write-only (9.3)
- CLI / UI redaction Yes Combine sensitive + ephemeral if you also want redaction
- Written to state Yes No
- Written to plan files Yes No
- Encrypts local state No No — it simply is not stored
- Minimum Terraform 0.15 1.10 (ephemeral), 1.11 (write-only args)
- Legal destinations Anywhere an expression is allowed Only other ephemeral or write-only contexts
If the question says "redact from the terminal," sensitive is enough. If it says "do not persist in state," sensitive is the wrong tool. HashiCorp's 1.12 page opens with that fork on purpose.
004 traps for classic sensitive data
sensitive = true is not encryption and not omission from state.
Local terraform.tfstate is plaintext, including flagged values.
terraform output -json and -raw print secrets. Default terraform output does not.
An output that interpolates a sensitive variable must itself be sensitive .
Provider access keys in committed .tf are wrong even if you add sensitive on an unrelated variable.
Ignoring *.tfstate but committing secrets.auto.tfvars still leaks.
Remote state can encrypt at rest; that is the backend, not the sensitive flag.