
Infrastructure as Code on AWS: CloudFormation, CDK and Terraform Explained
Stop clicking through the console. Learn what Infrastructure as Code is, how AWS CloudFormation and CDK approach it, and how Terraform works, with a hands-on S3 example and tips for choosing the right tool.
The problem with "click-ops"
Most of us start on AWS by clicking through the console: create a VPC, launch an EC2 instance, attach a security group. It works, until you need to rebuild the same setup for a test environment, or someone asks "who changed that security group?" and nobody knows.
Infrastructure as Code (IaC) solves this by describing your infrastructure in files instead of clicks.
What is Infrastructure as Code?
Infrastructure as Code is the practice of defining and managing cloud resources (networks, servers, databases, permissions) through code that can be versioned, reviewed, tested and reused.
š· Insert image: iac-workflow.png
Why teams adopt it:
- Repeatable: the same code builds identical dev, test and prod environments.
- Version controlled: every change lives in Git, with history, pull requests and easy rollback.
- Faster: a full environment comes up in minutes, not an afternoon of clicking.
- Less human error: no forgotten settings or "it worked on my account" surprises.
- Documented by default: the code is the documentation of what you run.
Declarative vs imperative
- Declarative: you describe the end state ("I want one S3 bucket with versioning on") and the tool works out the steps. CloudFormation and Terraform work this way.
- Imperative: you write the steps yourself, such as a shell script calling the AWS CLI.
Declarative tools are also idempotent: running them twice doesn't create duplicates, it only fixes differences.
IaC on AWS: native options
AWS CloudFormation
CloudFormation is AWS's built-in IaC service. You write a template in YAML or JSON, and CloudFormation creates the resources as a stack.
- Change sets preview what will change before you apply it.
- Drift detection shows resources changed outside the template.
- Automatic rollback if a deployment fails.
- AWS manages the state for you, so there is no state file to store.
1
2
3
4
5
6
Resources:
DemoBucket:
Type: AWS::S3::Bucket
Properties:
VersioningConfiguration:
Status: EnabledAWS CDK (Cloud Development Kit)
The CDK lets you define infrastructure in languages like TypeScript, Python, Java, C# and Go. Your code is synthesized into a CloudFormation template and deployed through CloudFormation. It's a good fit if your team prefers real programming languages, with loops, functions and classes.
What is Terraform?
Terraform is an open-source IaC tool from HashiCorp. You write configuration in HCL (HashiCorp Configuration Language), and Terraform talks to cloud APIs through plugins called providers. Because providers exist for AWS, Azure, Google Cloud, Kubernetes, GitHub and many more, one tool and one workflow can manage all of them.
Core concepts
| Concept | What it means |
|---|---|
| Provider | Plugin that talks to a platform's API (e.g. hashicorp/aws) |
| Resource | A piece of infrastructure to create (e.g. aws_s3_bucket) |
| Variable / Output | Inputs to your config / values it exposes after apply |
| Data source | Read-only lookup of existing resources |
| Module | A reusable bundle of resources |
| State | Terraform's record of what it has created |
The Terraform workflow
š· Insert image: terraform-workflow.png
terraform initdownloads the providers and prepares the backend.terraform plancompares your code with the real world and previews changes.terraform applymakes the changes and updates the state.terraform destroyremoves everything it manages. Great for cleaning up labs.
Hands-on: an S3 bucket with Terraform
Create a file called
main.tf:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "demo" {
bucket = "my-iac-demo-bucket-12345" # must be globally unique
tags = {
Project = "iac-demo"
ManagedBy = "terraform"
}
}
resource "aws_s3_bucket_versioning" "demo" {
bucket = aws_s3_bucket.demo.id
versioning_configuration {
status = "Enabled"
}
}
output "bucket_name" {
value = aws_s3_bucket.demo.bucket
}Make sure your AWS credentials are configured (for example with
aws configure or IAM Identity Center), then run:1
2
3
terraform init
terraform plan
terraform applyThe plan will end with something like
Plan: 2 to add, 0 to change, 0 to destroy. Type yes and your bucket exists. When you're done experimenting:1
terraform destroyš· Optional: add a screenshot of your own
terraform plan / apply output here. It makes the article more authentic.Don't skip: remote state
By default Terraform stores state in a local
terraform.tfstate file. That's fine for learning, but for teams you should store it remotely, in an S3 bucket with encryption and locking, so it's shared and protected from simultaneous runs:1
2
3
4
5
6
7
8
9
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "demo/terraform.tfstate"
region = "us-east-1"
encrypt = true
use_lockfile = true
}
}Older setups used a DynamoDB table for locking, so you'll still see that in many tutorials. Check the Terraform docs for the version you use. Never commit state files to Git, since they can contain sensitive values.
CloudFormation vs CDK vs Terraform
| CloudFormation | AWS CDK | Terraform | |
|---|---|---|---|
| Language | YAML / JSON | TypeScript, Python, Java, C#, Go | HCL |
| Cloud support | AWS only | AWS only | Multi-cloud |
| State | Managed by AWS | Managed by AWS (via CloudFormation) | You manage it (e.g. S3) |
| Preview changes | Change sets | cdk diff | terraform plan |
| Best for | AWS-only teams wanting zero setup | Developers who prefer real code | Multi-cloud or portable skills |
There's no single winner. If you're all-in on AWS, CloudFormation or CDK keep everything native. If you work across clouds, or want one skill that transfers, Terraform is a strong choice.
IaC best practices
- Keep everything in Git and use pull requests for infrastructure changes.
- Use remote state with encryption and locking.
- Build modules instead of copy-pasting code.
- Separate environments (dev / staging / prod), with separate state.
- Follow least privilege for the IAM role your pipeline uses.
- Never hardcode secrets. Use AWS Secrets Manager or SSM Parameter Store.
- Run
planautomatically in CI/CD and require review beforeapply. - Tag every resource so you can track ownership and cost.
- Scan code with tools like
cfn-lint,tfsecor Checkov before deploying.
Wrapping up
Infrastructure as Code turns cloud setup from a manual chore into something you can review, repeat and trust. Start small: pick one resource you normally create by hand, write it as code, and destroy and recreate it a couple of times. That's when it clicks.
Are you using CloudFormation, CDK or Terraform? Let me know in the comments, and what you'd like to see next, maybe a VPC or EC2 example or a CI/CD pipeline for Terraform. š
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article