Infrastructure as Code in AWS: Terraform vs CloudFormation vs CDK

Infrastructure as Code (IaC) means defining your AWS resources in files rather than clicking through the console. Those files live in version control, get reviewed in pull requests, and can recreate your entire environment exactly on demand. This page explains what IaC is, how it works in AWS, and how to choose between Terraform, CloudFormation, and CDK.

What is Infrastructure as Code, in plain English

Before IaC, engineers built cloud environments by clicking through the AWS console: creating a VPC here, adding a security group there, tweaking settings by hand. It worked, but nobody wrote down exactly which settings were chosen or why. When something broke, rebuilding it meant doing it all again from memory.

IaC flips this. Instead of clicking, you write a file that describes what you want: “I need a VPC with two subnets, an S3 bucket with versioning enabled, and an IAM role that can read from it.” You commit that file to git, open a pull request, get it reviewed, and merge. A pipeline applies it. If the environment ever needs to be rebuilt, you run one command.

The files are the infrastructure. The console is just a read-only view.

Think of it like a recipe. If you cook by improvising, you might get a great result, but you cannot recreate it exactly, and you cannot hand it to someone else. IaC is writing the recipe down. Any engineer (or CI/CD pipeline) can follow it and produce the same environment every time.

Why teams use Infrastructure as Code in AWS

IaC is not just a best practice label. Each benefit solves a real operational problem.

  • Consistency. The same code runs in dev, staging, and production. There is no “it works in dev but breaks in prod” because of a missing environment variable or a different security group rule.
  • Repeatability. Need a new staging environment for a feature branch? Run one command. Need to rebuild production from scratch after a disaster? Run one command. Environments become disposable.
  • Reviewability. Every infrastructure change goes through a pull request. Teammates see what will change before it happens. Security reviews become part of the normal development workflow.
  • Faster recovery. Manual rebuilds from memory can take hours or days. With IaC, the environment rebuilds from a known-good state. Recovery time drops to minutes.
  • Safer change management. IaC tools show you a plan: what will be created, modified, or deleted, before making any changes. You see that a database will be destroyed before it happens, not after.

How Infrastructure as Code works in AWS

The same workflow applies whether you use Terraform, CloudFormation, or CDK. Here is how a change moves from idea to deployed infrastructure.

  1. Write the desired state. You edit a configuration file to describe what you want: “I want an S3 bucket named my-project-assets with versioning enabled.” This is the desired state — what should exist, not the steps to create it.
  2. Commit to version control. The file goes into git like any other code change. Every change has an author, a timestamp, and a commit message explaining why it was made.
  3. Run a plan or preview. Before anything is touched, the tool compares your desired state against what currently exists in AWS and shows you the diff: resources to create, modify, or delete. A -/+ on a database means it will be deleted and recreated empty. Read this carefully.
  4. Review in a pull request. The plan output goes into the PR. Teammates and security reviewers see the exact changes. Tools like policy as code can block the PR automatically if it violates security rules.
  5. Merge and apply. When the PR is approved and merged, the CI/CD pipeline applies the changes. The tool creates or modifies resources in AWS to match the desired state.
  6. Drift detection. Over time, manual console changes can make real infrastructure diverge from the code. IaC tools detect this by comparing current resource state against what the code describes. The next apply will overwrite manual changes.
  7. State management. Terraform and CloudFormation track state: a record of every resource they have created. This is how they calculate what needs to change on the next run. Storing Terraform state in S3 with DynamoDB locking is essential once more than one person runs Terraform on the same environment.
# The core Terraform workflow
terraform init          # Download providers and modules
terraform plan          # Preview changes — read this carefully
terraform apply         # Apply the changes

The plan step is the most important part of the workflow. It shows every resource that will be created, modified, or destroyed before anything is touched. Skipping the plan and running apply directly is how accidental production deletions happen.

What problems IaC actually solves

Manual console workflows create specific problems that grow worse as teams and environments scale.

Manual config drift. When someone clicks through the console to add a security group rule, nobody records every setting or why it was added. Three months later, nobody remembers, and the configuration exists only as running state. IaC files are self-documenting. The configuration is the documentation.

Environment inconsistency. Dev environments built by hand rarely match production exactly. A missing environment variable, a different instance type, a security group rule that exists in prod but not in staging — these differences cause bugs that only appear in production. IaC uses the same code for every environment, with variable overrides for things like instance size. The dev vs staging vs production guide covers how to structure this cleanly.

Slow recovery. If production goes down and needs to be rebuilt from scratch, doing it manually can take hours. With IaC, you run one command and the environment rebuilds from a known-good state.

Auditability gaps. Compliance frameworks like SOC 2 and PCI DSS require you to know who changed what and when. Console changes are logged in CloudTrail, but the intent behind them is not. IaC changes are tracked in git: every change has a commit message, an author, and a pull request showing who reviewed and approved it.

Team scaling issues. When one engineer knows how an environment is configured and they leave, that knowledge leaves with them. IaC means the configuration is in the repo, not in someone’s head or in their local scripts.

Infrastructure as Code tools in AWS

Three tools dominate IaC for AWS. A fourth (Pulumi) is worth knowing exists, but is less common.

ToolLanguageAWS-only or multi-cloudBest forMain trade-off
TerraformHCLMulti-cloudTeams using multiple cloud providers or wanting the largest module ecosystemState file requires careful management; BSL license change in 2023
CloudFormationJSON or YAMLAWS-onlyAWS-only environments, no external tooling, AWS-managed stateVerbose templates; debugging failed stacks can be tedious
AWS CDKTypeScript, Python, Java, GoAWS-onlyTeams who prefer a general-purpose language and want powerful abstractionsSteeper learning curve; compiles to CloudFormation, so CloudFormation limits apply
PulumiTypeScript, Python, Go, C#Multi-cloudTeams who want Terraform-like behavior but in a real programming languageSmaller community and module ecosystem than Terraform

Terraform

Terraform is the most widely used IaC tool across the industry. It uses HCL (HashiCorp Configuration Language), which is readable and designed specifically for describing infrastructure. Terraform works with AWS, GCP, Azure, and hundreds of other providers through a plugin system, making it the default choice for multi-cloud teams.

Terraform tracks state: a record of every resource it has created, used to calculate what needs to change on the next run. Managing that state safely in teams means storing it remotely. The Terraform state management guide covers S3 and DynamoDB backends in detail.

For a hands-on walkthrough, the Terraform for AWS getting started guide covers installation, authentication, and your first resource. For team setups, Terraform project structure covers how to organize files beyond a single main.tf.

AWS CloudFormation

CloudFormation is AWS’s native IaC service. You write templates in JSON or YAML that describe your AWS resources, and CloudFormation creates and manages them as a stack. Because it is an AWS service, it supports every AWS resource on day one. There is no waiting for a community provider to add support for a new AWS feature.

CloudFormation manages its own state, so there is no external state file to store or protect. Stacks can be nested for modularity. The downside is that CloudFormation templates can become very verbose at scale, and debugging failed deployments means reading through event logs that do not always surface the root cause cleanly.

AWS CDK

The AWS Cloud Development Kit lets you write infrastructure in TypeScript, Python, Java, or Go. Under the hood, CDK generates CloudFormation templates, so it runs on top of CloudFormation and inherits its state management. The advantage is the full power of a programming language: loops, conditionals, type checking, and reusable constructs (CDK’s equivalent of Terraform modules).

CDK is a strong fit for teams already writing application code in TypeScript or Python who want to share language tooling between application and infrastructure code.

Terraform vs CloudFormation vs CDK: who should choose what

The right tool depends on your team’s situation more than on technical merit. Here is how to think through the decision.

If you…ConsiderBecause
Are AWS-only and want the simplest setupCloudFormationNo state file to manage, no extra tooling, deep AWS integration
Use or plan to use multiple cloud providersTerraformSingle tool and workflow across AWS, GCP, Azure
Have a team already comfortable with TerraformTerraformThe team already knows it. Switching costs are real.
Want maximum module reuse and community supportTerraformTerraform Registry has thousands of maintained modules
Prefer writing infrastructure in TypeScript or PythonCDKFull programming language, type safety, reusable constructs
Are brand new to IaC with no team preference yetTerraformMost learning resources, widest job market, multi-cloud transferability

Choose Terraform if your team already uses it, you need multi-cloud support, or you want access to the Terraform Registry module ecosystem. Plan for remote state from day one.

Choose CloudFormation if you are AWS-only and want the simplest possible setup: no external tools, no state file, AWS-native everything. The tooling is less ergonomic than Terraform, but the operational overhead is lower.

Choose CDK if your team strongly prefers programming languages over configuration languages and you are AWS-only. The abstractions are powerful, but expect a steeper learning curve, and CloudFormation limits still apply underneath.

Do not manage the same resources with multiple tools. If you manage a VPC with Terraform, do not also manage it with CloudFormation. Overlapping tools cause state conflicts and make drift detection unreliable. Splitting by layer (Terraform for networking, CloudFormation for application stacks) is acceptable. Managing the same resource with both is not.

Manual console vs Infrastructure as Code

The AWS console is not the enemy. It is useful for exploration, debugging, and one-off tasks. The problem is using it to manage production infrastructure that should be stable, auditable, and repeatable.

Manual consoleInfrastructure as Code
RepeatabilityManual, different every timeIdentical, same code produces same result
Audit trailCloudTrail API calls, no intent capturedGit history with author, message, and PR review
Code reviewNonePull request with automated policy checks
Disaster recoveryRebuild from memoryRerun the pipeline
Environment consistencyVaries, manual drift is inevitableConsistent, same code for all environments
Best forExploration, debugging, one-off tasksAll managed, long-lived infrastructure

Console use is fine for exploration and learning. It becomes a liability when someone makes a console change to a managed environment and does not update the IaC code. That change will be overwritten on the next apply, or create drift that confuses future engineers.

📄

Drift is like editing a printed document by hand. You have a Word file (your IaC code) and a printed copy (your running infrastructure). If someone scribbles changes on the printout, the Word file is now out of date. The next time you print, the hand-written changes disappear. Console changes work the same way: the code wins on the next apply.

When to use Infrastructure as Code

IaC is the right choice in these situations.

  • Small team starting AWS seriously. IaC from the beginning is much easier than migrating an existing manually-built environment. Start as you mean to go on.
  • Multi-environment setup. If you need dev, staging, and production to behave consistently, IaC with variable overrides is the cleanest way to manage the differences. The dev vs staging vs production guide covers how to structure this.
  • Compliance and review needs. SOC 2, PCI DSS, and similar frameworks require documented change management. IaC gives you that for free through git history and pull request reviews.
  • Reusable environments. Feature branches, ephemeral test environments, or cloned staging environments all become simple when infrastructure is code.
  • CI/CD-driven infrastructure changes. Pairing IaC with a CI/CD pipeline means infrastructure changes go through the same review and automation process as application code. Tools like policy as code can automatically block PRs that violate security rules.

A concrete example: S3 bucket with IAM policy via Terraform

Here is a minimal but realistic example: creating an S3 bucket, an IAM policy that allows reading from it, and separating the environments using a variable.

# main.tf

variable "environment" {
  description = "Deployment environment (dev, staging, prod)"
  type        = string
}

resource "aws_s3_bucket" "assets" {
  bucket = "my-project-assets-${var.environment}"

  tags = {
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

resource "aws_s3_bucket_versioning" "assets" {
  bucket = aws_s3_bucket.assets.id

  versioning_configuration {
    status = "Enabled"
  }
}

data "aws_iam_policy_document" "read_assets" {
  statement {
    actions   = ["s3:GetObject", "s3:ListBucket"]
    resources = [
      aws_s3_bucket.assets.arn,
      "${aws_s3_bucket.assets.arn}/*"
    ]
  }
}

resource "aws_iam_policy" "read_assets" {
  name   = "read-assets-${var.environment}"
  policy = data.aws_iam_policy_document.read_assets.json
}

Running terraform apply -var=“environment=dev” creates the dev environment. Running it with environment=prod creates an identical production environment with different names. Same code, same structure, different variable values.

Managing IAM resources in Terraform — policies, roles, attachments — is covered in detail in the managing IAM with Terraform guide.

This example does not store any secrets in the code. Database passwords and API keys belong in AWS Secrets Manager, referenced by ARN from the IaC code.

Common beginner mistakes

  1. Making console changes after adopting IaC. Any manual change to a managed environment will be overwritten on the next apply, or create drift that confuses future plans. Once you adopt IaC for an environment, all changes go through the code. Console use belongs only in the initial exploration phase.
  2. Skipping the plan review. The plan output shows every resource that will be created, modified, or destroyed. Running terraform apply without reading the plan is how accidental production database deletions happen. Always read the full plan output before applying.
  3. Using one environment for everything. Starting with IaC directly against production is risky. Build a dev environment first, validate there, then promote to production. The same code should work in both with different variable values.
  4. Storing secrets in IaC files or state. Database passwords, API keys, and TLS private keys should never appear in Terraform or CloudFormation files. Terraform state also stores resource attributes, some of which may include sensitive values. Use AWS Secrets Manager and reference the secret ARN. The secrets in CI/CD pipelines guide covers this in practice.
  5. Mixing tools on the same resources. Managing the same VPC with both Terraform and CloudFormation causes state conflicts and makes drift detection unreliable. Pick one tool per resource boundary and stick to it.
  6. Using long-lived AWS access keys in CI/CD. Storing access keys as CI/CD secrets is a common starting point, but long-lived access keys are a security risk. Use OIDC role assumption in GitHub Actions or CodeBuild instead: no stored credentials, short-lived tokens only. The secure CI/CD pipelines guide covers OIDC setup.

Frequently asked questions

What is the difference between Terraform and CloudFormation?

Terraform is a multi-cloud tool that uses HCL and has a large community module ecosystem. CloudFormation is AWS-native, integrates tightly with every AWS service on day one, and requires no external tooling or state file management. Terraform is generally preferred for teams working across multiple cloud providers. CloudFormation is a solid choice for AWS-only environments that want the simplest possible operational setup.

Is AWS CDK still Infrastructure as Code?

Yes. CDK lets you write infrastructure in TypeScript, Python, Java, or Go, but it compiles down to CloudFormation templates before deploying. You get the full power of a programming language, including loops, conditionals, and type checking, while still using CloudFormation as the deployment engine underneath. The result is version-controlled, reviewable, repeatable infrastructure, which is the definition of IaC.

Which IaC tool should beginners learn first in AWS?

Terraform is a strong first choice because it has the largest community, the most learning resources, and skills that transfer to GCP and Azure. CloudFormation is worth learning if you are AWS-only and want to understand the native tooling. CDK is best approached after you are comfortable with either Terraform or CloudFormation and understand the underlying infrastructure concepts.

Can you mix Terraform and CloudFormation?

You can use both in the same AWS account, but you should not manage the same resource with both tools. Using Terraform to create a VPC and CloudFormation to deploy applications into it is a reasonable split. Managing the same VPC with both tools simultaneously causes state conflicts and makes drift detection unreliable.

What is drift in Infrastructure as Code?

Drift happens when the real state of your infrastructure no longer matches what your IaC code describes. The most common cause is a manual change made in the AWS console without updating the code. IaC tools detect drift by comparing deployed resources against the desired state and flagging differences. The next apply will overwrite manual changes.

Last verified: 25 April 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.