{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "AWS CloudFormation Sample Template Rails_Multi_AZ: Create a highly available, scalable Ruby on Rails stack with a multi-AZ MySQL Amazon RDS database instance for the backend data store. This template demonstrates using the AWS CloudFormation bootstrap scripts to install the packages and files necessary to deploy the packages and files at instance launch time. **WARNING** This template creates one or more Amazon EC2 instances, an Elastic Load Balancer and an Amazon RDS DB instance. You will be billed for the AWS resources used if you create a stack from this template.",
"Parameters" : {
"KeyName": {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
"Type": "String",
"MinLength": "1",
"MaxLength": "255",
"AllowedPattern" : "[\\x20-\\x7E]*",
"ConstraintDescription" : "can contain only ASCII characters."
},
"DBName": {
"Default": "MyDatabase",
"Description" : "MySQL database name",
"Type": "String",
"MinLength": "1",
"MaxLength": "64",
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
},
"DBUsername": {
"NoEcho": "true",
"Description" : "Username for MySQL database access",
"Type": "String",
"MinLength": "1",
"MaxLength": "16",
"AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
},
"DBPassword": {
"NoEcho": "true",
"Description" : "Password for MySQL database access",
"Type": "String",
"MinLength": "8",
"MaxLength": "41",
"AllowedPattern" : "[a-zA-Z0-9]*",
"ConstraintDescription" : "must contain only alphanumeric characters."
},
"DBAllocatedStorage": {
"Default": "5",
"Description" : "The size of the database (Gb)",
"Type": "Number",
"MinValue": "5",
"MaxValue": "1024",
"ConstraintDescription" : "must be between 5 and 1024Gb."
},
"DBInstanceClass": {
"Default": "db.m1.small",
"Description" : "The database instance type",
"Type": "String",
"AllowedValues" : [ "db.m1.small", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge" ],
"ConstraintDescription" : "must select a valid database instance type."
},
"MultiAZDatabase": {
"Default": "true",
"Description" : "Create a multi-AZ MySQL Amazon RDS database instance",
"Type": "String",
"AllowedValues" : [ "true", "false" ],
"ConstraintDescription" : "must be either true or false."
},
"WebServerCapacity": {
"Default": "2",
"Description" : "The initial number of WebServer instances",
"Type": "Number",
"MinValue": "1",
"MaxValue": "5",
"ConstraintDescription" : "must be between 1 and 5 EC2 instances."
},
"InstanceType" : {
"Description" : "WebServer EC2 instance type",
"Type" : "String",
"Default" : "m1.small",
"AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","m3.xlarge","m3.2xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
"ConstraintDescription" : "must be a valid EC2 instance type."
},
"SSHLocation" : {
"Description" : " The IP address range that can be used to 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" : {
"AWSInstanceType2Arch" : {
"t1.micro" : { "Arch" : "64" },
"m1.small" : { "Arch" : "64" },
"m1.medium" : { "Arch" : "64" },
"m1.large" : { "Arch" : "64" },
"m1.xlarge" : { "Arch" : "64" },
"m2.xlarge" : { "Arch" : "64" },
"m2.2xlarge" : { "Arch" : "64" },
"m2.4xlarge" : { "Arch" : "64" },
"m3.xlarge" : { "Arch" : "64" },
"m3.2xlarge" : { "Arch" : "64" },
"c1.medium" : { "Arch" : "64" },
"c1.xlarge" : { "Arch" : "64" },
"cc1.4xlarge" : { "Arch" : "64HVM" },
"cc2.8xlarge" : { "Arch" : "64HVM" },
"cg1.4xlarge" : { "Arch" : "64HVM" }
},
"AWSRegionArch2AMI" : {
"us-east-1" : { "32" : "ami-31814f58", "64" : "ami-1b814f72", "64HVM" : "ami-0da96764" },
"us-west-2" : { "32" : "ami-38fe7308", "64" : "ami-30fe7300", "64HVM" : "NOT_YET_SUPPORTED" },
"us-west-1" : { "32" : "ami-11d68a54", "64" : "ami-1bd68a5e", "64HVM" : "NOT_YET_SUPPORTED" },
"eu-west-1" : { "32" : "ami-973b06e3", "64" : "ami-953b06e1", "64HVM" : "NOT_YET_SUPPORTED" },
"ap-southeast-1" : { "32" : "ami-b4b0cae6", "64" : "ami-beb0caec", "64HVM" : "NOT_YET_SUPPORTED" },
"ap-southeast-2" : { "32" : "ami-b3990e89", "64" : "ami-bd990e87", "64HVM" : "NOT_YET_SUPPORTED" },
"ap-northeast-1" : { "32" : "ami-0644f007", "64" : "ami-0a44f00b", "64HVM" : "NOT_YET_SUPPORTED" },
"sa-east-1" : { "32" : "ami-3e3be423", "64" : "ami-3c3be421", "64HVM" : "NOT_YET_SUPPORTED" }
}
},
"Resources" : {
"ElasticLoadBalancer" : {
"Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
"Metadata" : {
"Comment" : "Configure the Load Balancer with a simple health check and cookie-based stickiness"
},
"Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"LBCookieStickinessPolicy" : [ {
"PolicyName" : "CookieBasedPolicy",
"CookieExpirationPeriod" : "30"
} ],
"Listeners" : [ {
"LoadBalancerPort" : "80",
"InstancePort" : "3000",
"Protocol" : "HTTP",
"PolicyNames" : [ "CookieBasedPolicy" ]
} ],
"HealthCheck" : {
"Target" : "HTTP:3000/",
"HealthyThreshold" : "2",
"UnhealthyThreshold" : "5",
"Interval" : "10",
"Timeout" : "5"
}
}
},
"WebServerGroup" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"Properties" : {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : "1",
"MaxSize" : "5",
"DesiredCapacity" : { "Ref" : "WebServerCapacity" },
"LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
}
},
"LaunchConfig": {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Metadata" : {
"Comment1" : "Configure the bootstrap helpers to install the Rails",
"Comment2" : "The application is downloaded from the CloudFormationRailsSample.zip file",
"AWS::CloudFormation::Init" : {
"config" : {
"packages" : {
"yum" : {
"gcc-c++" : [],
"make" : [],
"ruby-devel" : [],
"rubygems" : [],
"mysql" : [],
"mysql-devel" : [],
"mysql-libs" : []
},
"rubygems" : {
"rack" : ["1.3.6"],
"execjs" : [],
"therubyracer" : [],
"rails" : ["3.2.14"]
}
},
"sources" : {
"/home/ec2-user/sample" : "https://s3.amazonaws.com/cloudformation-examples/CloudFormationRailsSample.zip"
},
"files" : {
"/home/ec2-user/sample/config/database.yml" : {
"content" : { "Fn::Join" : ["", [
"development:\n",
" adapter: mysql2\n",
" encoding: utf8\n",
" reconnect: false\n",
" pool: 5\n",
" database: ", { "Ref" : "DBName" }, "\n",
" username: ", { "Ref" : "DBUsername" }, "\n",
" password: ", { "Ref" : "DBPassword" }, "\n",
" host: ", { "Fn::GetAtt": [ "MySQLDatabase", "Endpoint.Address" ] }, "\n",
" port: ", { "Fn::GetAtt": [ "MySQLDatabase", "Endpoint.Port" ] }, "\n"
]]},
"mode" : "000644",
"owner" : "root",
"group" : "root"
}
}
}
}
},
"Properties": {
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
{ "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"InstanceType" : { "Ref" : "InstanceType" },
"SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
"KeyName" : { "Ref" : "KeyName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -v\n",
"yum update -y aws-cfn-bootstrap\n",
"# Helper function\n",
"function error_exit\n",
"{\n",
" /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "WaitHandle" }, "'\n",
" exit 1\n",
"}\n",
"# Install Rails packages\n",
"/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r LaunchConfig ",
" --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",
"# Install any other Gems, create the database and run a migration\n",
"cd /home/ec2-user/sample\n",
"export PATH=$PATH:/usr/local/bin\n",
"bundle install || error_exit 'Failed to install bundle'\n",
"rake db:migrate || error_exit 'Failed to execute database migration'\n",
"# Startup the rails server\n",
"rails server -d\n",
"echo \"cd /home/ec2-user/sample\" >> /etc/rc.local\n",
"echo \"rails server -d\" >> /etc/rc.local\n",
"# All is well so signal success\n",
"/opt/aws/bin/cfn-signal -e 0 -r \"Rails application setup complete\" '", { "Ref" : "WaitHandle" }, "'\n"
]]}}
}
},
"WaitHandle" : {
"Type" : "AWS::CloudFormation::WaitConditionHandle"
},
"WaitCondition" : {
"Type" : "AWS::CloudFormation::WaitCondition",
"DependsOn" : "WebServerGroup",
"Properties" : {
"Handle" : {"Ref" : "WaitHandle"},
"Timeout" : "1500",
"Count" : { "Ref" : "WebServerCapacity" }
}
},
"WebServerSecurityGroup" : {
"Type" : "AWS::EC2::SecurityGroup",
"Properties" : {
"GroupDescription" : "Enable HTTP access via port 3000 locked down to the load balancer + SSH access",
"SecurityGroupIngress" : [
{"IpProtocol" : "tcp", "FromPort" : "3000", "ToPort" : "3000", "SourceSecurityGroupOwnerId" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"]},"SourceSecurityGroupName" : {"Fn::GetAtt" : ["ElasticLoadBalancer", "SourceSecurityGroup.GroupName"]}},
{"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
]
}
},
"DBSecurityGroup": {
"Type": "AWS::RDS::DBSecurityGroup",
"Properties": {
"GroupDescription": "Grant database access to web server",
"DBSecurityGroupIngress": [
{ "EC2SecurityGroupName": { "Ref": "WebServerSecurityGroup" } }
]
}
},
"MySQLDatabase": {
"Type": "AWS::RDS::DBInstance",
"Properties": {
"Engine" : "MySQL",
"DBName" : { "Ref": "DBName" },
"MultiAZ" : { "Ref": "MultiAZDatabase" },
"MasterUsername": { "Ref": "DBUsername" },
"MasterUserPassword": { "Ref" : "DBPassword" },
"DBInstanceClass": { "Ref" : "DBInstanceClass" },
"DBSecurityGroups": [ { "Ref": "DBSecurityGroup" } ],
"AllocatedStorage": { "Ref" : "DBAllocatedStorage" }
}
}
},
"Outputs" : {
"WebsiteURL" : {
"Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "ElasticLoadBalancer", "DNSName" ]}]] },
"Description" : "URL for newly created Rails application"
}
}
}