Intermediate
Infrastructure as Code
Deploy and manage AWS infrastructure using CloudFormation or Terraform
Project Overview
Define your entire infrastructure in code, enabling version control, reproducibility, and automated deployments. A critical skill for any cloud architect.
Prerequisites
- Familiarity with AWS services (VPC, EC2, etc.)
- YAML or JSON knowledge
- AWS CLI installed and configured
- Basic programming knowledge for CDK (optional)
Choose Your Tool
CloudFormation
AWS Native
Terraform
Multi-Cloud
CDK
Code-First
↓
Template
YAML/HCL
Deploy
Stack/Apply
AWS Resources
VPC, EC2, RDS...
Version control your infrastructure like application code
Step-by-Step Instructions
1
Start with a Simple Template
- Create a template that deploys a single S3 bucket
- Define the AWSTemplateFormatVersion and Description
- Add a Resources section with your S3 bucket
- Deploy using AWS Console or CLI
- Verify the resource was created
2
Add Parameters and Outputs
- Add Parameters section for customizable values
- Use parameter types (String, Number, AWS::SSM::Parameter::Value)
- Define Outputs to expose resource IDs and ARNs
- Export outputs for cross-stack references
3
Build a VPC Template
- Define VPC with CIDR block
- Create public and private subnets
- Add Internet Gateway and NAT Gateway
- Configure route tables
- Use intrinsic functions: !Ref, !Sub, !GetAtt
4
Add Compute and Database Resources
- Add EC2 instances or Lambda functions
- Define security groups with proper ingress/egress
- Add RDS or DynamoDB resources
- Use DependsOn for resource ordering
- Add Conditions for environment-specific resources
5
Implement Nested Stacks or Modules
- Break large templates into nested stacks
- Store child templates in S3
- Pass parameters between stacks
- Import outputs from other stacks
- Create reusable modules for common patterns
6
Set Up CI/CD for Infrastructure
- Store templates in Git repository
- Use CloudFormation change sets for previewing changes
- Create a CodePipeline for automated deployments
- Add testing and validation stages
- Implement drift detection
Tips
- Use nested stacks/modules for reusability - Create reusable components for common patterns
- Store state remotely (Terraform) - Use S3 + DynamoDB for team collaboration
- Always use change sets - Preview changes before applying to avoid surprises
- Enable termination protection - Prevent accidental deletion of production stacks
Code Examples
CloudFormation VPC Template
vpc-template.yaml
YAML
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC with public and private subnets
Parameters:
VpcCidr:
Type: String
Default: 10.0.0.0/16
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-vpc
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [0, !Cidr [!Ref VpcCidr, 4, 8]]
AvailabilityZone: !Select [0, !GetAZs '']
MapPublicIpOnLaunch: true
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [1, !Cidr [!Ref VpcCidr, 4, 8]]
AvailabilityZone: !Select [0, !GetAZs '']
InternetGateway:
Type: AWS::EC2::InternetGateway
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
Outputs:
VpcId:
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-VpcId
Terraform AWS Configuration
main.tf
HCL
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "my-terraform-state"
key = "vpc/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = var.aws_region
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
tags = {
Name = "${var.project_name}-vpc"
}
}
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 4, count.index)
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
AWS CDK (TypeScript)
lib/vpc-stack.ts
TYPESCRIPT
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
export class VpcStack extends cdk.Stack {
public readonly vpc: ec2.Vpc;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
this.vpc = new ec2.Vpc(this, 'MainVpc', {
maxAzs: 2,
subnetConfiguration: [
{
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
cidrMask: 24,
},
{
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
cidrMask: 24,
},
],
});
new cdk.CfnOutput(this, 'VpcId', {
value: this.vpc.vpcId,
});
}
}
CLI Commands
Terminal Commands
BASH
# CloudFormation
aws cloudformation deploy \
--template-file vpc-template.yaml \
--stack-name my-vpc-stack \
--parameter-overrides VpcCidr=10.0.0.0/16
# View change set before applying
aws cloudformation create-change-set \
--stack-name my-vpc-stack \
--change-set-name my-changes \
--template-body file://vpc-template.yaml
# Terraform
terraform init
terraform plan -out=tfplan
terraform apply tfplan
# CDK
cdk bootstrap
cdk diff
cdk deploy
What You'll Learn
- Infrastructure as Code principles and benefits
- CloudFormation template authoring and best practices
- Stack management, updates, and rollbacks
- Cross-stack references and nested stacks
- Drift detection and remediation