Infrastructure as Code on AWS: Terraform vs CloudFormation for SMBs
The question is not which tool is technically superior. The question is which tool your team will actually use consistently, and which one creates less operational debt over a three-to-five year horizon.
Both Terraform and CloudFormation can manage AWS infrastructure competently. The differences are in workflow, state management, multi-cloud scope, and ecosystem. Here is a direct comparison.
What Each Tool Is
AWS CloudFormation is AWS's native IaC service. You write templates in YAML or JSON describing the AWS resources you want, and CloudFormation creates, updates, and deletes them as stacks. Because it is an AWS service, it has first-class support for every AWS resource, often on the day of launch. State is managed entirely by AWS.
Terraform is HashiCorp's open-source IaC tool. You write configuration in HCL (HashiCorp Configuration Language) and Terraform manages a state file that tracks the current state of your infrastructure. Terraform supports AWS via the AWS provider, which the community and AWS maintain jointly. It also supports Azure, GCP, Kubernetes, and hundreds of other providers with the same tooling.
Both are declarative: you describe the desired state of your infrastructure, and the tool works out what actions to take to reach it.
CloudFormation: Where It Wins
Native AWS integration. CloudFormation supports every AWS service, often before third-party providers have published Terraform support. If you need a new AWS service the week it launches, CloudFormation will have it. The Terraform AWS provider typically catches up within weeks to months, but the lag exists.
No state management overhead. CloudFormation state is held and managed by AWS. There is no state file to store, lock, back up, or recover. For small teams, this is a genuine operational simplification.
AWS CDK. The AWS Cloud Development Kit lets you write infrastructure in TypeScript, Python, Java, or Go, and synthesises CloudFormation templates. If your team is more comfortable in a general-purpose programming language than in HCL or YAML, CDK is compelling. You get type checking, abstraction, and reuse without learning a new language.
IAM integration. CloudFormation integrates with AWS IAM natively. Stack-level permissions, service roles, and drift detection are all built in.
Cost. CloudFormation itself is free. You pay only for the AWS resources it creates. Terraform is also free as open source, but Terraform Cloud (HashiCorp's managed service for state and runs) has a paid tier once you exceed the free allowance.
Terraform: Where It Wins
Multi-cloud and multi-provider scope. If your infrastructure spans AWS and Azure, or AWS and Kubernetes, Terraform manages everything in one tool with one workflow. This is the strongest argument for Terraform for businesses running a hybrid cloud estate.
HCL readability. HCL is more concise and readable than CloudFormation YAML for most engineers. Complex CloudFormation templates can become deeply nested and hard to reason about. Terraform modules tend to stay flatter and more explicit.
Plan before apply. terraform plan shows you exactly what changes will be made before you apply them. It is a dry run with a diff. CloudFormation has Change Sets, which provide similar functionality, but the Terraform plan workflow is faster and more integrated into the typical development loop.
Ecosystem and community. The Terraform Registry has a large library of community modules for common infrastructure patterns. The community around Terraform tooling (Terragrunt, Atlantis, Infracost, Checkov) is mature. If you are hiring engineers, Terraform familiarity is widespread.
Explicit state management gives you control. The state file is a liability if managed badly, but it is also an asset. You can inspect it, manipulate it to import existing resources, and run targeted applies against specific resources. CloudFormation's managed state is simpler but less flexible.
The CDK Option
AWS CDK deserves separate mention because it changes the comparison somewhat. If your team already writes TypeScript or Python and finds YAML-based IaC painful, CDK offers:
- Infrastructure written in a proper programming language, with types, loops, and abstractions
- CloudFormation as the output, so you retain all CloudFormation benefits
- The
cdk diffcommand, which gives you a change preview similar toterraform plan - AWS Construct Library: high-level abstractions (L2 and L3 constructs) that handle common patterns with sensible defaults
CDK is not the same as CloudFormation authoring. You write less, and what you write is easier to test. The trade-off is that the synthesised CloudFormation templates are verbose and hard to read, which can make debugging CloudFormation errors less intuitive.
Making the Decision
Choose CloudFormation (or CDK) if:
- Your infrastructure is entirely AWS, with no plans to add other cloud providers
- You want to minimise operational overhead: no state file to manage
- Your team is already proficient in TypeScript or Python and wants to use CDK
- You want the fastest new-service support without waiting on provider updates
Choose Terraform if:
- You run or plan to run a multi-cloud environment
- You are hiring engineers who already know Terraform (saves ramp-up time)
- You want the richer open-source tooling ecosystem (Atlantis for PR-based runs, Infracost for cost estimation, Checkov for policy-as-code)
- You find HCL more readable than YAML for your team
The answer that is usually wrong: mixing both tools for the same AWS environment. Managing some resources in CloudFormation and some in Terraform creates two state sources, two sets of dependencies to reason about, and two sets of tooling to maintain. Pick one and migrate the other.
State Management for Terraform on AWS
If you choose Terraform, state management is the first infrastructure problem to solve, not an afterthought.
Standard pattern for Terraform state on AWS:
- Create an S3 bucket for state storage with versioning enabled and public access blocked
- Create a DynamoDB table for state locking (prevents concurrent applies corrupting the state file)
- Configure your Terraform backend:
```hcl
terraform {
backend "s3" {
bucket = "your-terraform-state-bucket"
key = "env/production/terraform.tfstate"
region = "eu-west-2"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
```
- Apply least-privilege IAM policies to the role that runs Terraform applies
- Enable S3 bucket logging to track state file access
Do not store state files in version control. They contain sensitive resource metadata including identifiers that should not be committed.
Structuring Your IaC Repository
Regardless of tool, structure matters more than syntax. A common pattern for AWS environments:
```
infrastructure/
modules/
vpc/
ecs-cluster/
rds/
environments/
dev/
staging/
production/
```
Modules contain reusable infrastructure patterns. Environments compose modules with environment-specific variables. This separation means changing the VPC module updates all environments consistently, and environment-specific configuration (instance sizes, replica counts) is isolated.
Keep modules small and focused. A monolithic "all the infrastructure" module is hard to test, hard to understand, and hard to apply partially.
Policy as Code
IaC gives you the ability to enforce standards through code rather than through human review. Tools worth knowing:
AWS CloudFormation Guard (cfn-guard): Rule-based validation for CloudFormation templates. Define policies (encryption must be enabled, public access must be blocked) and fail the pipeline if templates violate them.
Checkov: Open-source static analysis for Terraform, CloudFormation, and CDK. Runs in CI to catch misconfigurations before deployment.
AWS Config: Runtime compliance checking against deployed resources. Catches drift between what IaC says should exist and what actually exists.
Running policy checks in your CI pipeline (before terraform apply or cfn deploy) is more valuable than running them after. Catching a missing encryption setting at PR review costs nothing to fix. Catching it after deployment costs a change window.
Where Critical Cloud Comes In
Getting IaC right from the start, with state management, policy enforcement, and CI integration in place before the environment grows complex, is significantly easier than retrofitting it. Critical Cloud manages AWS infrastructure for tech-led businesses, with infrastructure-as-code and automated deployment pipelines integrated into every engagement. If you want AWS done properly from day one, see how Critical Support works.