Terraform and OpenTofu both validate configs before plan/apply; secure teams also lock providers, scan plans in CI, and protect state. Read now.
Validate early, then treat plan as the security gate
Terraform and OpenTofu both check configuration before they build a plan or change infrastructure. That validation catches syntax errors, unknown arguments, and type mismatches early. It does not prove the change is safe. A valid config can still open a security group to the world, grant a role broader permissions than intended, or replace a production database. Treat validation as a cheap first filter, not a security review.
The plan is where intent becomes concrete. It shows which resources will be created, updated, or destroyed and which attributes will change. Review plans for privilege expansion, public exposure, and destructive replacements before apply. Prefer automation that fails the pipeline when the plan introduces high-risk patterns, rather than relying on a hurried human skim at merge time.
Lock providers and pin what your modules trust
Provider plugins mediate every API call your configuration makes. Unlocked version ranges let a later run pull a different provider build than the one you tested. That can change defaults, add unexpected resources, or alter behavior in ways your last review never saw. Secure teams pin provider versions and commit the lock file so every workspace resolves the same artifacts.
Apply the same discipline to modules. Prefer modules you control or have reviewed, and pin module sources to exact versions or immutable references. Avoid floating tags and implicit “latest” behavior in production roots. When a provider or module must move, bump it deliberately, re-run plan review, and promote the lock change through the same CI path as application code.
- Commit provider lock files with every infrastructure root that runs in CI or production.
- Pin module versions; do not resolve modules from mutable branch tips in shared environments.
- Separate experimental upgrades into short-lived branches so production roots stay on known good pins.
Scan plans in CI before apply can run
Policy and security checks belong on the plan artifact, not only on the raw HCL. Static linting of configuration is useful, but the plan encodes the real delta: new public endpoints, widened IAM, deleted stateful resources, and cross-account attachments. Wire CI so that merge and deploy paths generate a plan, run scanners and policy checks against that plan, and block apply when findings exceed your threshold.
Keep apply out of developer laptops for shared environments. Use a pipeline identity with least privilege, store plan output as a build artifact for audit, and require an explicit approval step for production when the plan touches sensitive resources. If plan scanning only runs on a best-effort job that never fails the build, it will be ignored under schedule pressure.
Protect state as production data
State files map real resources to addresses, hold attribute values, and often include sensitive outputs or intermediate data. Anyone who can read or write state can learn your topology or force drift that the next apply will try to reconcile. Put remote state behind encryption at rest, strict access control, and backend locking so concurrent runs cannot corrupt it.
Limit who can pull state, who can unlock a stuck lock, and which pipelines may write to each backend. Prefer separate state per environment and per blast-radius boundary so a compromised non-production root cannot rewrite production mappings. Rotate credentials used by the backend and by providers on a fixed schedule, and treat accidental state commits the same way you treat leaked secrets: revoke access, rewrite history if needed, and move to a secured remote backend immediately.