Terraform security best practices help teams prevent cloud misconfigurations before infrastructure changes reach production. Secure Terraform means protecting state, keeping secrets out of code, reviewing modules and providers, enforcing least privilege, using policy guardrails, and scanning infrastructure as code in pull requests.
This guide is provider-neutral. It applies whether you use Terraform with AWS, Azure, Google Cloud, Kubernetes, Datadog, Cloudflare, or a multi-cloud platform.
Terraform security checklist
Start with these controls:
- Store Terraform state in an encrypted remote backend.
- Restrict state access with least privilege.
- Enable state locking and versioning.
- Keep secrets out of
.tffiles and state wherever possible. - Pin provider versions and review provider sources.
- Pin module versions and review module provenance.
- Use least-privilege cloud IAM for Terraform runs.
- Require pull request review for network, IAM, storage, and public exposure changes.
- Review Terraform plans before apply.
- Use policy-as-code or pre-merge guardrails for risky resources.
- Scan Terraform code in CI/CD.
- Monitor drift and high-risk out-of-band changes.
Corgea IaC scanning helps catch risky Terraform configuration before merge and gives developers clearer remediation guidance than raw policy output.
1. Protect Terraform state files
Terraform state can contain resource IDs, network topology, generated values, and sometimes sensitive data. Treat state like production infrastructure metadata.
Use a remote backend with encryption, locking, access control, and versioning:
terraform {
backend "s3" {
bucket = "company-prod-terraform-state"
key = "payments/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-locks"
}
}
State security best practices:
- do not store state on developer laptops for shared environments;
- restrict state bucket/container access to Terraform automation and platform owners;
- enable logging on state storage;
- separate state by environment and blast radius;
- rotate credentials that have had state access after suspected compromise.
2. Keep secrets out of Terraform files and outputs
Terraform secrets best practices start with not hardcoding secrets:
# Avoid
variable "db_password" {
default = "ChangeMe123!"
}
Use a secret manager or runtime identity instead. When Terraform must pass sensitive values, mark them as sensitive and avoid exposing them in outputs.
variable "db_password" {
type = string
sensitive = true
}
output "database_endpoint" {
value = aws_db_instance.main.endpoint
}
Even with sensitive = true, the value may still exist in state depending on the resource. Scan pull requests with Corgea secrets scanning and prefer secret managers such as Vault, AWS Secrets Manager, Azure Key Vault, or Google Secret Manager for runtime secrets.
3. Pin providers and review provider sources
Providers are executable plugins with access to cloud credentials. Pin versions and trust sources deliberately.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.80"
}
}
}
Provider practices:
- avoid unreviewed community providers in production;
- use dependency lock files;
- review major provider upgrades;
- separate credentials by environment;
- give Terraform only the permissions required to manage its resources.
4. Review and pin Terraform modules
Modules are a supply chain dependency. A module can create public storage, wildcard IAM policies, permissive security groups, or unexpected data paths.
Use explicit versions:
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.21.0"
name = "prod"
}
Before adopting a module, review:
- maintainer reputation and release history;
- variables that affect public exposure;
- IAM permissions created by the module;
- default security group behavior;
- outputs that expose sensitive data;
- upgrade notes and breaking changes.
Corgea dependency scanning is useful for application package risk; Terraform modules need the same mindset: know what you import and review changes before they merge.
5. Enforce least-privilege cloud IAM
Terraform often runs with powerful cloud permissions. Over-permissioned automation turns a compromised CI job into a cloud control plane incident.
Better patterns:
- separate read-only plan roles from apply roles;
- use environment-scoped roles;
- require approvals for production apply jobs;
- use OIDC from CI instead of static long-lived cloud keys;
- keep break-glass admin access outside normal Terraform automation.
Avoid wildcard policies unless there is a documented reason and compensating control.
6. Prevent public exposure and risky cloud defaults
Many Terraform security incidents come from small configuration mistakes:
0.0.0.0/0on administrative ports;- public object storage buckets;
- databases reachable from the internet;
- disabled encryption;
- missing logging;
- permissive Kubernetes cluster access;
- overly broad IAM trust policies.
Example of a security group rule that deserves review:
resource "aws_security_group_rule" "ssh_from_world" {
type = "ingress"
security_group_id = aws_security_group.admin.id
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
}
Pre-merge scanning should flag this before it becomes a production exposure. Corgea IaC scanning is designed for this merge-time guardrail.
7. Use policy-as-code and human review together
Policy-as-code is valuable, but it should not be the only review layer. Combine automated guardrails with reviewers who understand the business context.
Good candidates for policy:
- block public storage unless explicitly approved;
- require encryption on databases, queues, disks, and buckets;
- block admin ports from the internet;
- require tags for ownership and environment;
- prevent wildcard IAM actions in production roles;
- require approved registries for Kubernetes and container resources.
Use Corgea developer experience to keep remediation close to the pull request instead of sending developers to a separate backlog.
8. Review Terraform plans before apply
Terraform plans are security artifacts. Review them for:
- new public endpoints;
- IAM privilege expansion;
- deleted logging or monitoring;
- changed security groups;
- resource replacement that may destroy protected data;
- new secrets in outputs;
- provider or module upgrades.
For high-risk environments, separate plan and apply jobs. Store signed or immutable plan artifacts carefully, and avoid using untrusted artifacts in privileged apply workflows.
9. Scan Terraform in CI/CD
Run Terraform security checks before merge:
- format and validate Terraform;
- scan IaC for misconfigurations;
- scan secrets in
.tfvars, examples, and CI variables; - review plan output for dangerous changes;
- require approval for production apply;
- use OIDC for cloud credentials.
For a broader pipeline view, read CI/CD static analysis integration and the GitHub Actions security checklist.
10. Connect Terraform risk to application risk
Infrastructure findings become more useful when tied to application context. A public load balancer, Kubernetes ingress, or security group matters more when it exposes a vulnerable service.
Pair Terraform scanning with:
- Corgea AI SAST for application code;
- Corgea container scanning for deployed images;
- Corgea attack surface mapping for reachable endpoints;
- Corgea SBOM and license enforcement for software inventory.
Try Corgea scanning for Terraform
Want Terraform security best practices enforced before infrastructure changes merge?
- Corgea IaC scanning scans Terraform for risky cloud configuration.
- Corgea secrets scanning catches leaked credentials in Terraform files and variables.
- Corgea developer experience gives developers actionable PR feedback.
- Corgea container scanning and AI SAST connect infrastructure risk with the applications it deploys.
Try Corgea today or book a custom demo for your Terraform repositories.
If you are comparing IaC and AppSec scanning platforms, read Corgea vs Checkmarx, Corgea vs Snyk, and Corgea vs Semgrep. GitHub-native teams should also review Corgea vs GitHub Advanced Security.
FAQ
What are the most important Terraform security best practices?
The most important Terraform security best practices are protecting remote state, keeping secrets out of Terraform files and state where possible, pinning providers and modules, using least-privilege cloud IAM, reviewing plans, and scanning IaC in pull requests.
How do you secure Terraform state files?
Use an encrypted remote backend, restrict access with least privilege, enable state locking and versioning, avoid storing secrets in state where possible, and monitor access to state storage.
What are Terraform secrets best practices?
Do not hardcode secrets in .tf files, avoid outputs that expose sensitive values, use secret managers or Vault where possible, mark sensitive variables and outputs, and scan pull requests for leaked credentials.
Can Corgea scan Terraform code?
Yes. Corgea IaC scanning helps identify risky Terraform configuration and cloud misconfigurations before merge, while Corgea developer workflows bring findings into pull requests.