A Simple Introduction to AWS CloudFormation Part 1: EC2 Instance
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
- A Simple Introduction to AWS CloudFormation Part 1: EC2 Instance
- A Simple Introduction to AWS CloudFormation Part 2: EC2 Instance and Route53
- A Simple Introduction to AWS CloudFormation Part 3: Updating a Stack
- 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.
Thanks for reading this far. If you found this article useful, I'd really appreciate it if you share this article so others can find it too! Thanks 😁 Also follow me on Twitter.
Got questions? Check out BoltOps.
You might also like
More tools:
-
Kubes
Kubes: Kubernetes Deployment Tool
Kubes is a Kubernetes Deployment Tool. It builds the docker image, creates the Kubernetes YAML, and runs kubectl apply. It automates the deployment process and saves you precious finger-typing energy.
-
Jets
Jets: The Ruby Serverless Framework
Ruby on Jets allows you to create and deploy serverless services with ease, and to seamlessly glue AWS services together with the most beautiful dynamic language: Ruby. It includes everything you need to build an API and deploy it to AWS Lambda. Jets leverages the power of Ruby to make serverless joyful for everyone.
-
Lono
Lono: The CloudFormation Framework
Building infrastructure-as-code is challenging. Lono makes it much easier and fun. It includes everything you need to manage and deploy infrastructure-as-code.