Ship infrastructure safely — keep secrets out of code and state, scan configurations for misconfigurations with tfsec, Checkov, and KICS, and enforce rules with policy as code.
Why: the cardinal rule is no secrets in .tf files — they end up in git forever. Pass them at run time through TF_VAR_ environment variables or a secrets manager, mark any secret variable and output sensitive so it is redacted, and remember state stores secrets in plaintext, so the backend must be encrypted and access-controlled.
variable "db_password" {
type = string
sensitive = true # redacted in plan/apply output
}
# Supply it from the environment, never in a committed file:
# export TF_VAR_db_password="..."
output "db_password" {
value = var.db_password
sensitive = true
}Why: it is easy to write valid HCL that is insecure — a public bucket, an open security group, an unencrypted volume. Static scanners read your configuration and flag these against a rule library. tfsec, Checkov, and KICS all do this; run at least one in CI so insecure infrastructure never merges.
tfsec — fast, Terraform-focused
tfsec .Checkov — multi-framework, hundreds of policies
checkov -d .KICS — broad IaC coverage
kics scan -p .Note: a scanner reports an ID, a severity, and the file/line — for example tfsec's aws-s3-enable-bucket-encryption on a bucket missing encryption. Fix real issues; for an accepted risk, suppress that one rule with an inline comment so the scan stays green and the decision is documented. Never blanket-disable a scanner to make CI pass.
resource "aws_s3_bucket" "logs" {
bucket = "my-app-logs"
# Example: consciously ignore one rule, with a reason, inline:
#tfsec:ignore:aws-s3-enable-bucket-logging -- this *is* the log bucket
}Why: scanners catch known misconfigurations; policy as code enforces YOUR organization's rules — "every resource must be tagged", "no public databases", "only these regions". Tools like Open Policy Agent (Conftest) and HashiCorp Sentinel evaluate the plan against written policies and block an apply that violates them. This is how guardrails scale across many teams.
terraform plan -out=tfplan
│
▼ (convert plan to JSON, evaluate against policy)
policy engine (OPA / Conftest / Sentinel)
│
├─ pass ─▶ terraform apply tfplan
└─ fail ─▶ block the apply, report the violated rule