Terraformon AWS

Deploy AWS infrastructure reliably with Terraform — Stacks, ephemeral values, provider-defined functions, Test Framework, and OpenTofu state encryption for teams that need an OSI-licensed alternative.
Infrastructure as Code. Updated April 29, 2026. Reviewed by FactualMinds AWS-certified architects (Solutions Architect – Professional).
Terraform on AWS in figures

Stacks GACoordinated multi-configuration deployments on HCP Terraform
EphemeralValues and resources that never hit state (1.10+)
OpenTofu1.8+ adds client-side state encryption and early resources

Where Terraform meets AWS
Terraform keeps state in S3 encrypted with KMS, and CI signs in through OIDC to an IAM role. See what else meets there
- IAM
- IAM Identity Center
- S3
- KMS
- Organizations
Ship it in 5 steps
Set up Terraform on AWS cleanly: the short version. Every step is expanded in the guide below.

Install and authenticate
Install Terraform (or OpenTofu) via tfenv or your package manager. For CI, configure GitHub Actions / GitLab / Buildkite with AWS OIDC federation — no long-lived access keys. Local developers use AWS IAM Identity Center SSO with short-lived credentials.
Provision a remote state backend with locking
Create a dedicated `tfstate` S3 bucket per account with versioning, KMS CMK encryption, TLS-only bucket policy, Block Public Access, and Object Lock where compliance requires it. Add an S3-native lockfile (Terraform 1.10+) or a DynamoDB lock table for older versions. One backend bucket per AWS account; one state key per environment/workload.
Bootstrap an account scaffolding repo
Use AWS Organizations + Control Tower as the landing-zone source of truth; your Terraform repo manages workload accounts, IAM Identity Center assignments, VPCs, baseline SCPs, Security Hub, and Config. Separate state per account.
Write modules with pinned versions and tests
Pin `hashicorp/aws` (e.g., `~> 5.0` → `~> 6.0` when upgrading), pin module versions, and add `terraform test` coverage for security-critical invariants (no public S3, encryption everywhere, least-privilege IAM). Use ephemeral values for secrets and tokens so nothing lands in state.
Wire CI with plan-on-PR, apply-on-merge, and drift detection
GitHub Actions via OIDC → Terraform plan on PR (comment on PR), apply on merge to main, and a nightly `terraform plan` to detect drift. For regulated workloads, gate apply behind Sentinel (HCP Terraform) or OPA policies and a manual approval.
Terraform on AWS in 2026
Terraform is still the default IaC tool on AWS for enterprise teams. What changed over the last two years is (a) Terraform Stacks going GA on HCP Terraform, (b) ephemeral values and ephemeral resources finally removing plaintext secrets from state files, (c) provider-defined functions cleaning up a lot of HCL gymnastics, (d) a production-ready Test Framework, and (e) OpenTofu maturing as a credible OSI-licensed alternative with client-side state encryption.
This page is a working guide to the 2026 configuration we ship.
Licensing in one paragraph: Terraform moved from MPL 2.0 to the Business Source License (BUSL 1.1) in August 2023. IBM completed its acquisition of HashiCorp in early 2025; Terraform and HCP Terraform continue under the HashiCorp brand inside IBM Software. OpenTofu is the Linux Foundation / CNCF fork on MPL 2.0 — functionally compatible with Terraform for most AWS workflows, increasingly diverging on advanced features (Stacks, Sentinel). Pick based on license policy, HCP usage, and roadmap needs.
What’s new for Terraform on AWS in 2026
- Terraform Stacks (GA on HCP Terraform, 2025) — coordinated multi-configuration deployments across accounts and regions.
- Ephemeral values (1.10+) and ephemeral resources (1.11+) — never land in state.
- Provider-defined functions (1.8+) — cleaner HCL ergonomics.
- Test Framework maturity (1.6 → 1.10+) — production-ready module testing.
- S3-native state locking (1.10+) — DynamoDB lock table now optional.
- Provider-defined validators and richer CLI diagnostics.
- HCP Terraform — Stacks, Sentinel, OIDC to AWS, private registry, audit.
- OpenTofu 1.8+ — client-side state encryption, early provider innovations, community governance.
- AWS provider v6 — continued coverage for new AWS services (Bedrock, S3 Tables, EKS Auto Mode, VPC Lattice, Verified Access).
Why Terraform on AWS
- Reproducibility — the same module deploys identical infra in every account and region.
- Governance — Sentinel / OPA / HCP Terraform policy enforcement on every plan.
- Scalability — modules and Stacks let one platform team serve dozens of product teams.
- Drift detection — nightly plans catch console drift before auditors do.
- Ecosystem — richest provider ecosystem of any IaC tool; community modules cover most AWS services.
Account and state architecture (landing-zone first)
- AWS Organizations + Control Tower owns account creation and baseline guardrails.
- IAM Identity Center replaces long-lived IAM users; Terraform runs via OIDC with short-lived credentials.
- One AWS account per environment × workload (or per business unit × environment).
- One state key per (account, workload) in an S3 backend inside that account.
- S3 state bucket: versioning on, KMS CMK encryption, Block Public Access, TLS-only bucket policy, Object Lock where compliance mandates it.
- Locking: S3-native (Terraform 1.10+) or DynamoDB lock table.
- Cross-workspace references: Terraform Stacks, not shared state files.
Core Terraform concepts (refresher)
- Providers —
hashicorp/awsfor AWS APIs; pin with~> 6.0(or whatever current major) and upgrade deliberately. - Resources / data sources — managed vs read-only views of infra.
- Ephemeral values and resources — new in 1.10+, never persisted.
- Modules — small, focused, versioned via the registry or a private registry on HCP Terraform.
- Stacks — HCP Terraform, coordinate multiple configs.
- State — remote, encrypted, locked, never in Git.
Example: remote state with S3-native locking
terraform {
required_version = ">= 1.10"
backend "s3" {
bucket = "acme-prod-tfstate"
key = "platform/eks/terraform.tfstate"
region = "eu-west-1"
encrypt = true
kms_key_id = "arn:aws:kms:eu-west-1:111122223333:key/…"
use_lockfile = true
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}Example: ephemeral value for a bootstrap password
ephemeral "random_password" "bootstrap" {
length = 32
special = true
}
resource "aws_secretsmanager_secret_version" "initial" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = ephemeral.random_password.bootstrap.result
lifecycle {
ignore_changes = [secret_string]
}
}The generated password exists during the apply, writes into Secrets Manager, and is then gone — not in state, not in the plan, not in logs.
Example: provider-defined function
locals {
arn_parts = provider::aws::arn_parse(aws_s3_bucket.this.arn)
account_id = local.arn_parts.account_id
}No more regexes over ARNs.
Terraform Stacks (HCP Terraform)
- Define components (configurations) and deployments (targets).
- Reference components from each other with typed outputs.
- Deploy an entire environment — VPC → EKS → workloads → observability — in one orchestrated apply.
- Roll out changes across environments with deployment strategies (percentage, wave, approval gates).
- Sentinel policies gate every step.
Use when you have three or more coupled configurations deploying together. For single-state workloads, plain workspaces are still fine.
Test Framework
# tests/s3.tftest.hcl
run "s3_bucket_is_private" {
command = plan
assert {
condition = aws_s3_bucket_public_access_block.this.block_public_acls == true
error_message = "S3 bucket must block public ACLs"
}
assert {
condition = aws_s3_bucket_server_side_encryption_configuration.this.rule[0].apply_server_side_encryption_by_default.sse_algorithm == "aws:kms"
error_message = "S3 bucket must be encrypted with KMS"
}
}Run in CI on every PR. Pair with tflint, tfsec / checkov, and Sentinel (HCP) or OPA.
OpenTofu 1.8+
- Client-side state encryption — encrypt state at rest with keys OpenTofu never sends to its backend.
- Early provider work and its own registry.
- Governance: Linux Foundation / CNCF.
- Compatibility: tracks Terraform core features where licensing and design allow; Stacks and Sentinel are Terraform-only.
Adopt OpenTofu when OSI licensing is non-negotiable or you want client-side state encryption today.
Terraform vs AWS CDK vs CloudFormation (quick matrix)
| Dimension | Terraform | AWS CDK | CloudFormation / SAM | Pulumi |
|---|---|---|---|---|
| Language | HCL | TS / Python / Java / .NET / Go | YAML / JSON | TS / Python / Go / .NET |
| Scope | Multi-cloud | AWS-only (CloudFormation under the hood) | AWS-only | Multi-cloud |
| State | External (S3 / HCP) | CloudFormation | CloudFormation | External |
| Policy engine | Sentinel / OPA | Custom Aspects | CloudFormation Guard | CrossGuard / OPA |
| Drift detection | terraform plan | CloudFormation Drift | CloudFormation Drift | pulumi preview |
| Ecosystem | Largest | Growing | Native AWS coverage | Growing |
| Good fit | Enterprise multi-cloud, HCP governance | AWS-only dev teams wanting TypeScript | Minimal-tooling AWS shops | Teams wanting CDK-style with multi-cloud |
Failure modes & resilience
1. AWS provider rate limits / throttling. hashicorp/aws calls the AWS API like any other client; large apply runs hit Throttling, RequestLimitExceeded, or TooManyRequestsException on Organizations, IAM, and Lambda APIs. Configure exponential backoff via max_retries and split monolithic state files:
provider "aws" {
region = var.aws_region
max_retries = 25 # default 25; bump only for known burst-heavy workloads
default_tags {
tags = local.common_tags
}
}For Organizations / Control Tower management, throttle parallelism: terraform apply -parallelism=5 (default 10). Apply runs against AWS Organizations APIs are particularly sensitive.
2. Partial-apply rollback. Terraform does not roll back on partial failure — the failed resource is left as-is, and state reflects what succeeded. Recovery: read the error, fix the cause, re-run apply. For the high-stakes case where you cannot tolerate forward recovery, gate apply behind a taint/untaint workflow and add prevent_destroy lifecycle blocks on critical infra (RDS, KMS keys, S3 buckets with Object Lock).
3. State corruption recovery. S3 versioning is your safety net. Recovery flow: aws s3api list-object-versions --bucket <state-bucket> --prefix <key> → identify last-good version → aws s3api copy-object → re-acquire lock → terraform refresh. KMS-CMK rotation does NOT impact existing state reads (KMS retains old material for decrypt) but rotating to a new key alias requires re-encrypting state via terraform init -reconfigure.
4. Drift between console and state. Inevitable in regulated multi-team accounts. Run nightly terraform plan -detailed-exitcode in CI; non-zero exit means drift. Auto-import via terraform plan -generate-config-out= (1.5+) for known-safe drift; ticket the rest.
5. Provider major-version upgrades. hashicorp/aws v5→v6 changed default behavior on several resources (aws_s3_bucket_versioning, aws_lb defaults). Always test in a staging account; use ~> 6.0 to allow patch upgrades only.
6. Cross-region operations. aws_route53_record, ACM certs in us-east-1 for CloudFront, and aws_s3_bucket_replication_configuration need provider aliases. Forgetting an alias silently creates resources in the default region.
Multi-region rollout with Stacks
# stack.tfdeploy.hcl
deployment "us_east_1" {
inputs = { region = "us-east-1", weight = 100 }
}
deployment "eu_west_1" {
inputs = { region = "eu-west-1", weight = 100 }
depends_on = [deployment.us_east_1]
}
orchestrate "rolling" {
check {
condition = context.plan.changes.add < 50
reason = "Plan changed too many resources; manual review required."
}
}Stacks deployments run sequentially with explicit depends_on; orchestration checks gate progression. For wave-based rollouts across many regions, group deployments into waves with manual approval gates between them.
Observability runbook
CI signals:
| Signal | Action |
|---|---|
Nightly terraform plan exit code 2 (drift) | Auto-create ticket; review before next deploy; terraform import if benign |
apply duration > 30 min | Split state; check for aws_route53_* deletes (slow), ASG drains, RDS modifications |
Throttling / RateExceeded in plan/apply logs | Reduce -parallelism, bump max_retries, split state, request limit increase |
HCP Terraform run failure rate > 5% | Investigate Sentinel policy regressions, provider version skew |
State lock held > 1 hr | terraform force-unlock <lock-id> only after confirming no active apply in CI logs |
Drift alarm wiring (CloudWatch Logs → Metric Filter → Alarm):
aws logs put-metric-filter \
--log-group-name "/aws/codebuild/terraform-drift" \
--filter-name terraform-drift-detected \
--filter-pattern '"Plan: " "to add" "to change" "to destroy"' \
--metric-transformations metricName=DriftCount,metricNamespace=Terraform,metricValue=1Then alarm on Terraform/DriftCount > 0 and route to PagerDuty / Slack.
Common pitfalls (field-tested)
- Manual console changes alongside Terraform — run nightly
terraform planas drift detection; useterraform importto reconcile. - Committing state or
*.tfvars—.gitignorethem; store secrets in Secrets Manager, pull via ephemeral values. - Monolithic configurations — break into modules; giant single-state plans are slow and risky.
- Skipping plan review — require PR-level plan review; gate apply behind approval.
- Unpinned providers — pin
hashicorp/awswith a major-version constraint; upgrade deliberately. - Long-lived IAM access keys in CI — use OIDC federation instead.
When Terraform is NOT the best fit
- Pure AWS-only team already deep in CDK with CDK Pipelines and application-language colocation.
- Very small team, one environment, one workload — CloudFormation/SAM templates are lighter.
- Workloads that are 95% managed AWS services with no Kubernetes, no DNS-external, no SaaS providers — CDK or CloudFormation may be enough.
- Strict air-gapped environment with no provider registry access — evaluate Terraform Enterprise / OpenTofu + mirrored registry.
Related reading
Terraform AWS provider upgrade strategyTerraform state management on AWS: import, move, repairSafe Terraform apply workflows with approval gates on AWSMigrating from Terraform to OpenTofu on AWSTerraform vs AWS CDK: IaC decision guideAWS infrastructure drift detection with Terraform
Related services
Terraform questions
- What is Terraform Stacks and when should I adopt it?
- Terraform Stacks (GA on HCP Terraform in 2025) is a deployment model for coordinating multiple Terraform configurations as one unit — think "VPC + EKS + app + data tier across four environments and three regions" deployed through one orchestration instead of seven independently wired workspaces. It replaces a lot of the glue code teams write around `terraform_remote_state`, wrapper scripts, and CI fan-outs. Adopt it when (a) you manage more than two or three logical layers that deploy together, (b) you fan out across accounts/regions, and (c) you are already on HCP Terraform. For small teams on one workspace, plain workspaces + modules remain simpler.
- What are ephemeral values and ephemeral resources?
- Ephemeral values (Terraform 1.10+) are references that exist only during a plan or apply and never land in state — ideal for short-lived secrets (a rotating DB password fetched from Vault or Secrets Manager, a temporary token, a bootstrap password). Ephemeral resources (1.11+) extend the same idea to provider-defined data sources — they are materialized, used, and discarded in one run. Use them everywhere you were previously using `sensitive = true` + state encryption as a workaround; it is the first real fix for "my plaintext password is in `terraform.tfstate`".
- What are provider-defined functions and what do they replace?
- Provider-defined functions (Terraform 1.8+) let providers ship HCL functions alongside resources and data sources — e.g., `provider::aws::arn_parse(...)`, or parsing / formatting helpers inside the AWS / GCP / Vault providers. They replace brittle patterns like `regex(...)` gymnastics, stringly-typed helpers, and external `data` sources written just to do string manipulation. Terraform 1.9+ added `templatestring` and other native improvements; 1.10+ brought ephemeral values; 1.11+ continues the trend. Keep Terraform on a recent minor — the ergonomics wins are real.
- Is the Terraform Test Framework production-ready in 2026?
- Yes, for module-level unit and integration tests. `terraform test` (Terraform 1.6+, matured through 1.10+) is how we verify module invariants in CI: plan-only tests run fast and cover security rules (no public S3, encryption on, least privilege); apply-tests spin up ephemeral resources in a sandbox account and verify end-to-end. Combine with `tflint`, `checkov` / `tfsec`, and Sentinel or OPA for policy. The weak spots are long-running infra (databases, Kubernetes clusters) — those still benefit from dedicated Terratest/Go-based harnesses. For most everyday modules, the native framework is enough.
- Should I stay on Terraform or move to OpenTofu in 2026?
- Depends on three questions. (1) **Are you bound by an OSI-only or CNCF-only policy?** Then OpenTofu (MPL 2.0, Linux Foundation) is the pragmatic choice. OpenTofu 1.8 added client-side state encryption, 1.9 and 1.10 continued to track Terraform features where feasible. (2) **Do you rely on HCP Terraform / Sentinel / Stacks?** Stay on Terraform — Stacks in particular is not on OpenTofu's roadmap in the same form. (3) **Do you want a single provider ecosystem and minimal supply-chain friction?** Both share the same providers (OpenTofu has its own registry + ability to consume the Terraform registry) and both work with `hashicorp/aws`. Many enterprises run both: HCP Terraform for production golden-path, OpenTofu for greenfield or open-source-restricted contexts.
- What changed with the IBM + HashiCorp acquisition?
- IBM completed its acquisition of HashiCorp in early 2025. Practically, nothing in the CLI or AWS provider has meaningfully changed for users — Terraform and HCP Terraform remain under HashiCorp branding inside IBM Software. The BUSL 1.1 license on Terraform core remains in effect; the OpenTofu fork continues as the OSI-licensed alternative. IBM's Cloud Paks and Red Hat Ansible ecosystem have closer integration stories (ansible-terraform operators, HCP Terraform on Red Hat OpenShift), but the core AWS experience is unchanged. Watch the roadmap for deeper Watson / GenAI features in HCP Terraform over the next year.
- How should I organize Terraform state and AWS accounts in 2026?
- Landing zone first: AWS Control Tower + Organizations + IAM Identity Center. One AWS account per environment (dev/test/stage/prod) per workload, or per business unit + environment — not "one AWS account for everything". One Terraform state key per (account, workload); remote state in an S3 bucket inside that account with KMS CMK encryption, Block Public Access, Object Lock where required, and S3-native locking (Terraform 1.10+) or a DynamoDB table on older versions. Never share a state file across accounts; use `terraform_remote_state` data sources or — better — Terraform Stacks for cross-workspace references.
- Terraform vs AWS CDK vs CloudFormation in 2026 — which should I pick?
- Short version. **Terraform**: pick for multi-cloud, large existing module ecosystem, HCP Terraform policy/governance, and teams comfortable with HCL. **AWS CDK**: pick for AWS-only shops that want to write infra in TypeScript/Python, for tight integration with AWS-managed constructs, and where you want to share libraries with application code. CloudFormation under the hood. **CloudFormation / SAM**: pick for AWS-only shops that want the lightest-weight tooling, native drift detection, and ChangeSets. **Pulumi** is a fourth option if you want a CDK-like programming model with multi-cloud. Most of our enterprise customers converge on Terraform for landing-zone and cross-workload infra, with selective CDK for highly AWS-native services (Lambda packaging, CDK Pipelines, Amplify).
- How do I handle secrets in Terraform on AWS?
- Three rules. (1) Never hard-code secrets in HCL. (2) Source short-lived secrets with ephemeral values — pull from AWS Secrets Manager, HashiCorp Vault, or Parameter Store at plan/apply time, and let them expire after the run. (3) For long-lived generated secrets (initial DB password, KMS-wrapped values), generate inside AWS (`aws_secretsmanager_secret_version` with `random_password`) and store only an ARN reference in state. Combine with S3 state encryption via KMS CMK and OpenTofu 1.8+ client-side state encryption if your threat model requires zero-plaintext-secrets-at-rest.
Connect
Our AWS-certified engineers design, build and run Terraform on AWS, or review what you already have.
Pictures:L.M.Ericsson Manual telephone exchange circa 1910 Elisa Telephone Museum, Antti Leppänen (CC BY 4.0); Siemens & Halske telephone switchboard Elisa Telephone Museum, Antti Leppänen (CC BY 4.0); Telephone switchboard 6 Oct 1907 Salt Lake City, The Salt Lake Herald (public domain); Telephone operators, Japan, 1902 (public domain); Chinese Telephone Exchange, San Francisco, California, 1918 (public domain); WAC telephone operators at the Victory switchboard, Potsdam Conference, 1945 (public domain); American telephone practice (1905), Kempster B. Miller (no known copyright restrictions). Via Wikimedia Commons.
