AWS CF - Single AZ ASG
Updated at 2016-02-24 08:35
This is a CloudFormation template that creates a VPC, a public subnet, a single availability zone ELB, an ASG and boot instances in there.
Save JSON blob at the end of this page to example-web-server-stack.json
and create a new CF stack with it.
CloudFormation stack updated are usually non-destructive:
- Updating desired instance count to 2 using the template:
- Starts a new instance, does not touch the old ones.
- Changing the allowed SSH source:
- Doesn't reboot instances, only modifies the security group.
Here are a couple of scenarios I tried out:
New instance is created:
00:16 new server starts to boot
00:17 ASG = InService, Healthy
ELB = OutOfService
00:19 ELB = InService, getting traffic
Server becomes unhealthy:
00:07 sent `sudo service httpd stop` to i-5f0f4385
ELB = InService
ASG = InService, Healthy
00:15 ELB = OutOfService, no more traffic
00:16 ASG = InService, Unhealthy
00:16 ASG = Terminating, Unhealthy
Server becomes unhealthy before the first health check:
23:54 sent `sudo service httpd stop` to i-fe0a4624 after boot, before
any health checks
Because ASG grace period is 5min, considers it `Healthy` and `InService`
Because ELB starts checking right after bootup for routing,
says that it's `OutOfService` so doesn't direct any traffic to it.
00:02 it got marked as Unhealthy and ASG starts replacing it.
00:03 new server starts to boot
ELB = OutOfService
ASG = InService, Healthy
00:06 ELB = InService, getting traffic
AWS CloudFront has this cfn
toolbox of helper scripts:
aws-cfn-bootstrap
is a yum package that contains helper scripts for CloudFormation ecosystem and is automatically installed on Amazon Linux machines./opt/aws/bin/cfn-init
: Initialization helper script that..- Fetches and parses metadata from CloudFormation, from "Resource/WebServerLC/Metadata" in this example.
- Installs packages.
- Writes files to disk like initial configuration.
- Start and stops services.
/opt/aws/bin/cfn-signal
: Helper script for manual communication of instance state to CloudFromation.- Usually used with
CreationPolicy
andUpdatePolicy
in auto scaling groups so CloudFormation knows how many signals to wait for.
- Usually used with
/opt/aws/bin/cfn-get-metadata
: Prints specified metadata to STDOUT, not used in this example./opt/aws/bin/cfn-hup
: Background service that checks for CloudFormation updates and triggers custom hooks when detected like/etc/cfn/hooks.d/cfn-auto-reloader.conf
in this example.
{
"AWSTemplateFormatVersion":"2010-09-09",
"Description":"VPC + Subnet + EBL + ASG with Web Servers",
"Parameters":{
"WebServerKey":{
"Description":"EC2 SSH key pair name for web server instances.",
"Type":"AWS::EC2::KeyPair::KeyName"
},
"WebServerAllowedSSHSource":{
"Description":"Source CIDR where web server SSH key access is allowed.",
"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 CIDR range of the form x.x.x.x/x."
},
"WebServerCount":{
"Description":"Number of desired EC2 instances after launch / update.",
"Type":"Number",
"Default":"1"
},
"WebServerInstanceType":{
"Description":"Web server instance type.",
"Type":"String",
"Default":"t2.small",
"AllowedValues":[
"t1.micro", "t2.nano", "t2.micro", "t2.small", "t2.medium", "t2.large"
]
}
},
"Mappings":{
"RegionToBitsToAMIMap":{
"ap-northeast-1":{ "64":"ami-03cf3903" },
"ap-southeast-1":{ "64":"ami-68d8e93a" },
"ap-southeast-2":{ "64":"ami-fd9cecc7" },
"eu-central-1":{ "64":"ami-a8221fb5" },
"eu-west-1":{ "64":"ami-a10897d6" },
"sa-east-1":{ "64":"ami-b52890a8" },
"us-east-1":{ "64":"ami-1ecae776" },
"us-west-1":{ "64":"ami-d114f295" },
"us-west-2":{ "64":"ami-e7527ed7" }
}
},
"Resources":{
"MainVPC":{
"Type":"AWS::EC2::VPC",
"Properties":{
"CidrBlock":"10.0.0.0/16",
"EnableDnsSupport":"true",
"EnableDnsHostnames":"true"
}
},
"MainSubnet":{
"Type":"AWS::EC2::Subnet",
"Properties":{
"VpcId":{ "Ref":"MainVPC" },
"CidrBlock":"10.0.0.0/24"
}
},
"MainInternetGateway":{
"Type":"AWS::EC2::InternetGateway"
},
"MainGatewayAttachment":{
"Type":"AWS::EC2::VPCGatewayAttachment",
"Properties":{
"VpcId":{ "Ref":"MainVPC" },
"InternetGatewayId":{ "Ref":"MainInternetGateway" }
}
},
"MainRouteTable":{
"Type":"AWS::EC2::RouteTable",
"Properties":{
"VpcId":{ "Ref":"MainVPC" }
}
},
"MainRoute":{
"Type":"AWS::EC2::Route",
"DependsOn":"MainGatewayAttachment",
"Properties":{
"RouteTableId":{ "Ref":"MainRouteTable" },
"DestinationCidrBlock":"0.0.0.0/0",
"GatewayId":{ "Ref":"MainInternetGateway" }
}
},
"MainRouteTableAssociation":{
"Type":"AWS::EC2::SubnetRouteTableAssociation",
"Properties":{
"SubnetId":{ "Ref":"MainSubnet" },
"RouteTableId":{ "Ref":"MainRouteTable" }
}
},
"MainELBSG":{
"Type":"AWS::EC2::SecurityGroup",
"Properties":{
"GroupDescription":"ELB security for Internet access.",
"VpcId":{ "Ref":"MainVPC" },
"SecurityGroupIngress":[{
"IpProtocol":"tcp",
"FromPort":"80",
"ToPort":"80",
"CidrIp":"0.0.0.0/0"
}],
"SecurityGroupEgress":[{
"IpProtocol":"tcp",
"FromPort":"80",
"ToPort":"80",
"CidrIp":"0.0.0.0/0"
}]
}
},
"MainELB":{
"Type":"AWS::ElasticLoadBalancing::LoadBalancer",
"Properties":{
"CrossZone":"true",
"SecurityGroups":[{ "Ref":"MainELBSG" }],
"Subnets":[{ "Ref":"MainSubnet" }],
"Listeners":[{
"LoadBalancerPort":"80",
"InstancePort":"80",
"Protocol":"HTTP"
}],
"HealthCheck":{
"Target":"HTTP:80/",
"HealthyThreshold":"3",
"UnhealthyThreshold":"5",
"Interval":"90",
"Timeout":"60"
}
}
},
"WebServerASG":{
"Type":"AWS::AutoScaling::AutoScalingGroup",
"DependsOn":"MainRoute",
"Properties":{
"AvailabilityZones":[
{"Fn::GetAtt":["MainSubnet", "AvailabilityZone"]}
],
"VPCZoneIdentifier":[{ "Ref":"MainSubnet" }],
"LaunchConfigurationName":{ "Ref":"WebServerLC" },
"MinSize":"1",
"MaxSize":"10",
"DesiredCapacity":{ "Ref":"WebServerCount" },
"LoadBalancerNames":[{ "Ref":"MainELB" }],
"HealthCheckType": "ELB",
"HealthCheckGracePeriod": 300
},
"CreationPolicy":{
"ResourceSignal":{
"Timeout":"PT45M",
"Count":{ "Ref":"WebServerCount" }
}
},
"UpdatePolicy":{
"AutoScalingRollingUpdate":{
"MinInstancesInService":"1",
"MaxBatchSize":"1",
"PauseTime":"PT15M",
"WaitOnResourceSignals":"true"
}
}
},
"WebServerSG":{
"Type":"AWS::EC2::SecurityGroup",
"Properties":{
"GroupDescription":"Allow access from balancer to web servers",
"VpcId":{ "Ref":"MainVPC" },
"SecurityGroupIngress":[
{
"IpProtocol":"tcp",
"FromPort":"80",
"ToPort":"80",
"SourceSecurityGroupId":{ "Ref":"MainELBSG" }
},
{
"IpProtocol":"tcp",
"FromPort":"22",
"ToPort":"22",
"CidrIp":{ "Ref" : "WebServerAllowedSSHSource" }
}
]
}
},
"WebServerLC":{
"Type":"AWS::AutoScaling::LaunchConfiguration",
"Metadata":{
"AWS::CloudFormation::Init":{
"config":{
"packages":{ "yum":{ "httpd":[] } },
"files":{
"/var/www/html/index.html":{
"content":"<h1>Hello World!</h1>",
"mode":"000644",
"owner":"root",
"group":"root"
},
"/etc/cfn/cfn-hup.conf":{
"content":{
"Fn::Join":["", [
"[main]\n",
"stack=", { "Ref":"AWS::StackId" },"\n",
"region=", { "Ref":"AWS::Region" },"\n"
]]
},
"mode":"000400",
"owner":"root",
"group":"root"
},
"/etc/cfn/hooks.d/cfn-auto-reloader.conf":{
"content":{
"Fn::Join":[
"",
[
"[cfn-auto-reloader-hook]\n",
"triggers=post.update\n",
"path=Resources.WebServerLC.Metadata.AWS::CloudFormation::Init\n",
"action=/opt/aws/bin/cfn-init -v ",
" --resource WebServerLC ",
" --stack ", { "Ref":"AWS::StackName" },
" --region ", { "Ref":"AWS::Region" },
"\n",
"runas=root\n"
]
]
}
}
},
"services":{
"sysvinit":{
"httpd":{
"enabled":"true",
"ensureRunning":"true",
"files":[
"/etc/httpd/conf.d/aptobackend.conf",
"/var/www/html/index.html"
]
},
"cfn-hup":{
"enabled":"true",
"ensureRunning":"true",
"files":[
"/etc/cfn/cfn-hup.conf",
"/etc/cfn/hooks.d/cfn-auto-reloader.conf"
]
}
}
}
}
}
},
"Properties":{
"ImageId":{
"Fn::FindInMap":[
"RegionToBitsToAMIMap", { "Ref":"AWS::Region" }, "64"
]
},
"SecurityGroups":[{ "Ref":"WebServerSG" }],
"InstanceType":{ "Ref":"WebServerInstanceType" },
"KeyName":{ "Ref":"WebServerKey" },
"AssociatePublicIpAddress":"true",
"UserData":{
"Fn::Base64":{
"Fn::Join":[
"",
[
"#!/bin/bash -xe\n",
"yum update -y aws-cfn-bootstrap\n",
"# Install the sample application\n",
"/opt/aws/bin/cfn-init -v ",
" --resource WebServerLC ",
" --stack ", { "Ref":"AWS::StackId" },
" --region ", { "Ref":"AWS::Region" },
"\n",
"# Signal copletion\n",
"/opt/aws/bin/cfn-signal -e $? ",
" --resource WebServerASG ",
" --stack ", { "Ref":"AWS::StackId" },
" --region ", { "Ref":"AWS::Region" },
"\n"
]
]
}
}
}
}
},
"Outputs":{
"WebSite":{
"Description":"URL of the website",
"Value":{ "Fn::Join":[ "", [
"http://", { "Fn::GetAtt":[ "MainELB", "DNSName" ] }
]]}
}
}
}