# Terraform and OpenTofu Best Practices for AWS and the Cloud

> A practical checklist of Terraform and OpenTofu best practices: state, modules, versions, security, CI/CD, naming, testing and cost control.

- Source: https://www.itwonderlab.com/terraform-best-practices/
- Published: 2026-02-24
- Updated: 2026-02-24
- Author: Javier Ruiz Jiménez (https://www.javierruizjimenez.com/)
- Site: IT Wonder Lab (https://www.itwonderlab.com/)

---

## A checklist for production-ready infrastructure code

These practices apply to Terraform and OpenTofu. Each section links to a detailed guide in this site.

### State

- Store the [state](https://www.itwonderlab.com/terraform-state/) in a remote [backend](https://www.itwonderlab.com/terraform-backend/) with locking, versioning and encryption, such as S3 with KMS. See [backends](https://www.itwonderlab.com/aws-terraform-tutorial-terraform-backends/).
- Never edit the state by hand and never commit it to Git.
- Split the state by layer and environment to limit the blast radius ([project structure](https://www.itwonderlab.com/terraform-project-structure/)).
- Encrypt it on the client with [OpenTofu state encryption](https://www.itwonderlab.com/terraform-state-file-encryption/) when possible.

### Code

- Pin the versions of Terraform or OpenTofu and of the providers, and commit `.terraform.lock.hcl`.
- Write [modules](https://www.itwonderlab.com/terraform-module/) for patterns that repeat, and keep them small.
- Use [`for_each`](https://www.itwonderlab.com/terraform-for-each-vs-count/) instead of `count` for collections.
- Add `description` and `type` to every [variable](https://www.itwonderlab.com/terraform-variables-outputs-locals/), and validate inputs.
- Do not hard-code values: AMIs, regions, account IDs and availability zones come from [data sources](https://www.itwonderlab.com/terraform-data-sources-remote-state/).
- Use [`moved`, `import` and `removed`](https://www.itwonderlab.com/terraform-import-moved-removed/) blocks to refactor safely.
- Protect critical resources with [`prevent_destroy`](https://www.itwonderlab.com/terraform-lifecycle-meta-argument/) and service-level deletion protection.
- Keep one tool for each job: do not mix Terraform with manual console changes. When someone changes something by hand, detect the [drift](https://www.itwonderlab.com/terraform-drift/) and fix it in code.

### Environments

- One AWS account per environment, with a separate state for each ([workspaces vs directories](https://www.itwonderlab.com/terraform-workspaces-vs-directories/)).
- Promote the same module version from dev to pro, not different code.

### Security

- No secrets in code or Git: [secrets management](https://www.itwonderlab.com/terraform-secrets-management/).
- Authenticate pipelines with [OIDC](https://www.itwonderlab.com/terraform-github-actions-aws-oidc/) and least-privilege [IAM roles](https://www.itwonderlab.com/aws-terraform-tutorial-aws-iam-roles-policies/), not static access keys.
- Run [security scanners](https://www.itwonderlab.com/terraform-security-scanning-tflint-checkov-trivy/) on every pull request.
- Encrypt everything that supports it with [KMS](https://www.itwonderlab.com/aws-kms/) and make buckets private by default.

### Process

- Always review the `plan` before the `apply`. Apply the same saved plan that was reviewed.
- Run Terraform only from CI/CD for shared environments.
- Use `-target` only in emergencies.
- Test: `fmt`, `validate`, lint and [native tests](https://www.itwonderlab.com/terraform-testing-opentofu-test/).
- Estimate the cost of changes before merging ([Infracost](https://www.itwonderlab.com/terraform-cost-estimation-infracost/)).
- Upgrade providers regularly in small steps and read their changelogs.

### Naming and tagging

- Use a consistent [naming convention](https://www.itwonderlab.com/aws-and-terraform-naming-best-practices/) and apply tags with the provider's `default_tags`:

```hcl title="providers.tf"
provider "aws" {
  region = "eu-west-1"

  default_tags {
    tags = {
      Environment = var.environment
      Project     = "demo"
      ManagedBy   = "opentofu"
    }
  }
}
```

See [AWS resource tagging](https://www.itwonderlab.com/aws-resource-tagging/).

### Documentation

- A README per module with inputs, outputs and an example.
- Generate it with `terraform-docs`.
- Explain decisions in comments, not what the code already says.

### Related

[Debugging](https://www.itwonderlab.com/how-to-debug-terraform/), [cheat sheet](https://www.itwonderlab.com/terraform-cheat-sheet/) and [AWS best practices](https://www.itwonderlab.com/best-practices/aws-best-practices/).
