What is CloudFormation
- CloudFormation is a declarative way of outlining your AWS Infrastructure, for any resources (most of them are supported).
- For example, within a CloudFormation template, you say
- I want a security group
- I want two EC2 machines using this security group
- I want two Elastic IPs for these EC2 machines
- I want an S3 bucket
- I want a load balancer (ELB) in front of these machines
- Then CloudFormation creates those for you, in the right order, with the exact configuration that you specify.
Benefits of AWS CloudFormation
- Infrastructure as code
- No resources are manually created, which is excellent for control
- The code can be version controlled for example using git
- Changes to the infrastructure are reviewed through code
- Cost
- Each resources within the stack is stagged with an identifier so you can easily see how much a stack costs you
- You can estimate the costs of your resources using the CloudFormation template
- Savings strategy: In Dev, you could automation deletion of templates at 5 Pm and recreated at 8 Am, safely
- Productivity
- Ability to destroy and re-create an infrastructure on the cloud on the fly
- Automated generation of Diagram for your templates!
- Declarative programming (no need to figure out ordering and orchestration)
- Separation of concern: create many stacks for many apps, and many layers. Ex:
- VPC stacks
- Network stacks
- App stacks
- Don’t re-invent the wheel
- Leverage existing templates on the web!
- Leverage the documentation
How CloudFormation Works
- Templates have to be uploaded in S3 and then referenced in CloudFormation
- To update a template, we can’t edit previous ones. We have to re-upload a new version of the template to AWS
- Stacks are identified by a name
- Deleting a stack deletes every single artifact that was created by CloudFormation.
Deploying CloudFormation templates
- Manual way:
- Editing templates in the CloudFormation Designer
- Using the console to input parameters, etc
- Automated way:
- Editing templates in aYAML file
- Using the AWS CLI (Command Line Interface) to deploy the templates
- Recommended way when you fully want to automate your flow
CloudFormation Building Blocks
- Templates components (one course section for each):
- Resources: your AWS resources declared in the template (MANDATORY)
- Parameters: the dynamic inputs for your template
- Mappings:the static variables for your template
- Outputs: References to what has been created
- Conditionals: List of conditions to perform resource creation
- Metadata
- Templates helpers:
- References
- Functions
Consider such an yaml file
1 2 3 4 5 6 7 8 |
--- Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: AvailabilityZone: eu-central-1a ImageId: ami-00a205cb8e06c3c4e InstanceType: t2.micro |
Create a stack in North Virginia:
CloudFormation -> Stacks -> Create stack
Next -> Create stack
At the EC2 -> Instances we can see that our EC2 instance has been created.
Update and delete the stack
Now, let’s update the stack.
CloudFormation -> Stacks -> MyFirstCloudFormationTemplate -> Update stack -> Upload a temple file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
--- Parameters: SecurityGroupDescription: Description: Security Group Description Type: String Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: AvailabilityZone: eu-central-1a ImageId: ami-00a205cb8e06c3c4e InstanceType: t2.micro SecurityGroups: - !Ref SSHSecurityGroup - !Ref ServerSecurityGroup # an elastic IP for our instance MyEIP: Type: AWS::EC2::EIP Properties: InstanceId: !Ref MyEC2Instance # our EC2 security group SSHSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable SSH access via port 22 SecurityGroupIngress: - CidrIp: 0.0.0.0/0 FromPort: 22 IpProtocol: tcp ToPort: 22 # our second EC2 security group ServerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Ref SecurityGroupDescription SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: 192.168.1.1/32 |
Next -> Next -> Update Stack
EC2 instance has been updated. Elastic IP and security group has been created.
Now, we want to get rid off all resources we created. So we need to delete the stack.
CloudFormation -> Stacks -> MyFirstCloudFormationTemplate -> Delete -> Delete stack
Parameters.
What are parameters?
- Parameters are a way to provide inputs to your AWS CloudFormation template
- They’re important to know about if:
- You want to reuse your templates across the company
- Some inputs can not be determined ahead of time
- Parameters are extremely powerful, controlled, and can prevent errors from happening in your templates thanks to types.
When should you use a parameter?
- Ask yourself this:
- Is this Cloud Formation resource configuration likely to change in the future?
- If so, make it a parameter.
- You won’t have to re-upload a template to change its content
1 2 3 4 |
Parameters: SecurityGroupDescription: Description: Security Group Description (Simple parameter) Type: String |
Parameters Settings
Parameters can be controlled by all these settings:
Type:
String
Number
CommaDelimitedList
List<Type>
AWS Parameter (to help catch invalid values — match against existing values in the AWS Account)
Description
Constraints
ConstraintDescription (String)
Min/MaxLength
Min/MaxValue
Defaults
AllowedValues (array)
AllowedPattern (regexp)
NoEcho (Boolean)
How to Reference a Parameter
- The
Fn::Ref
function can be leveraged to reference parameters - Parameters can be used anywhere in a template.
- The shorthand for this in YAML is !Ref
- The function can also reference other elements within the template
1 2 3 4 |
DbSubnetl: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC |
Concept: Pseudo Parameters
- AWS offers us pseudo parameters in any CloudFormation template.
- These can be used at any time and are enabled by default
Reference Value | Example Return Value |
AWS::AccountId |
1234567890 |
AWS::NotificationARNs |
[arn:aws:sns:us-east-1:123456789012:MyTopic] |
AWS::NoValue |
Does not return a value. |
AWS::Region |
us-east-2 |
AWS::StackId |
arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/lc2fa62 0-982a-11e3-aff7-50e2416294e0 |
AWS::StackName |
MyStack |
Resources
- Resources are the core of your CloudFormation template (MANDATORY)
- They represent the different AWS Components that will be created and configured
- Resources are declared and can reference each other
- AWS figures out creation, updates and deletes of resources for us
- There are over 224 types of resources (!)
- Resource types identifiers are of the form:
1 |
AWS::aws-product-name::data-type-name |
- All the resources can be found here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
- Example here (for an EC2 instance): https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html
Mappings
- Mappings are fixed variables within your CloudFormationTemplate.
- They’re very handy to differentiate between different environments (dev vs prod), regions (AWS regions), AMI types, etc
- All the values are hardcoded within the template
- Example:
1 2 3 4 5 6 7 8 |
Mappings: Mapping01: Key01: Name: Value01 Key02: Name: Value02 Key03: Name: Value03 |
1 2 3 4 5 6 7 8 9 10 |
RegionMap: us-east-1: "32": "ami-6411e20d" "64": "ami-7a11e213" us-west-1: "32": "ami-c9c7978c" "64": "ami-cfc7978a" eu-west-1: "32": "ami-37c2f643" "64": "ami-31c2f645" |
When would you use mappings vs parameters ?
- Mappings are great when you know in advance all the values that can be taken and that they can be deduced from variables such as
- Region
- Availability zone
- AWS Account
- Environment (dev vs prod)
- Etc …
- They allow safer control over the template.
- Use parameters when the values are really user specific
Fn::FindlnMap
Accessing Mapping Values
- We use
Fn::FindInMap
to return a named value from a specific key FindInMap [ MapName, TopLevelKey, SecondLevelKey ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
AWSTemplateFormatVersion: "2010-09-09" Mappings: RegionMap: us-east-1: "32": "ami-6411e20d" "64": "ami-7a11e213" us-west-1: "32": "ami-c9c7978c" "64": "ami-cfc7978a" eu-west-1: "32": "ami-37c2f643" "64": "ami-31c2f645" ap-southeast-1: "32": "ami-66f28c34" "64": "ami-60f28c32" ap-northeast-1: "32": "ami-9c03a89d" "64": "ami-a003a8a1" Resources: myEC2Instance: Type: "AWS::EC2::Instance" Properties: ImageId: !FindInMap [ RegionMap, !Ref "AWS::Region", 32 ] InstanceType: m1.small |
Outputs
- The Outputs section declares optional outputs values that we can import into other stacks (if you export them first)!
- You can also view the outputs in the AWS Console or in using the AWS CLI
- They’re very useful for example if you define a network CloudFormation, and output the variables such as VPC ID and your Subnet IDs
- It’s the best way to perform some collaboration cross stack, as you let expert handle their own part of the stack
- You can’t delete a CloudFormation Stack if its outputs are being referenced by another CloudFormation stack
Outputs Example
- Creating a SSH Security Group as part of one template
- We create an output that references that security group
1 2 3 4 5 6 |
Outputs: StackSSHSecurityGroup: Description: The SSH Security Group for our Company Value: !Ref MyCompanyWideSSHSecurityGroup Export: Name: SSHSecurityGroup |
Cross Stack Reference
- We then create a second template that leverages that security group
- For this, we use the
Fn::ImportValue
function - You can’t delete the underlying stack until all the references are deleted too.
1 2 3 4 5 6 7 8 9 |
Resources: MySecureInstance: Type: AWS::EC2::Instance Properties: AvailabilityZone: us-east-la Imageld: ami-a4c7edb2 InstanceType: t2.micro SecurityGroups: - !ImportValue SSHSecurityGroup |
Conditions
- Conditions are used to control the creation of resources or outputs based on a condition.
- Conditions can be whatever you want them to be, but common ones are:
- Environment (dev / test / prod)
- AWS Region
- Any parameter value
- Each condition can reference another condition, parameter value or mapping
How to define a condition?
1 2 |
Conditions: CreateProdResources: !Equals [ !Ref EnvType, prod ] |
- The logical ID is for you to choose. It’s how you name condition
- The intrinsic function (logical) can be any of the following:
Fn::And
Fn::Equals
Fn::If
Fn::Not
Fn::Or
Using a Condition
- Conditions can be applied to resources / outputs / etc…
1 2 3 4 |
Resources: MountPoint: Type: "AWS::EC2::VolumeAttachment" Condition: CreateProdResources |
Stack status codes:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html
Troubleshooting:
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/troubleshooting.html