IaC Security Cheat Sheet [2026]: Terraform & OpenTofu
Bottom Line
Shift-left IaC security is mostly workflow discipline, not one magic scanner: pin providers, validate early, review plans as sensitive artifacts, and keep state under strong access control. OpenTofu adds native state and plan encryption, while Terraform leans more on backend and platform controls.
Key Takeaways
- ›Commit
.terraform.lock.hcl; both tools use it to pin provider versions and checksums. - ›
validateneeds an initialized directory, so useinit -backend=falsefor fast local checks. - ›Saved plans and machine-readable JSON can expose secrets; treat them like credentials, not logs.
- ›Use
plan -detailed-exitcodefor CI gates, and keep-targetfor incident-only workflows. - ›OpenTofu has native state and plan encryption; Terraform relies on backend or HCP encryption controls.
Shift-left security for infrastructure as code is less about buying another scanner and more about tightening the default path every engineer already uses. For Terraform and OpenTofu, that means validating before remote access, pinning providers with a lock file, reviewing plans as sensitive artifacts, and making state storage boringly secure. The commands and patterns below were verified against official HashiCorp and OpenTofu docs on May 20, 2026, and optimized for breadth over depth.
Key Takeaways
- Commit
.terraform.lock.hcl; both tools use it to pin provider versions and checksums. validaterequires an initialized working directory, soinit -backend=falseis the fast local gate.- Saved plans, state, and machine-readable JSON should be handled like secrets, not disposable build output.
plan -detailed-exitcodeis the clean CI signal for no-change vs change vs failure.- OpenTofu adds native state and plan encryption; Terraform pushes that concern into backend and platform choices.
| Dimension | Terraform | OpenTofu | Edge |
|---|---|---|---|
| Core shift-left loop | init → validate → plan | init → validate → plan | Tie |
| Dependency lock file | .terraform.lock.hcl for provider versions and checksums | .terraform.lock.hcl for provider versions and checksums | Tie |
| Native state and plan encryption | Backend- or platform-dependent | Built-in support for encryption at rest | OpenTofu |
| Provider mirrors | Filesystem and network mirrors | Filesystem, network, and OCI mirrors | OpenTofu |
| Plan review in CI | -detailed-exitcode and show -json | -detailed-exitcode and show -json | Tie |
| Targeted operations | Supported, but documented as exceptional | Supported, but documented as exceptional | Tie |
Terraform vs OpenTofu
Bottom Line
If your goal is fewer dangerous IaC changes, both tools reward the same habits: validate before cloud access, pin providers, review plan output carefully, and lock down state. OpenTofu currently gives security-minded teams a notable extra with native state and plan encryption.
What actually changes for security
- Validation behavior: both tools validate configuration before plan and apply, and both support standalone validate for early feedback.
- Supply-chain controls: both tools use .terraform.lock.hcl to record provider selections and checksums.
- State exposure risk: both tools can surface sensitive values through artifacts and automation if you treat state or JSON output casually.
- Encryption posture: OpenTofu ships native state and plan encryption; Terraform expects you to get encryption from the backend or HCP platform layer.
show -json can return sensitive values in plain text. If you need to share snippets internally, sanitize them first with the Data Masking Tool.Daily Security Loop
Live Command Filter
Keyboard: / focus search, Esc clear, t jump to top, 1-5 jump sections.
Keyboard Shortcuts
| Shortcut | Action | Why it helps |
|---|---|---|
/ | Focus search | Jump straight to the command filter. |
Esc | Clear search | Reset the cheat sheet without touching the mouse. |
t | Scroll to top | Return to the overview fast during review. |
1 to 5 | Jump sections | Move between compare, loop, config, automation, and choose sections. |
Commands Grouped by Purpose
1. Fast local hygiene
- Use fmt for canonical formatting and cleaner diffs.
- Use init -backend=false to validate without touching remote state.
terraform fmt -check
terraform init -backend=false
terraform validate -json
tofu fmt -check
tofu init -backend=false
tofu validate -json
2. CI plan gate
- -detailed-exitcode gives a clean machine signal for no diff, diff, or failure.
- Save the plan when you need reproducible approval and apply steps.
terraform plan -out=tfplan -detailed-exitcode
terraform show -json tfplan > plan.json
tofu plan -out=tfplan -detailed-exitcode
tofu show -json -plan=tfplan > plan.json
3. Provider supply-chain hardening
- Mirror providers for controlled installs and reduced internet dependency.
- Review lock-file changes like dependency upgrades in application code.
terraform providers mirror -platform=linux_amd64 ./mirror
tofu providers mirror -platform=linux_amd64 ./mirror
4. Drift and incident response
- Use -refresh-only to reconcile state after intentional out-of-band changes.
- Use force-unlock only when you know the lock is stale and belongs to you.
terraform plan -refresh-only
terraform force-unlock LOCK_ID
tofu plan -refresh-only
tofu force-unlock LOCK_ID
5. Module test runs
- Use built-in test runners for reusable modules and guardrail assertions.
- Filter test files to keep feedback loops small during PR review.
terraform test -filter=tests/validations.tftest.hcl
tofu test -filter=tests/validations.tftest.hcl
Secure Config Patterns
Pin providers and make upgrades reviewable
- Declare provider sources and version constraints in code.
- Commit
.terraform.lock.hclso checksum and version changes show up in PRs. - Do not treat provider upgrades as an incidental side effect of init.
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
Keep secrets out of the happy path
- Mark values as sensitive for safer UI behavior, but do not mistake redaction for storage security.
- Prefer remote state with encryption and strong access control over local plaintext state on laptops.
- Pass dynamic backend settings through -backend-config rather than hard-coding environment-specific values everywhere.
variable "db_password" {
type = string
sensitive = true
}
terraform init -backend-config=prod.hcl
Use CLI configuration to constrain provider installs
- Filesystem mirrors reduce surprise downloads and help air-gapped or tightly controlled runners.
- Shared plugin caches can reduce repeated downloads, but they are not the same thing as a policy boundary.
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
provider_installation {
filesystem_mirror {
path = "/usr/share/terraform/providers"
include = ["hashicorp/*"]
}
direct {
exclude = ["hashicorp/*"]
}
}
OpenTofu-only encryption option
- If native encryption at rest for state and saved plans is a platform requirement, OpenTofu gives you a built-in path instead of pushing that entirely into backend selection.
- That reduces one class of accidental exposure, but it does not replace access control, key rotation, or artifact hygiene.
Advanced Automation
Encode assumptions with preconditions, postconditions, and checks
- Use precondition for assumptions you need before a resource is created.
- Use postcondition for guarantees the resulting object must satisfy.
- Use check blocks for non-blocking validation that still surfaces dangerous drift or bad outcomes.
resource "aws_instance" "app" {
ami = data.aws_ami.app.id
instance_type = var.instance_type
lifecycle {
precondition {
condition = data.aws_ami.app.architecture == "x86_64"
error_message = "The selected AMI must use x86_64."
}
}
}
check "private_subnet_only" {
assert {
condition = startswith(aws_instance.app.private_dns, "ip-")
error_message = "Instance must remain on the private network path."
}
}
Make plan review machine-friendly, but not casual
- Use JSON output for automation, but remember that machine-readable artifacts often reveal more than terminal output.
- Keep retention short for plan files and JSON reports if they can contain secrets or network topology details.
- Restrict who can download build artifacts from CI.
fmt, init -backend=false, validate -json, then a privileged plan -out job with artifact access locked down to reviewers.Test modules without pretending they are static files
- terraform test and tofu test are useful for module contracts and assertions.
- Tests can create real infrastructure, so run them in dedicated accounts or isolated projects with cleanup discipline.
terraform test -test-directory=tests -filter=tests/outputs.tftest.hcl
tofu test -test-directory=tests -filter=tests/outputs.tftest.hcl
When to Choose Which
Choose Terraform when:
- Your delivery model already depends on HCP Terraform or Terraform Enterprise workflows.
- Your team wants the smallest process change from an existing HashiCorp-centered platform.
- Your encryption, access control, and audit posture are already solved at the backend and platform layers.
Choose OpenTofu when:
- You want native state and plan encryption in the toolchain itself.
- You want OCI-based provider mirror options and a more open governance story.
- You need near-drop-in workflow familiarity but want extra control over artifact handling.
Shared non-negotiables
- Keep state out of Git.
- Review lock-file changes in code review.
- Restrict artifact downloads for plans and JSON exports.
- Treat
-targetas a recovery lever, not a normal deployment feature.
Frequently Asked Questions
Is sensitive = true enough to protect Terraform or OpenTofu secrets?
+
Should I commit .terraform.lock.hcl to Git?
+
What is the safest basic CI gate for IaC pull requests? +
fmt, then init -backend=false, then validate -json, followed by a privileged plan -detailed-exitcode job. Keep plan files and JSON output behind restricted artifact access because they can expose sensitive data.
Is -target safe for normal Terraform or OpenTofu deployments?
+
Do terraform test and tofu test create real infrastructure?
+
Get Engineering Deep-Dives in Your Inbox
Weekly breakdowns of architecture, security, and developer tooling — no fluff.