Created
March 29, 2018 10:03
-
-
Save baliame/e41876e4418635159ab512c903a1b69f to your computer and use it in GitHub Desktop.
Cloudformation basics blog post @ cheppers.com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
AWSTemplateFormatVersion: "2010-09-09" | |
Parameters: | |
DBPassword: | |
Type: String | |
NoEcho: true # Specifies that this string should never be printed on the interface or through the API. | |
AllowedPattern: ^[a-zA-Z0-9_]*$ # Provided strings must match this regex. | |
MinLength: 8 | |
MaxLength: 32 | |
Description: RDS root password. | |
Resources: | |
VPC: # This is the internal resource identifier, an arbitrary string. | |
Type: AWS::EC2::VPC # VPCs are of the type AWS::EC2::VPC. All AWS resources are in the form of AWS::<Category>::<Resource> | |
Properties: | |
CidrBlock: 10.41.0.0/16 # CidrBlock defines the IP range for the VPC. | |
Tags: # Tags allow us to assign arbitrary key-value pairs to most AWS resources. | |
- Key: Name | |
Value: ExampleVPC | |
InternetGateway: | |
Type: AWS::EC2::InternetGateway | |
Properties: | |
Tags: | |
- Key: Name | |
Value: ExampleGateway | |
VPCGatewayAttachment: | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
InternetGatewayId: !Ref InternetGateway # !Ref is an AWS function that gets a reference to a different resource by internal ID. | |
VpcId: !Ref VPC # The JSON syntax for this function is { Ref: "InternalResourceName" } | |
# The return value of Ref is different for each resouce type. Refer to the documentation for more information. | |
SQSQueue: | |
Type: AWS::SQS::Queue | |
Properties: | |
QueueName: !Join ["-", [!Ref "AWS::StackName", "task-queue"]] | |
SubnetAPIZoneA: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]] # Like AWS::StackName, AWS::Region is a referencable constant. This will construct the string | |
# "us-east-1a" in the region us-east-1. | |
CidrBlock: 10.41.0.0/26 # The IP range to assign to the subnet. For this subnet, we use 10.41.0.0 - 10.41.0.63. | |
# The IP range must be a subset of the range defined for the VPC. | |
VpcId: !Ref VPC # Assigns this subnet as a subnet within the VPC defined above. | |
SubnetAPIZoneB: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]] # us-east-1b | |
CidrBlock: 10.41.0.64/26 # 10.41.0.64 - 10.41.0.127 | |
VpcId: !Ref VPC | |
SubnetWorkerZoneA: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]] # us-east-1a | |
CidrBlock: 10.41.1.0/26 # 10.41.1.0 - 10.41.1.63 | |
VpcId: !Ref VPC | |
SubnetWorkerZoneB: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]] # us-east-1b | |
CidrBlock: 10.41.1.64/26 # 10.41.1.64 - 10.41.1.127 | |
VpcId: !Ref VPC | |
SubnetDatabaseZoneA: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "a"]] # us-east-1a | |
CidrBlock: 10.41.2.0/26 # 10.41.2.0 - 10.41.2.63 | |
VpcId: !Ref VPC | |
SubnetDatabaseZoneB: | |
Type: AWS::EC2::Subnet | |
Properties: | |
AvailabilityZone: !Join ["", [!Ref "AWS::Region", "b"]] # us-east-1b | |
CidrBlock: 10.41.2.64/26 # 10.41.2.64 - 10.41.2.127 | |
VpcId: !Ref VPC | |
SubnetGroupDatabase: | |
Type: AWS::RDS::DBSubnetGroup | |
Properties: | |
DBSubnetGroupDescription: MySQL Database subnet grouping. # Arbitrary | |
SubnetIds: | |
- !Ref SubnetDatabaseZoneA | |
- !Ref SubnetDatabaseZoneB | |
SecurityGroupDatabase: | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: !Join ["-", [!Ref "AWS::StackName", "database"]] # Despite being named description, this is more of an ID for the group. | |
SecurityGroupEgress: # Outgoing traffic rules. | |
- CidrIp: 0.0.0.0/0 # Allow traffic anywhere. | |
FromPort: "-1" | |
ToPort: "-1" # Through any port. | |
IpProtocol: "-1" # On any IP protocol. | |
SecurityGroupIngress: # Incoming traffic rules. | |
- CidrIp: !GetAtt ["VPC", "CidrBlock"] # Retrieves the CidrBlock attribute of the VPC resource. Not all attributes are exposed | |
# to GetAtt. JSON syntax is { "Fn::GetAtt": ["Resource", "Attribute"] }. | |
# This essentially means that traffic is allowed from the IP range of the VPC, and | |
# transitively, from all servers within the VPC. | |
FromPort: "3306" | |
ToPort: "3306" # Specifies the port range 3306-3306 (equivalent to port 3306). | |
IpProtocol: "tcp" # Altogether, this rule specifies that we allow TCP traffic on port 3306 from the VPC | |
VpcId: !Ref VPC | |
Database: | |
Type: AWS::RDS::DBInstance | |
Properties: | |
AllocatedStorage: "10" # The allocated storage space for the data in gigabytes. | |
DBInstanceClass: db.m4.large # The class to use for this instance. Instance classes come in several sizes with different | |
# optimization focuses - for example, the burst-capable t2 instances or the memory optimized r3s. | |
# In our case, we use a third generation standard instance. | |
DBSubnetGroupName: !Ref SubnetGroupDatabase # Reference to the subnet group we created above. | |
Engine: MySQL # The database engine to use. Options include aurora, mariadb, mysql and postgres. | |
EngineVersion: "5.7.17" # The version of the database engine - refer to the documentation for the available versions. | |
MasterUserPassword: !Ref DBPassword # Use the password parameter that we set up above. | |
MasterUsername: admin # Username for the root account. | |
MultiAZ: true # Since we're using a subnet group with multiple AZs, we set this to true. | |
VPCSecurityGroups: # Attaches security groups to this RDS instance. | |
- !Ref SecurityGroupDatabase | |
SecurityGroupAPILoadBalancer: # The API's load balancer should be exposed to the world as our API endpoint and should be able to forward traffic to the VPC. | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: !Join ["-", [!Ref "AWS::StackName", "api-elb"]] | |
SecurityGroupEgress: # As load balancers run no custom code, we don't care where it can forward traffic and allow it to go anywhere. | |
- CidrIp: 0.0.0.0/0 | |
FromPort: "-1" | |
IpProtocol: "-1" | |
ToPort: "-1" | |
SecurityGroupIngress: # We allow HTTP traffic from any IP address. | |
- CidrIp: 0.0.0.0/0 | |
FromPort: "80" | |
IpProtocol: tcp | |
ToPort: "80" | |
VpcId: !Ref VPC | |
SecurityGroupAPI: # The API instances should be able to listen to HTTP requests from the ELB and SSH requests from developers. | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: !Join ["-", [!Ref "AWS::StackName", "api-elb"]] | |
SecurityGroupEgress: # We could theoretically restrict outgoing traffic to within the ELB, but this is entirely up to what our app requires. | |
- CidrIp: 0.0.0.0/0 | |
FromPort: "-1" | |
IpProtocol: "-1" | |
ToPort: "-1" | |
SecurityGroupIngress: | |
- CidrIp: 0.0.0.0/0 # This first rule specifies that we accept SSH requests (TCP port 22) from any IP address. | |
FromPort: "22" | |
IpProtocol: tcp | |
ToPort: "22" | |
- FromPort: "3000" # This second rule specifies that we accept requests on port 3000 from the load balancer's security group. | |
ToPort: "3000" # We do this on port 3000 because the ELB has the ability to change the port of an incoming request. | |
IpProtocol: tcp # Later, we'll see that the ELB picks up requests on port 80 and forwards them on port 3000. | |
SourceSecurityGroupId: !Ref SecurityGroupAPILoadBalancer | |
VpcId: !Ref VPC | |
SecurityGroupWorker: # The worker instances have no exposed API, so we only care about the ability to SSH in. | |
Type: AWS::EC2::SecurityGroup | |
Properties: | |
GroupDescription: !Join ["-", [!Ref "AWS::StackName", "api-elb"]] | |
SecurityGroupEgress: | |
- CidrIp: 0.0.0.0/0 | |
FromPort: "-1" | |
IpProtocol: "-1" | |
ToPort: "-1" | |
SecurityGroupIngress: | |
- CidrIp: 0.0.0.0/0 # This first rule specifies that we accept SSH requests (TCP port 22) from any IP address. | |
FromPort: "22" | |
IpProtocol: tcp | |
ToPort: "22" | |
VpcId: !Ref VPC | |
LoadBalancerAPI: | |
Type: AWS::ElasticLoadBalancing::LoadBalancer | |
Properties: | |
CrossZone: true # Specifies that the target instances are in multiple availability zones. | |
HealthCheck: # Defines how the ELB determines if an instance is healthy and may receive requests. | |
HealthyThreshold: "5" # An unhealthy instance must pass this many health checks in a row to be considered healthy again. | |
Interval: "15" # The time (in seconds) between two health checks. | |
Target: HTTP:3000/ping # The protocol, port (and for HTTP, the path) to send a request to. For HTTP, a 200 status code indicates a healthy response. | |
# This specific target specifies that an HTTP request is sent to port 3000 on path /ping on each instance. | |
Timeout: "8" # Time to wait for response - timeouts are considered unhealthy. | |
UnhealthyThreshold: "2" # A healthy instance must fail this many health checks in a row to be considered unhealthy. | |
Listeners: # Specifies a forwarding rule. | |
- LoadBalancerPort: "80" # The load balancer listens on port 80; | |
Protocol: HTTP # uses the HTTP transport protocol for forwarding; | |
InstancePort: "3000" # and sends it to port 3000 of one of the healthy instances under the ELB. | |
LoadBalancerName: !Join ["-", [!Ref "AWS::StackName", "api"]] | |
SecurityGroups: # Here we assign the security group that we created above to the ELB. | |
- !Ref SecurityGroupAPILoadBalancer | |
Subnets: # And the subnets as well. | |
- !Ref SubnetAPIZoneA | |
- !Ref SubnetAPIZoneB | |
LaunchConfigurationAPI: | |
Type: AWS::AutoScaling::LaunchConfiguration | |
DependsOn: | |
- SecurityGroupAPI # We set this explicit dependency to enforce security groups to be created before the launch configuration is created. | |
Properties: | |
AssociatePublicIpAddress: true # When set, an instance is assigned a public IP address as well as a private one. Required for access from outside the VPC. | |
BlockDeviceMappings: # Associates storage devices with the instances. | |
- DeviceName: "/dev/sda1" # The device will be available at this path for mounting. | |
Ebs: # Specifies that this should be an Elastic Block Storage volume. | |
VolumeSize: "10" # The storage space on the device, in gigabytes. | |
VolumeType: gp2 # Specifies the block device type - gp2 is a general-purpose SSD that should suffice for most instances. | |
ImageId: "ami-d15a75c7" # Specifies the AMI (image) that will be used as the base for these instances. | |
InstanceType: "t2.micro" # The EC2 instance type to provision. As with database instances, several sizes and purposes are available. | |
SecurityGroups: # Assigns the security groups specified here to each instance created by the configuration. | |
- !Ref SecurityGroupAPI | |
UserData: # UserData is a base64-encoded shell script that runs after the instane is created as root. | |
Fn::Base64: # Fn::Base64 is a function key that base64-encodes the value string. | |
!Join ["\n", [ | |
"#!/bin/bash", | |
"service my-api-service start" | |
]] | |
LaunchConfigurationWorker: | |
Type: AWS::AutoScaling::LaunchConfiguration | |
DependsOn: | |
- SecurityGroupWorker | |
Properties: | |
AssociatePublicIpAddress: true | |
BlockDeviceMappings: | |
- DeviceName: "/dev/sda1" | |
Ebs: | |
VolumeSize: "10" | |
VolumeType: gp2 | |
ImageId: "ami-d15a75c7" | |
InstanceType: "t2.micro" | |
SecurityGroups: | |
- !Ref SecurityGroupWorker | |
UserData: | |
Fn::Base64: | |
!Join ["\n", [ | |
"#!/bin/bash", | |
"service my-worker-service start" | |
]] | |
AutoScalingGroupAPI: | |
Type: AWS::AutoScaling::AutoScalingGroup | |
DependsOn: # Setting explicit dependencies will enforce the order in which AWS creates the resources when provisioning and deletes the | |
- SQSQueue # resources during deprovisioning. There are edge cases where CloudFormation fails to implicitly determine the correct order | |
- LoadBalancerAPI # and thus it is recommended that you always specify explicit dependencies. | |
Properties: | |
DesiredCapacity: 3 # We want 3 instances as the initial number in the autoscaling group. | |
LaunchConfigurationName: !Ref LaunchConfigurationAPI | |
LoadBalancerNames: | |
- !Ref LoadBalancerAPI | |
MaxSize: 4 # An autoscaling group may never have more instances than its maximum size, even during rolling updates. For that reason, it is | |
# recommended that the maximum size is always at least 1 greater than the desired capacity. | |
MinSize: 1 # The autoscaling group will aim to have at least this many instances in service at all time. Prevents dynamic scaledowns | |
# from bottlenecking resources. | |
VPCZoneIdentifier: # Specifies the subnets used by this autoscaling group. | |
- !Ref SubnetAPIZoneA | |
- !Ref SubnetAPIZoneB | |
UpdatePolicy: # Attaches a policy that describes how the group behaves during CloudFormation stack updates. | |
AutoScalingRollingUpdate: # The AutoScalingRollingUpdate policy describes that a rolling update must be performed. | |
MaxBatchSize: 1 # No more than this many instances may be updated at a time. | |
MinInstancesInService: 1 | |
PauseTime: PT1M # The amount of time to pause between two batch updates. PT1M means 1 minute. | |
AutoScalingGroupWorker: | |
Type: AWS::AutoScaling::AutoScalingGroup | |
DependsOn: | |
- SQSQueue | |
Properties: | |
DesiredCapacity: 3 | |
LaunchConfigurationName: !Ref LaunchConfigurationWorker | |
MaxSize: 4 | |
MinSize: 1 | |
VPCZoneIdentifier: | |
- !Ref SubnetAPIZoneA | |
- !Ref SubnetAPIZoneB | |
UpdatePolicy: | |
AutoScalingRollingUpdate: | |
MaxBatchSize: 1 | |
MinInstancesInService: 1 | |
PauseTime: PT1M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment