Module 4 · Infrastructure as Code with Terraform
State and secrets
Understand what Terraform's state file contains, why it must be stored remotely with locking and encryption, and find the secrets sitting in plain text inside Tallybook's state.
About 25 minutes
The problem
A Tallybook engineer once emailed the state file to a contractor "so they could see what we have". It seemed harmless: it's just a list of resources. But state records every attribute of every resource Terraform manages, and some attributes are secrets.
Terraform hides sensitive values when it prints them. It doesn't hide them in the file.
The concept
What state is for
Terraform compares three things on every plan: your code (what you want), the state (what it created last time), and the real infrastructure (what exists now). State is how it knows that aws_instance.api[2] is the server with ID r-0017.
Where state should live
| Practice | Why |
|---|---|
| Remote backend (for example an encrypted storage bucket) | everyone and the pipeline use the same state; it isn't lost with a laptop |
| Locking | two people running apply at once can corrupt state |
| Encryption and tight access | state contains secrets |
| Never in git, never emailed | anyone who has it has the secrets |
Secrets in state
Database passwords, generated keys and some API tokens end up in state. Mark variables and outputs sensitive = true so they're hidden in output, but treat the state file itself as a secret. Better still, let the database generate and keep its own password in a secrets manager, so it never passes through Terraform.
Example
Terraform itself hides the output. Here's what terraform output shows for Tallybook's state (from a real run of Terraform 1.9.5 on this file):
db_endpoint = "tallybook-prod.c9x2.af-south-1.rds.example:5432"
db_password = <sensitive>Now read the file directly:
import json
from urllib.request import urlopen
with urlopen("https://academy.cloudtechanalytics.com/datasets/terraform/terraform.tfstate") as f:
state = json.load(f)
print("Output marked sensitive:", state["outputs"]["db_password"]["sensitive"])
print("Its value in the file:", state["outputs"]["db_password"]["value"])Output marked sensitive: True
Its value in the file: Tallyb00k-Prod-2025!"Sensitive" only means "don't print it". Search the whole state for attributes that look secret:
SECRET_WORDS = ("password", "secret", "token", "private_key")
def find_secrets(state):
found = []
for r in state["resources"]:
for inst in r["instances"]:
for key, value in inst["attributes"].items():
if any(w in key for w in SECRET_WORDS) and value:
found.append((f"{r['type']}.{r['name']}", key, value[:4] + "..."))
return found
for item in find_secrets(state):
print(item)('aws_db_instance.prod', 'password', 'Tall...')
('aws_db_instance.staging', 'password', 'stag...')Both database passwords are in the file, in full (shortened here). The production one is the password for a database that, per its own attributes, is publicly accessible. Anyone who received that email could have connected to Tallybook's production database.
Walkthrough
- Run the cells. Find the production database's
publicly_accessibleandstorage_encryptedattributes. What do they mean together with the password finding? - List everyone who might have a copy of this state file (laptops, email, chat, CI logs).
- Write the steps to secure the state and rotate the password (the task below).
- Look up how your cloud provider's secrets manager could generate the database password instead.
Practice
Practice
How many secret-looking attributes does find_secrets find in the state's resources?
Task
8 minWrite the steps to secure Tallybook's state and secrets, one numbered step per line: at least five, covering rotating the exposed password, a remote backend with locking and encryption, access to the state, keeping it out of git, and getting passwords out of state.
Your work is checked for
- At least five numbered steps
- Rotate the password
- A remote backend
- Locking
- Encryption
- Restricted access
- Out of git
- Passwords out of state (secrets manager, generated, managed)
Check your understanding
Answer every question to check.