AWS CloudFormation & DevTools Examples
This page contains real-world CloudFormation templates, infrastructure automation examples, and AWS Developer Tools (CodeDeploy, CodeCommit, CodePipeline) configurations extracted from hands-on labs and architectural patterns.
Related: For AWS interview preparation covering CloudFormation concepts and architecture, see the AWS Interview Questions page which includes Top 100 and Top 250 questions.
Architecture Overview
The templates and examples on this page build a complete three-tier architecture:
1. VPC Infrastructure
VPC CloudFormation Template
Creates a VPC with two public subnets across availability zones, with DNS enabled and Internet Gateway.
File: Automating Infrastructure Builds Using CloudFormation/vpc.yaml
Description: >
AWS CloudFormation Template that creates a VPC with DNS and public IPs
enabled and two public subnets. Exports VPC and public subnet IDs.
Parameters:
VpcCIDR:
Description: Please enter the CIDR for this VPC
Type: String
Default: 10.7.0.0/16
PublicSubnet1CIDR:
Description: Please enter the CIDR for the public subnet in the first Availability Zone
Type: String
Default: 10.7.1.0/24
PublicSubnet2CIDR:
Description: Please enter the CIDR for the public subnet in the second Availability Zone
Type: String
Default: 10.7.2.0/24
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCIDR
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
-
Key: Name
Value: !Ref AWS::StackName
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
-
Key: Name
Value: !Ref AWS::StackName
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [0, !GetAZs ""]
CidrBlock: !Ref PublicSubnet1CIDR
MapPublicIpOnLaunch: true
Tags:
-
Key: Name
Value: !Sub AWS::StackName Public Subnet (AZ1)
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
AvailabilityZone: !Select [1, !GetAZs ""]
CidrBlock: !Ref PublicSubnet2CIDR
MapPublicIpOnLaunch: true
Tags:
-
Key: Name
Value: !Sub AWS::StackName Public Subnet (AZ2)
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
-
Key: Name
Value: !Sub AWS::StackName Public Route Table
DefaultPublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
Outputs:
VPC:
Description: VPC ID
Value: !Ref VPC
Export:
Name:
!Sub "${AWS::StackName}-VPC"
PublicSubnets:
Description: A list of the public subnets
Value: !Join [",", [!Ref PublicSubnet1, !Ref PublicSubnet2]]
PublicSubnet1:
Description: Public subnet 1 ID
Value: !Ref PublicSubnet1
Export:
Name:
!Sub "${AWS::StackName}-PublicSubnet1"
PublicSubnet2:
Description: Public subnet 2 ID
Value: !Ref PublicSubnet2
Export:
Name:
!Sub "${AWS::StackName}-PublicSubnet2"
Key Features:
- Two availability zones for high availability
- Public subnets with automatic public IP assignment
- Internet Gateway for outbound connectivity
- CloudFormation Exports for cross-stack references
- Customizable CIDR blocks via parameters
Usage:
aws cloudformation create-stack \
--stack-name my-vpc \
--template-body file://vpc.yaml \
--parameters \
ParameterKey=VpcCIDR,ParameterValue=10.7.0.0/16 \
ParameterKey=PublicSubnet1CIDR,ParameterValue=10.7.1.0/24 \
ParameterKey=PublicSubnet2CIDR,ParameterValue=10.7.2.0/24
VPC IAM Policy
This policy demonstrates CloudFormation stack update controls using resource-level permissions.
File: Advanced CloudFormation Concepts/vpc-policy.json
{
"Statement" : [
{
"Effect" : "Allow",
"Action" : "Update:*",
"Principal": "*",
"Resource" : "*"
},
{
"Effect" : "Deny",
"Action" : "Update:Replace",
"Principal": "*",
"Resource" : "LogicalResourceId/VPC",
"Condition" : {
"StringLike" : {
"ResourceType" : ["AWS::EC2::VPC"]
}
}
}
]
}
Key Concepts:
- Allows all update operations except replacement of the VPC resource
- Prevents accidental deletion and recreation of the VPC
- Uses resource-level permissions to enforce governance
- Applied via
StackPolicyin CloudFormation
2. Load Balancing & Auto Scaling
Application Load Balancer Template
Creates an ALB with listener, target group, and health checks configured.
File: Advanced CloudFormation Concepts/load-balancer.yaml
Description: >
AWS CloudFormation Template that creates an application load balancer,
listener, and target group. Requires network stack name as a parameter.
Parameters:
VPCStackName:
Description: >
Name of an active CloudFormation stack that contains the VPC
resources to be used by this stack.
Type: String
MinLength: 1
MaxLength: 255
AllowedPattern: "^[a-zA-Z][-a-zA-Z0-9]*$"
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow inbound HTTP from the load balancer"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: "0.0.0.0/0"
VpcId:
Fn::ImportValue:
Fn::Sub: "${VPCStackName}-VPC"
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets:
- Fn::ImportValue:
!Sub "${VPCStackName}-PublicSubnet1"
- Fn::ImportValue:
!Sub "${VPCStackName}-PublicSubnet2"
SecurityGroups:
- Ref: ALBSecurityGroup
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn:
Ref: ApplicationLoadBalancer
DefaultActions:
- Type: forward
TargetGroupArn:
!Ref ALBTargetGroup
Port: '80'
Protocol: HTTP
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-VPC"
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
- Key: stickiness.enabled
Value: 'true'
- Key: stickiness.type
Value: lb_cookie
- Key: stickiness.lb_cookie.duration_seconds
Value: '30'
Outputs:
ALBTargetGroup:
Description: Target group ARN
Value: !Ref ALBTargetGroup
Export:
Name:
!Sub "${AWS::StackName}-TargetGroup"
WebsiteURL:
Description: URL for ALB
Value:
Fn::Join:
- ''
- - http://
- Fn::GetAtt:
- ApplicationLoadBalancer
- DNSName
Key Features:
- Cross-stack references using
Fn::ImportValue - Session stickiness (duration 30 seconds)
- Graceful deregistration (20 second timeout)
- Health checks every 30 seconds with 2 healthy/5 unhealthy thresholds
- Exports ALB URL and target group ARN for use in other stacks
Auto Scaling Template
Creates a launch template and Auto Scaling Group with proper IAM roles for CloudWatch and SSM.
File: Advanced CloudFormation Concepts/auto-scaling.yaml
---
Description: >
AWS CloudFormation Template that creates a launch template and an Auto
Scaling Group. Requires network stack name as a parameter.
Parameters:
VPCStackName:
Description: >
Name of an active CloudFormation stack that contains the VPC
resources to be used by this stack.
Type: String
MinLength: 1
MaxLength: 255
AllowedPattern: "^[a-zA-Z][-a-zA-Z0-9]*$"
KeyName:
Description: Name of an existing EC2 key pair
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: Must be the name of an existing EC2 key pair
GroupSize:
Default: '2'
Description: The initial nuber of Webserver instances
Type: Number
InstanceType:
Description: Webserver EC2 instance type
Type: String
Default: t2.micro
AllowedValues:
- t2.nano
- t2.micro
- t2.small
- t2.medium
- t2.large
ConstraintDescription: Must be a valid T2 EC2 instance type.
SSHLocation:
Description: "IP address range that can SSH to the EC2 instances"
Type: String
MinLength: '9'
MaxLength: '18'
Default: 0.0.0.0/0
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Mappings:
AWSRegion2AMI:
us-east-1:
HVM64: ami-97785bed
us-west-2:
HVM64: ami-f2d3638a
us-west-1:
HVM64: ami-824c4ee2
eu-west-1:
HVM64: ami-d834aba1
eu-west-2:
HVM64: ami-403e2524
eu-west-3:
HVM64: ami-8ee056f3
eu-central-1:
HVM64: ami-5652ce39
ap-northeast-1:
HVM64: ami-ceafcba8
ap-northeast-2:
HVM64: ami-863090e8
ap-northeast-3:
HVM64: ami-83444afe
ap-southeast-1:
HVM64: ami-68097514
ap-southeast-2:
HVM64: ami-942dd1f6
ap-south-1:
HVM64: ami-531a4c3c
us-east-2:
HVM64: ami-f63b1193
ca-central-1:
HVM64: ami-a954d1cd
sa-east-1:
HVM64: ami-84175ae8
cn-north-1:
HVM64: ami-cb19c4a6
cn-northwest-1:
HVM64: ami-3e60745c
Resources:
ALBStack:
Description: The nested stack containing the application load balancer.
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/architecting-operational-excellence-aws/load-balancer.yaml
Parameters:
VPCStackName: !Ref VPCStackName
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow inbound HTTP and SSH"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: "0.0.0.0/0"
-
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp:
Ref: SSHLocation
VpcId:
Fn::ImportValue:
Fn::Sub: "${VPCStackName}-VPC"
InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: InstanceRole
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
- arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: InstanceProfile
Path: "/"
Roles:
- Ref: InstanceRole
WebserverLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
ImageId:
Fn::FindInMap:
- AWSRegion2AMI
- !Ref AWS::Region
- HVM64
IamInstanceProfile:
Arn:
Fn::GetAtt:
- InstanceProfile
- Arn
InstanceType:
Ref: InstanceType
SecurityGroupIds:
- Ref: InstanceSecurityGroup
KeyName:
Ref: KeyName
UserData:
Fn::Base64:
Fn::Sub: |
#!/bin/bash -xe
sudo yum -y update
sudo yum -y install aws-cfn-bootstrap
/opt/aws/bin/cfn-signal -e 0 --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
- Fn::ImportValue:
Fn::Sub: "${VPCStackName}-PublicSubnet1"
- Fn::ImportValue:
Fn::Sub: "${VPCStackName}-PublicSubnet2"
LaunchTemplate:
LaunchTemplateId:
Ref: WebserverLaunchTemplate
Version: '1'
MinSize: '1'
MaxSize: '6'
DesiredCapacity:
Ref: GroupSize
TargetGroupARNs:
- Fn::GetAtt: [ ALBStack, Outputs.ALBTargetGroup ]
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count:
Ref: GroupSize
Key Features:
- Regional AMI mappings for multi-region deployments
- IAM instance profile with SSM and CloudWatch permissions
- CloudFormation signal to wait for proper instance initialization
- Nested stack integration with ALB
- Min/Max/Desired capacity for scaling control
Load Balancer Update Template
Demonstrates updating ALB configuration without replacing resources.
File: CloudFormation/load-balancer-update.yaml
Description: >
AWS CloudFormation Template that creates an application load balancer,
listener, and target group. Requires network stack name as a parameter.
Parameters:
VPCStackName:
Description: >
Name of an active CloudFormation stack that contains the VPC
resources to be used by this stack.
Type: String
MinLength: 1
MaxLength: 255
AllowedPattern: "^[a-zA-Z][-a-zA-Z0-9]*$"
Resources:
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow inbound HTTP from the load balancer"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: "0.0.0.0/0"
VpcId:
Fn::ImportValue:
Fn::Sub: "${VPCStackName}-VPC"
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets:
- Fn::ImportValue:
!Sub "${VPCStackName}-PublicSubnet1"
- Fn::ImportValue:
!Sub "${VPCStackName}-PublicSubnet2"
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn:
Ref: ApplicationLoadBalancer
DefaultActions:
- Type: forward
TargetGroupArn:
!Ref ALBTargetGroup
Port: '80'
Protocol: HTTP
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-VPC"
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
- Key: stickiness.enabled
Value: 'true'
- Key: stickiness.type
Value: lb_cookie
- Key: stickiness.lb_cookie.duration_seconds
Value: '300'
Outputs:
ALBTargetGroup:
Description: Target group ARN
Value: !Ref ALBTargetGroup
Export:
Name:
!Sub "${AWS::StackName}-TargetGroup"
WebsiteURL:
Description: URL for ALB
Value:
Fn::Join:
- ''
- - http://
- Fn::GetAtt:
- ApplicationLoadBalancer
- DNSName
Changes from original:
- Increased stickiness duration from 30 to 300 seconds
- Removed security group from ALB properties (uses defaults)
- Simplified configuration for update-only scenarios
3. CloudWatch & AWS Config
Systems Manager Automation for Patching
Executes patch baselines on new EC2 instances triggered by EventBridge.
File: CloudWatch Events and AWS Config/PatchNewInstances.yml
---
schemaVersion: '0.3'
description: Executes the AWS-RunPatchBaseline command document against an instance.
assumeRole: "arn:aws:iam::xxxxxxxxxxxx:role/SSM-PatchNewInstanceRole"
parameters:
InstanceId:
type: String
description: "(Required) ID of EC2 instance to patch"
mainSteps:
- name: sleep
action: aws:sleep
inputs:
Duration: PT4M
- name: scan
action: aws:runCommand
maxAttempts: 1
onFailure: Continue
inputs:
DocumentName: AWS-RunPatchBaseline
InstanceIds:
- "{{InstanceId}}"
Parameters:
Operation: Install
Key Features:
- 4-minute delay before patching (allows SSM agent to start)
- Calls AWS-managed patch baseline document
- Install operation applies all available patches
- Continues on failure to prevent stack rollback
Usage Scenario: When EventBridge detects an EC2 instance launch, this automation document is invoked to automatically patch the instance.
IAM Policy for SSM Operations
Grants permissions for Systems Manager to execute patch operations.
File: CloudWatch Events and AWS Config/InlinePolicyForSSM-PatchNewInstanceRole.txt
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ssm:SendCommand",
"ssm:ListCommands",
"ssm:DescribeInstanceInformation",
"ssm:ListCommandInvocations"
],
"Resource": "*"
}
]
}
Permissions:
ssm:SendCommand- Execute commands on instancesssm:ListCommands- View command historyssm:DescribeInstanceInformation- Query instance inventoryssm:ListCommandInvocations- Check command results
IAM Policy for EventBridge
Allows EventBridge to invoke Systems Manager automation.
File: CloudWatch Events and AWS Config/InlinePolicyForEvents-PatchNewInstanceRole.txt
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "ssm:StartAutomationExecution",
"Resource": "*"
}
]
}
Purpose: EventBridge rule needs permission to execute the SSM automation document when new instances are detected.
EventBridge Input Transformer
Transforms EC2 instance launch events into automation document parameters.
File: CloudWatch Events and AWS Config/InputTransformer.txt
InputTemplate
"{"InstanceId":[<instance>]}"
InputPathsMap
"instance": "$.detail.EC2InstanceId"
How it works:
- EventBridge captures EC2 instance launch event
- Extracts
EC2InstanceIdfrom event detail - Passes as parameter to SSM automation document
- Automation receives
<instance>variable in JSON
4. AWS Developer Tools (CI/CD)
CodeDeploy Agent Installation
SSM document for installing CodeDeploy agent on Linux systems.
File: AWS Developer Tools - CodeCommit, CodeDeploy, and CodePipeline/installCodeDeployAgent.yaml
---
schemaVersion: '2.2'
description: cross-platform sample
mainSteps:
- action: aws:runShellScript
name: InstallCodeDeployAgent
precondition:
StringEquals:
- platformType
- Linux
inputs:
runCommand:
- yum install -y ruby
- yum install -y aws-cli
- cd /home/ec2-user
- aws s3 cp s3://aws-codedeploy-us-east-1/latest/install . --region us-east-1
- chmod +x ./install
- ./install auto
Installation steps:
- Install Ruby (CodeDeploy agent requirement)
- Install AWS CLI
- Download CodeDeploy installer from S3 bucket
- Make installer executable
- Run with
autoflag (auto-starts and enables on boot)
Deploy with CloudFormation:
UserData:
Fn::Base64:
Fn::Sub: |
#!/bin/bash -xe
sudo yum -y update
sudo yum -y install aws-cfn-bootstrap
yum install -y ruby
yum install -y aws-cli
cd /home/ec2-user
aws s3 cp s3://aws-codedeploy-us-east-1/latest/install . --region us-east-1
chmod +x ./install
./install auto
Auto Scaling for CodeDeploy
Launch template and Auto Scaling Group with CodeDeploy agent pre-installed.
File: AWS Developer Tools - CodeCommit, CodeDeploy, and CodePipeline/auto-scaling-update.yaml
---
Description: >
AWS CloudFormation Template that creates a launch template and an Auto
Scaling Group. Requires network stack name as a parameter.
Parameters:
VPCStackName:
Description: >
Name of an active CloudFormation stack that contains the VPC
resources to be used by this stack.
Type: String
MinLength: 1
MaxLength: 255
AllowedPattern: "^[a-zA-Z][-a-zA-Z0-9]*$"
KeyName:
Description: Name of an existing EC2 key pair
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: Must be the name of an existing EC2 key pair
GroupSize:
Default: '2'
Description: The initial nuber of Webserver instances
Type: Number
InstanceType:
Description: Webserver EC2 instance type
Type: String
Default: t2.micro
AllowedValues:
- t2.nano
- t2.micro
- t2.small
- t2.medium
- t2.large
ConstraintDescription: Must be a valid T2 EC2 instance type.
SSHLocation:
Description: "IP address range that can SSH to the EC2 instances"
Type: String
MinLength: '9'
MaxLength: '18'
Default: 0.0.0.0/0
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Mappings:
AWSRegion2AMI:
us-east-1:
HVM64: ami-97785bed
us-west-2:
HVM64: ami-f2d3638a
us-west-1:
HVM64: ami-824c4ee2
eu-west-1:
HVM64: ami-d834aba1
eu-west-2:
HVM64: ami-403e2524
eu-west-3:
HVM64: ami-8ee056f3
eu-central-1:
HVM64: ami-5652ce39
ap-northeast-1:
HVM64: ami-ceafcba8
ap-northeast-2:
HVM64: ami-863090e8
ap-northeast-3:
HVM64: ami-83444afe
ap-southeast-1:
HVM64: ami-68097514
ap-southeast-2:
HVM64: ami-942dd1f6
ap-south-1:
HVM64: ami-531a4c3c
us-east-2:
HVM64: ami-f63b1193
ca-central-1:
HVM64: ami-a954d1cd
sa-east-1:
HVM64: ami-84175ae8
cn-north-1:
HVM64: ami-cb19c4a6
cn-northwest-1:
HVM64: ami-3e60745c
Resources:
ALBStack:
Description: The nested stack containing the application load balancer.
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://s3.amazonaws.com/architecting-operational-excellence-aws/load-balancer.yaml
Parameters:
VPCStackName: !Ref VPCStackName
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow inbound HTTP and SSH"
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: "0.0.0.0/0"
-
IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp:
Ref: SSHLocation
VpcId:
Fn::ImportValue:
Fn::Sub: "${VPCStackName}-VPC"
InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: InstanceRole
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
- arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: InstanceProfile
Path: "/"
Roles:
- Ref: InstanceRole
WebserverLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateData:
ImageId:
Fn::FindInMap:
- AWSRegion2AMI
- !Ref AWS::Region
- HVM64
IamInstanceProfile:
Arn:
Fn::GetAtt:
- InstanceProfile
- Arn
InstanceType:
Ref: InstanceType
SecurityGroupIds:
- Ref: InstanceSecurityGroup
KeyName:
Ref: KeyName
UserData:
Fn::Base64:
Fn::Sub: |
#!/bin/bash -xe
sudo yum -y update
sudo yum -y install aws-cfn-bootstrap
yum install -y ruby
yum install -y aws-cli
cd /home/ec2-user
aws s3 cp s3://aws-codedeploy-us-east-1/latest/install . --region us-east-1
chmod +x ./install
./install auto
/opt/aws/bin/cfn-signal -e 0 --region ${AWS::Region} --stack ${AWS::StackName} --resource AutoScalingGroup
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier:
- Fn::ImportValue:
Fn::Sub: "${VPCStackName}-PublicSubnet1"
- Fn::ImportValue:
Fn::Sub: "${VPCStackName}-PublicSubnet2"
LaunchTemplate:
LaunchTemplateId:
Ref: WebserverLaunchTemplate
Version: '2'
MinSize: '1'
MaxSize: '6'
DesiredCapacity:
Ref: GroupSize
TargetGroupARNs:
- Fn::GetAtt: [ ALBStack, Outputs.ALBTargetGroup ]
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count:
Ref: GroupSize
Difference from generic Auto Scaling:
- UserData includes CodeDeploy agent installation
- All instances ready for immediate application deployment
- No additional manual configuration needed
5. Sample Application
Application Specification (AppSpec)
Defines deployment steps for CodeDeploy.
File: SampleApp/appspec.yml
version: 0.0
os: linux
files:
- source: index.html
destination: /var/www/html
hooks:
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 180
runas: root
AfterInstall:
- location: scripts/change_index.sh
timeout: 30
runas: root
- location: scripts/start_server.sh
timeout: 60
runas: root
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 60
runas: root
Lifecycle:
- ApplicationStop: Stop existing web server
- BeforeInstall: Install Apache HTTP server
- File copy: Deploy index.html to web root
- AfterInstall: Customize page and start server
Install Dependencies
Installs Apache HTTP Server.
File: SampleApp/scripts/install_dependencies.sh
#!/bin/bash
yum install -y httpd
Start Server
Starts the web service.
File: SampleApp/scripts/start_server.sh
#!/bin/bash
service httpd start
Stop Server
Gracefully stops the web service.
File: SampleApp/scripts/stop_server.sh
#!/bin/bash
isExistApp = `pgrep httpd`
if [[ -n $isExistApp ]]; then
service httpd stop
fi
Change Index
Customizes the index page with deployment ID.
File: SampleApp/scripts/change_index.sh
#!/bin/bash
sed -i -e "s/DEPLOYMENT_ID/$DEPLOYMENT_ID/g" /var/www/html/index.html
This script replaces the placeholder with the actual CodeDeploy deployment ID.
Sample HTML Page
Displays deployment success confirmation.
File: SampleApp/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample Deployment</title>
<style>
body {
color: #ffffff;
background-color: #0188cc;
font-family: Arial, sans-serif;
font-size: 14px;
}
h1 {
font-size: 500%;
font-weight: normal;
margin-bottom: 0;
}
h2 {
font-size: 200%;
font-weight: normal;
margin-bottom: 0;
}
</style>
</head>
<body>
<div align="center">
<h1>Congratulations!</h1>
<h2>You deployed this application using AWS CodeDeploy.</h2>
<h3>Deployment ID: DEPLOYMENT_ID</h3>
<p><a href="https://benpiper.com">benpiper.com</a></p>
</div>
</body>
</html>
Deployment Workflow
Step 1: Create VPC Stack
aws cloudformation create-stack \
--stack-name prod-vpc \
--template-body file://vpc.yaml
Step 2: Create ALB Stack
aws cloudformation create-stack \
--stack-name prod-alb \
--template-body file://load-balancer.yaml \
--parameters ParameterKey=VPCStackName,ParameterValue=prod-vpc
Step 3: Create Auto Scaling Stack
aws cloudformation create-stack \
--stack-name prod-compute \
--template-body file://auto-scaling.yaml \
--parameters \
ParameterKey=VPCStackName,ParameterValue=prod-vpc \
ParameterKey=KeyName,ParameterValue=my-keypair \
ParameterKey=GroupSize,ParameterValue=2
Step 4: Deploy Application via CodeDeploy
# Push code to CodeCommit
git push origin main
# Trigger CodePipeline (automatically deploys to instances)
# Or manually create deployment:
aws deploy create-deployment \
--application-name MyApp \
--deployment-group-name production \
--s3-location s3://my-bucket/app.zip \
--deployment-config-name CodeDeployDefault.OneAtATime
Best Practices
- Use nested stacks for modular infrastructure (VPC, ALB, Compute)
- Export values across stacks using
Fn::ImportValueandExport - Implement stack policies to prevent accidental resource replacement
- Use mappings for region-specific values (AMIs, availability zones)
- Add IAM roles with least-privilege permissions to instances
- Use CloudFormation signals (
cfn-signal) to wait for proper initialization - Document parameters with descriptions and constraints
- Version launch templates when making updates
- Test updates with
--change-setbefore applying - Monitor deployments with CloudWatch and EventBridge automation