Infrastructure as Code on AWS: Terraform vs CloudFormation vs CDK


Introduction: Why Infrastructure as Code Matters
Meet Nebula, a fast-growing startup. At first, they built everything by hand in the AWS console: a VPC here, a couple of subnets there, an EC2 instance for their app, and an S3 bucket for storage. It worked fine — until they needed to replicate the setup for staging, testing, and production.
Suddenly, chaos.
One engineer forgot a security group rule, another set up IAM roles differently, and no one could guarantee that environments were identical. Moving to another AWS region? Even harder.
This is the moment Nebula realized: they needed Infrastructure as Code (IaC).
By describing infrastructure as text, you get:
- Consistency: every environment (dev, staging, prod) looks the same.
- Version control: track changes just like application code.
- Automation: no more endless clicking in the console.
- Speed: spin up or tear down environments in minutes.
On AWS, three tools dominate the IaC landscape: Terraform, CloudFormation, and the AWS Cloud Development Kit (CDK). Each has strengths and trade-offs. Let’s see how Nebula’s team explored them.
What is Infrastructure as Code?
IaC allows you to describe complex infrastructure with text files (declarative or imperative), which can be version-controlled and automated. This eliminates manual configuration through the console, reduces human error, and improves collaboration across teams.
Terraform
Nebula’s engineers first tried Terraform, built by HashiCorp. They liked that it wasn’t tied to AWS — it supports Azure, GCP, Kubernetes, and dozens of other providers.
Pros:
- Multi-cloud and provider-agnostic
- Large community and plenty of reusable modules (Terraform Registry)
- Simple, readable HCL syntax
- Mature workflow (plan → apply → destroy)
Cons:
- External tool, not AWS-native
- State file (.tfstate) management can be tricky without a remote backend (S3 + DynamoDB)
- Debugging errors isn’t always straightforward
Example: Create an S3 bucket with Terraform
resource "aws_s3_bucket" "example" {
bucket = "my-terraform-bucket"
acl = "private"
}
A few lines of code and your bucket is ready. Run terraform plan
to see what will change, then terraform apply
to deploy.
Best for: teams working in multi-cloud setups or those who want maximum flexibility beyond AWS-native solutions.
CloudFormation
Next, Nebula explored AWS CloudFormation, the native choice. The team loved how tightly it integrates with IAM, CloudWatch, and CodePipeline. And AWS handles state behind the scenes.
Pros:
- Native and always up-to-date with new AWS services
- No state file management (handled by AWS)
- Tight integration with AWS services (IAM, CloudWatch, CodePipeline)
- StackSets for managing resources across multiple accounts/regions
Cons:
- Verbose: YAML/JSON templates get long and hard to maintain
- AWS-only (no multi-cloud support)
- Deployment error messages can be difficult to interpret
Example: Create an S3 bucket with CloudFormation
Resources:
MyS3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: my-cfn-bucket
Compared to Terraform, it’s more verbose, but it’s deeply integrated into AWS (e.g., with StackSets you can deploy across accounts and regions easily).
Best for: teams working exclusively on AWS with a need for tight integration and compliance.
AWS CDK
Finally, Nebula’s developers tried the AWS Cloud Development Kit (CDK). They loved writing infrastructure with real programming languages (TypeScript, Python, Java, C#). It felt like “real code,” with loops, conditions, and abstractions.
Pros:
- True “infrastructure as code” with loops, conditions, and functions
- Ability to reuse constructs and libraries (define patterns and replicate them)
- Ideal for developers already comfortable with programming languages
- Maintains compatibility with CloudFormation
Cons:
- Steeper learning curve for DevOps engineers less comfortable with coding
- Relatively new; smaller community compared to Terraform
- Debugging can be challenging if you’re not familiar with CloudFormation
Example: Create an S3 bucket with CDK (TypeScript)
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
export class MyStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new s3.Bucket(this, 'MyCdkBucket', {
bucketName: 'my-cdk-bucket',
versioned: true
});
}
}
Instead of static templates, you can use loops, conditions, and reusable constructs. Under the hood, CDK compiles this into CloudFormation.
Best for: developer-heavy teams who want a modular and programmable approach to AWS infrastructure.
Quick Comparison
Feature | Terraform | CloudFormation | AWS CDK |
---|---|---|---|
Multi-cloud | Yes | No | No |
Syntax | HCL (declarative) |
YAML / JSON (declarative) |
TypeScript Python Java (imperative) |
State management | Manual (S3, DynamoDB) |
Managed by AWS |
Managed by AWS |
Community | Large | AWS-focused | Growing |
Readability | Good | Poor (verbose) |
Depends on language |
Best fit | Multi-cloud & DevOps |
AWS-only enterprises |
Developer-heavy AWS teams |
Real-World Scenarios
- Startup going multi-cloud: Terraform is ideal — one tool across providers.
- Enterprise with strict compliance: CloudFormation wins — AWS-native, tightly controlled.
- Developer-heavy product team: CDK fits best — infrastructure and app code in the same repo.
At Nebula, the team even experimented with a hybrid approach: Terraform for cross-cloud resources, CDK for developer-focused services.
Practical Tips Before You Choose
- Start small: try an S3 bucket with all three tools.
- Use version control: Git + PRs = safer infrastructure.
- Plan for state: Terraform needs remote state, CloudFormation/CDK don’t.
- Mix carefully: hybrid approaches can work, but avoid overlapping chaos.
Conclusion: Which One Should You Choose?
It depends on your context:
- Terraform → multi-cloud flexibility.
- CloudFormation → AWS-only compliance and enterprise scale.
- CDK → developer-centric, modular AWS teams.
Nebula’s story teaches us there’s no single winner. Many organizations use more than one tool side by side. What matters most is:
- Stay consistent
- Version your infrastructure
- Follow DevOps best practices
That’s how IaC scales with your business.