Securing state, secrets & the supply chain
The IaC attack surface, closed.
Infrastructure code is an attack surface in its own right, and it concentrates risk in three places: the state file, the secrets that flow through Terraform, and the modules and providers you did not write. Securing Terraform means closing all three — because whoever controls your Terraform controls your infrastructure.
State is a secret — treat it like one
State stores resource attributes in plaintext, including database passwords, private keys, and generated credentials — so the state file is as sensitive as anything in it. Keep it in a remote backend that encrypts at rest, lock down who can read that bucket to a tight list (state read is credential read), enable versioning for recovery, and never write it to a laptop, a Git repo, or CI logs. A leaked state file is a leaked credential store.
terraform {backend "s3" {bucket = "acme-tf-state"key = "prod/terraform.tfstate"encrypt = true # SSE at restkms_key_id = "arn:aws:kms:...:key/..." # your own KMS keydynamodb_table = "acme-tf-locks"# bucket policy: only the tf-ci role and platform admins can read this prefix}}
Keep secrets out of code and state
Hard-coding a password in a .tf file commits it to Git forever; passing it through Terraform lands it in state anyway. The better pattern is to not put the secret in Terraform at all — read it at apply time from a secrets manager via a data source (Vault, AWS Secrets Manager), or inject it as a TF_VAR_ environment variable in CI from the secret store. For values that must live in a repo, encrypt them with SOPS so the committed file is ciphertext. Minimize what touches state, and encrypt what must.
data "aws_secretsmanager_secret_version" "db" {secret_id = "prod/db/password" # fetched at apply, from the manager}resource "aws_db_instance" "main" {password = data.aws_secretsmanager_secret_version.db.secret_string# still lands in state → encrypted backend + tight access are mandatory}
The module & provider supply chain
Every external module and provider is code you run with full authority over your infrastructure — a malicious or compromised one could exfiltrate credentials or backdoor resources. Pin every module and provider to an exact version, commit the .terraform.lock.hcl so versions and checksums are enforced, prefer a private registry or vendored copies for critical modules, and review third-party modules before adopting them. This is the same supply-chain discipline as container images, applied to IaC.