UPDATE 2022/8/12: Check out the improved CloudFormation Fundamentals Introductory Course.

If are using AWS and want to automate creating resources you should look into AWS CloudFormation.

What is CloudFormation?

The easiest way to describe what CloudFormation is that it is a tool from AWS that allows you to spin up resources effortlessly. You define all the resources you want AWS to spin up in a blueprint document, click a button, and then AWS magically creates it all. This blueprint is called a template in CloudFormation speak.

CloudFormation makes sure that dependent resources in your template are all created in the proper order. For example, let’s say we want to create a DNS Route53 record and an EC2 instance having the DNS record point to the EC2 instance. CloudFormation will take care to provision the EC2 instance first, wait for that to be ready, and then create the DNS record afterward. AWS CloudFormation “orchestrates” the provisioning of the desired resources.

So instead of having to write a script with a bunch of AWS API calls, wait loops, and retry logic, you just tell describe what you want and tell CloudFormation to do it for you. Beautiful.

Basic CloudFormation Example

Let’s go through a simple example of launching a CloudFormation stack. We are going to spin up an EC2 instance and a Security Group. Then we’ll ssh into the server to confirm. Going through this tutorial will require your own AWS account and cost you a few pennies.

Instead of starting with an empty CloudFormation template, grab a starter template from the AWS Documentation. The simple one we want is Amazon EC2 instance in a security group. As of this writing, the example template provided is in JSON form but I prefer using YAML. So I’ve taken the example template and have converted it to YAML. It is available on GitHub: tongueroo/cloudformation-examples/templates/single-instance.yml. The commands to download and convert the JSON template to YAML is:

$ mkdir templates
$ cd templates
$ curl -o single-instance.json "https://s3-us-west-2.amazonaws.com/cloudformation-templates-us-west-2/EC2InstanceWithSecurityGroupSample.template"
$ ruby -ryaml -rjson -e 'puts YAML.dump(JSON.load(ARGF))' < single-instance.json > single-instance.yml

Let’s study the template. At the top level of the template there are 6 properties. To inspect the template, I’ll use the JSON version of the template since jq makes inspecting the top level keys easy:

$ cat single-instance.json | jq -r 'keys[]'

Here’s a short explanation of what each means with the important ones bolded:

  • AWSTemplateFormatVersion: Specifies the AWS CloudFormation template version.
  • Description: A text string that describes the template.
  • Mappings: A mapping of keys and associated values that you can use to specify conditional parameter values. This is CloudFormation’s version of a “case” statement.
  • Outputs: Describes the values that are returned whenever you view your stack’s properties. This gets displayed in the AWS CloudFormation Console.
  • **Parameters: **Specifies values that you can pass into your template at runtime.
  • *Resources: **Specifies the stack resources and their properties, like our EC2 instance. This is the *only required property.

The most important top-level properties of a CloudFormation template are Parameters and Resources. The resources section is where our EC2 instance is defined. Let’s look at the EC2Instance resource more closely: templates/single-instance.yml#L361-L378 It is re-pasted here for convenience:

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType:
        Ref: InstanceType
      SecurityGroups:
      - Ref: InstanceSecurityGroup
      KeyName:
        Ref: KeyName
      ImageId:
        Fn::FindInMap:
        - AWSRegionArch2AMI
        - Ref: AWS::Region
        - Fn::FindInMap:
          - AWSInstanceType2Arch
          - Ref: InstanceType
          - Arch

This example nicely corresponds with the AWS CloudFormation Template Reference and AWS Resource Types documentation on AWS::EC2::Instance. The reference documentation is going to be your best friend once you get the hang of CloudFormation.

This EC2Instance resource demonstrates a couple of uses of Ref. Ref is a way to reference values from other parts of the template. For example, Ref: InstanceSecurityGroup refers to the only other resource in this template, the SecurityGroup to be created. Here’s the definition of that resource:

  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable SSH access via port 22
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp:
          Ref: SSHLocation

Ref: InstanceType also refers to the InstanceType parameter that can be passed in. The Parameters top-level section is where the InstanceType parameter comes from. Let’s take a look at that part of the Parameters section.

Parameters:
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t2.small
    AllowedValues:
    - t1.micro
    - t2.nano
    - t2.micro
    - t2.small
    - t2.medium

You can see that the default parameter for the EC2 Instance type to launch is t2.small. You can override this value when you launch the instance if you would like. For parameters with default values, you do not need to provide the parameter. For parameters without default values, you will need to provide the parameter. In this specific template, the only required parameter is the KeyName. The KeyName is the ssh key used to access the instance. You can create an ssh key with the EC2 Console menu under “Key Pairs”. For the purpose of this tutorial, I’ve created a tutorial keypair and will use that.

Launching the Stack

After all that explanation, let’s finally launch the stack!

$ aws cloudformation create-stack --template-body file://templates/single-instance.yml --stack-name single-instance --parameters ParameterKey=KeyName,ParameterValue=tutorial ParameterKey=InstanceType,ParameterValue=t2.micro

Upon successfully launching the CloudFormation stack you will see output similar to this:

{
    "StackId": "arn:aws:cloudformation:us-west-2:1606191131234:stack/single-instance/3401e900-3d83-11e7-bb7e-503f2a2cee4a"
}

To check on the status of the newly launch stack, you can use the AWS CloudFormation console and click on the Events Tab after selecting the stack name. Here’s what it looks like.

You can see that the EC2 Instance and the EC2 Security group were created in about a minute. Next, let’s find the running instance so we can grab the Public DNS to log in. We can find that on the EC2 Console.

Use the DNS info to now ssh into the instance.

$ ssh -i ~/.ssh/tutorial.pem ec2-user@ec2–54–71–197–155.us-west-2.compute.amazonaws.com
$ uptime

You have successfully launched an EC2 instance with CloudFormation and ssh into it!

Cleanup

Let’s destroy the resources so you do not get charged more money than you have to. This is super simple.

aws cloudformation delete-stack --stack-name single-instance

Summary

There is so much more you can do with CloudFormation than what was done in this post. This tutorial was designed to be basic intentionally since it is a simple introduction to CloudFormation.

CloudFormation is known for it’s steep learning curve. Hopefully you can see from this post that the steep learning curve is not actually CloudFormation itself but actually learning all the different resources themselves that you are able to spin up with CloudFormation. It’s like learning a programming language. The programming language itself is not that difficult to learn, it’s the standard libraries, the external libraries, the ecosystem that takes a while to learn and get fluent in.

There are other tools in this provisioning orchestration space like Terraform also. I’ve come to really enjoy using CloudFormation because of it’s stateless nature, automatic rollbacks and cleanup, events logging and helpful AWS support. Hope this post was helpful for everyone!

In the next post, I will talk about how to add a Route53 record resource to the template: A Simple Introduction to AWS CloudFormation Part 2: EC2 Instance and Route53.

Posts in This Series

  1. A Simple Introduction to AWS CloudFormation Part 1: EC2 Instance
  2. A Simple Introduction to AWS CloudFormation Part 2: EC2 Instance and Route53
  3. A Simple Introduction to AWS CloudFormation Part 3: Updating a Stack
  4. A Simple Introduction to AWS CloudFormation Part 4: Change Sets = Dry Run Mode

You may also be interested in the Lono CloudFormation Framework Introduction Series.