description = 'My static webapp {0} stack'.format(options['StackRole'])
cft = CloudFormationTemplate(description)
# Make a load balancer
cft.resources.add(Resource('LoadBalancer',
'AWS::ElasticLoadBalancing::LoadBalancer',
{
'AvailabilityZones': options['AppServerAvailabilityZones'],
'HealthCheck': {
'HealthyThreshold': '2',
'Interval': '30',
'Target': 'HTTP:80/',
'Timeout': '5',
'UnhealthyThreshold': '2'
},
'Listeners': [
{
'LoadBalancerPort': '80',
'Protocol': 'HTTP',
'InstanceProtocol': 'HTTP',
'InstancePort': '80',
'PolicyNames': []
},
],
})
)
# Make a security group for the load balancer
cft.resources.add(Resource('ELBSecurityGroup',
'AWS::EC2::SecurityGroup',
{
'GroupDescription': 'allow traffic from our app servers to the load balancer'
})
)
# Put an ingress policy on the load balancer security group
cft.resources.add(Resource('ELBSecurityGroupIngressHTTP',
'AWS::EC2::SecurityGroupIngress',
{
'GroupName': ref('ELBSecurityGroup'),
'IpProtocol': 'tcp',
'FromPort': '80',
'ToPort': '80',
'SourceSecurityGroupName': get_att('LoadBalancer', 'SourceSecurityGroup.GroupName'),
'SourceSecurityGroupOwnerId': get_att('LoadBalancer', 'SourceSecurityGroup.OwnerAlias')
})
)
# Let folks SSH up to the instance
cft.resources.add(Resource('SSHSecurityGroup',
'AWS::EC2::SecurityGroup',
{
'GroupDescription': 'allows inbound SSH from all',
'SecurityGroupIngress': {
'IpProtocol': 'tcp',
'CidrIp': '0.0.0.0/0',
'FromPort': '22',
'ToPort': '22'
}
})
)
# Make our auto scaling group
cft.resources.add(Resource('AppServerAutoScalingGroup',
'AWS::AutoScaling::AutoScalingGroup',
{
'AvailabilityZones': options['AppServerAvailabilityZones'],
'HealthCheckGracePeriod': 300,
'HealthCheckType': 'ELB',
'LaunchConfigurationName': ref('AppServerAutoScalingLaunchConfig'),
'LoadBalancerNames': [ref('LoadBalancer')],
'MaxSize': options['AutoScalingGroupMaxSize'],
'MinSize': options['AutoScalinggroupMinSize'],
'Tags': [{
'Key': 'Name',
'Value': options['StackRole'] + '-static-app-server',
'PropagateAtLaunch': True,
}]
})
)
# Create the auto scaling group configuration for managing the server instances
cft.resources.add(Resource('AppServerAutoScalingLaunchConfig',
'AWS::AutoScaling::LaunchConfiguration',
{
'ImageId': options['ImageId'],
'InstanceType': options['AppServerInstanceType'],
'KeyName': options['KeyPair'],
'SecurityGroups': [
ref('ELBSecurityGroup'),
ref('SSHSecurityGroup'),
],
# Another way to pass a user-data script,
# looks better than the first example, but it's more tedious
'UserData': base64(join('\n',
'#!/bin/bash -v',
'# do stuff...',
'# ',
'# Like maybe kick off cfnbootstrap, using all of the',
'# AWS:CloudFormation::Init Metadata That we could have',
'# put on our AutoScalingGroup',
'exit 0',
))
})
)
# Scale up policy for when the scale up alarm trips
cft.resources.add(Resource('AppServerScaleUpPolicy',
'AWS::AutoScaling::ScalingPolicy',
{
'AdjustmentType': 'ChangeInCapacity',
'AutoScalingGroupName': ref('AppServerAutoScalingGroup'),
'Cooldown': '600',
'ScalingAdjustment': '1'
})
)
# Scale down policy for when the scale down alarm trips
cft.resources.add(Resource('AppServerScaleDownPolicy',
'AWS::AutoScaling::ScalingPolicy',
{
'AdjustmentType': 'ChangeInCapacity',
'AutoScalingGroupName': ref('AppServerAutoScalingGroup'),
'Cooldown': '600',
'ScalingAdjustment': '-1'
})
)
# CloudWatch scale up alarm for triggering scale events
cft.resources.add(Resource('AppServerCPUAlarmHigh',
'AWS::CloudWatch::Alarm',
{
'AlarmDescription': 'Scale up if average CPU usage of the AppServers stays above 75% for at least 5 minutes',
'Dimensions': [{'Name': 'AutoScalingGroupName', 'Value': ref('AppServerAutoScalingGroup')}],
'Namespace': 'AWS/EC2',
'MetricName': 'CPUUtilization',
'Unit': 'Percent',
'Period': '60',
'EvaluationPeriods': '5',
'Statistic': 'Average',
'ComparisonOperator': 'GreaterThanThreshold',
'Threshold': '75',
'ActionsEnabled': options['CloudWatchAlarmActionsEnabled'],
'AlarmActions': [ref('AppServerScaleUpPolicy')]
})
)
# CloudWatch scale down alarm for triggering scale events
cft.resources.add(Resource('AppServerCPUAlarmLow',
'AWS::CloudWatch::Alarm',
{
'AlarmDescription': 'Scale down if average CPU usage of the AppServers stays below 25% for at least 5 minutes',
'Dimensions': [{'Name': 'AutoScalingGroupName', 'Value': ref('AppServerAutoScalingGroup')}],
'Namespace': 'AWS/EC2',
'MetricName': 'CPUUtilization',
'Unit': 'Percent',
'Period': '60',
'EvaluationPeriods': '5',
'Statistic': 'Average',
'ComparisonOperator': 'LessThanThreshold',
'Threshold': '25',
'ActionsEnabled': options['CloudWatchAlarmActionsEnabled'],
'AlarmActions': [ref('AppServerScaleUpPolicy')]
})
)
# Add the load balancer endpoint to our outputs
cft.outputs.add(
Output('LoadBalancerEndpoint',
get_att('LoadBalancer', 'DNSName')
)
)