Skip to content

Instantly share code, notes, and snippets.

@nicl
Created April 25, 2016 19:52
Show Gist options
  • Save nicl/34517e25fba0939a0f1419d4d0fbbeb8 to your computer and use it in GitHub Desktop.
Save nicl/34517e25fba0939a0f1419d4d0fbbeb8 to your computer and use it in GitHub Desktop.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Top Secret stack",
"Parameters": {
"Name": {
"Description": "Name of the service",
"Type": "String"
},
"VpcId": {
"Description": "ID of the VPC onto which to launch the application eg. vpc-1234abcd",
"Type": "String"
},
"PrivateVpcSubnets": {
"Description": "Subnets to use in VPC for public internet-facing ELB eg. subnet-abcd1234",
"Type": "CommaDelimitedList"
},
"PublicVpcSubnets": {
"Description": "Subnets to use in VPC for public internet-facing ELB eg. subnet-abcd1234",
"Type": "CommaDelimitedList"
},
"SSLCert": {
"Description": "ARN of valid SSL certificate",
"Type": "String"
},
"AMI": {
"Description": "EC2 AMI ID",
"Type": "String"
},
"InstanceType": {
"Description": "EC2 instance type (m3.large, etc.)",
"Type": "String"
},
"SshKey": {
"Description": "Key for ssh access to EC2 instances",
"Type": "String"
},
"AlarmSNS": {
"Description": "ARN for an SNS topic, used to send Cloudwatch alerts for any alarms",
"Type": "String"
}
},
"Resources": {
"InstanceProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [
{
"Ref": "Role"
}
]
}
},
"Role": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"ec2.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Path": "/",
"Policies": [
{
"PolicyName": "TopSecretPolicy",
"PolicyDocument": {
"Statement": [
{
"Resource": [
{ "Ref": "DynamoTable" },
{ "Fn::Join": [ "", [ { "Ref": "DynamoTable" },"/index/*"] ] }
],
"Action": [ "dynamodb:*" ],
"Effect": "Allow"
},
]
}
}
]
}
},
"DynamoTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"AttributeDefinitions": [
{
"AttributeName": "Id",
"AttributeType": "S"
}
],
"GlobalSecondaryIndexes": [
{
"IndexName": "my-secondary-index",
"Projection": {
"ProjectionType": "ALL"
},
"KeySchema": [
{
"KeyType": "HASH",
"AttributeName": "Id"
},
{
"KeyType": "RANGE",
"AttributeName": "LastModified"
}
],
"ProvisionedThroughput" : {
"ReadCapacityUnits" : 1,
"WriteCapacityUnits" : 1
}
}
],
"KeySchema": [
{
"AttributeName": "Id",
"KeyType": "HASH"
}
],
"LocalSecondaryIndexes": [ ],
"ProvisionedThroughput": {
"WriteCapacityUnits": 1,
"ReadCapacityUnits": 1
},
"TableName": { "Ref": "Name" }
}
"ElasticLoadBalancer": {
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
"Properties": {
"CrossZone": true,
"Listeners": [
{
"Protocol": "HTTPS",
"LoadBalancerPort": "443",
"InstancePort": "8080",
"SSLCertificateId": { "Ref": "SSLCert" }
}
],
"HealthCheck": {
"Target": "HTTP:8080/healthcheck",
"Timeout": "10",
"Interval": "20",
"UnhealthyThreshold": "10",
"HealthyThreshold": "2"
},
"Subnets": {
"Ref": "PublicVpcSubnets"
},
"SecurityGroups": [
{
"Ref": "ElasticLoadBalancerSecurityGroup"
}
]
}
},
"AppServerGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": {
"Fn::GetAZs": ""
},
"VPCZoneIdentifier": {
"Ref": "PrivateVpcSubnets"
},
"LaunchConfigurationName": {
"Ref": "LaunchConfig"
},
"MinSize": 1
"MaxSize": 2,
"HealthCheckType": "ELB",
"HealthCheckGracePeriod": 300,
"LoadBalancerNames": [ {"Ref": "ElasticLoadBalancer"}],
"Tags": [
{
"Key": "Name",
"Value": {"Ref": "Name"},
"PropagateAtLaunch": "true"
}
]
}
},
"LaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": { "Ref": "AMI" },
"SecurityGroups": [ { "Ref": "AppSecurityGroup" } ],
"InstanceType": { "Ref": "InstanceType" },
"IamInstanceProfile": { "Ref": "InstanceProfile" },
"KeyName": { "Ref": "SshKey" },
"UserData": {
"Fn::Base64": {
"Fn::Join": [ "\n", [
"#!/bin/bash -v",
"# any (bash) commands to execute on instance startup",
"# you could also download or execute any other scripts here",
"# you might need to dl the code deploy code here (I'm not sure how it works)"
] ]
}
}
}
},
"ElasticLoadBalancerSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "Allow public access over HTTP(S)",
"VpcId": {
"Ref": "VpcId"
},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "80",
"ToPort": "80",
"CidrIp": "0.0.0.0/0"
},
{
"IpProtocol": "tcp",
"FromPort": "443",
"ToPort": "443",
"CidrIp": "0.0.0.0/0"
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "tcp",
"FromPort": "8080",
"ToPort": "8080",
"CidrIp": "0.0.0.0/0"
}
]
}
},
"AppSecurityGroup": {
"Type": "AWS::EC2::SecurityGroup",
"Properties": {
"GroupDescription": "SSH and HTTP",
"VpcId": { "Ref": "VpcId" },
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "8080",
"ToPort": "8080",
"SourceSecurityGroupId": {
"Ref": "ElasticLoadBalancerSecurityGroup"
}
},
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": "0.0.0.0/0"
}
]
}
},
"HighLatencyAlarmAlert": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": { "Fn::Join": ["-", [ { "Ref": "Name"}, "alarm", "latency", "alert" ] ]},
"AlarmDescription": "Average latency is >= 3s for a 3 minute period",
"Namespace": "AWS/ELB",
"Dimensions": [ { "Name": "LoadBalancerName", "Value": { "Ref": "ElasticLoadBalancer" } } ],
"MetricName": "Latency",
"Statistic": "Average",
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"Threshold": "3",
"Period": "60",
"EvaluationPeriods": "3",
"AlarmActions": { "Ref": "AlarmSNS" }
}
},
"High5xxAlarmAlert": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": { "Fn::Join": ["-", [ { "Ref": "Name"}, { "Ref": "Stage" }, "alarm", "5xx", "alert" ] ]},
"AlarmDescription": "Number of 5XXs is >= 100 for a 1 minute period",
"Namespace": "AWS/ELB",
"Dimensions": [ { "Name": "LoadBalancerName", "Value": { "Ref": "ElasticLoadBalancer" } } ],
"MetricName": "HTTPCode_Backend_5XX",
"Statistic": "Sum",
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"Threshold": "100",
"Period": "60",
"EvaluationPeriods": "1",
"AlarmActions": { "Ref": "AlarmSNS" }
}
}
},
"Outputs": {
"ElasticLoadBalancer": {
"Value": { "Fn::GetAtt": [ "ElasticLoadBalancer", "DNSName" ] }
}
}
}
}
@nicl
Copy link
Author

nicl commented Apr 25, 2016

  • you'll need to add your own attribute definitions to the Dynamo table (you only need to define fields which are used for indexes)
  • you'll need to customise a lot of stuff, especially pay attention to the ELB healthcheck - you can set this to the EC2 type initially, but using a HTTP check is recommended

This link will help telling you what properties you can use:

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

Also worth checking out the available functions in cloudformation:

http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

More generally check out the left-hand-side menu bar for useful info.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment