AWS CloudFormation Basics and Tutorial

In this article, I will be going over the basics of CloudFormation and then demonstrate the benefits using an example.

This article is divided into the following main sections:

  • What is AWS CloudFormation?
  • AWS CloudFormation Basics
  • AWS CloudFormation Example

What is AWS CloudFormation?

AWS CloudFormation is a service that helps you model and set up your Amazon Web Services resources so that you can spend less time managing those resources and more time focusing on your applications that run in AWS. You create a template that describes all the AWS resources that you want (like Amazon EC2 instances or Amazon RDS DB instances), and AWS CloudFormation takes care of provisioning and configuring those resources for you.

For being able to build a cost-effective, scalable, and reliable infrastructure on the AWS platform, I recommend the AWS Fundamentals for System Administrators Course.

Benefits of AWS CloudFormation:

  • Simplify Infrastructure Management
  • Quickly Replicate Your Infrastructure
  • Easily Control and Track Changes to Your Infrastructure

AWS CloudFormation Basics:

In CloudFormation, you work with Templates and Stacks.

Template: JSON or YAML formatted text file which is a blueprint of your AWS resources.

Stack: In CloudFormation, you manage resources as a single unit called a Stack. All the resources in a stack are defined by the stack’s Template.

Hence the AWS CloudFormation workflow can be summarized using the image here.

AWS CloudFormation Fundamentals

Template:

A template is a JSON or YAML formatted text file that describes what resources are contained in the Stack. It contains information about each resource, its configuration and how it may be connected or dependent on other resources.

A template can be developed using two methods:

1. UI Designer: You can diagram your template resources using a drag-and-drop interface, and then edit their details using the integrated JSON/YAML editor.

2. Script: Directly scripting the template in JSON or YAML format.

As shown in the screenshot below, the designer and the editor are located side-by-side on the Console, and hence you are able to visualize your changes/updates as you make them.

AWS CloudFormation Fundamentals

Following are the major components of the template:

  • Description: Enables you to include arbitrary comments about your template. (Optional)
  • Parameters: Parameters enable you to input custom values to your template each time you create or update a stack. (Optional)
  • Mappings: Collection of Key-Value pairs which can be used to set values. (Optional)
  • Resources: Declares the AWS resources that you want to include in the stack. (Required)
  • Outputs: Declares output values that you can import into other stacks, return in response, or view on the AWS CloudFormation console. (Optional)

AWS CloudFormation Example:

AWS CloudFormation can be used to set up and deploy very complex stacks very quickly, saving you hours of manual work and ensuring repeatability. In this section, I will create a simple EC2 instance and a Security Group, and assign the Security Group to the instance. The sample template to create a configuration like this (also shown in the picture below), is also uploaded at aws-cloudformation-basic-example.template.

Once the stack is deployed, it will have all the resources provisioned. You can access the instance via SSH and do further configuration. As an option, you can also install Apps, create files, etc and do any custom configuration of the EC2 instances using the CloudFormation template, as described in the sections below:

AWS CloudFormation Fundamentals

Configuring your instances:

CloudFormation can also be used to automatically configure the instances once they are deployed in the stack. You can run scripts, download and install applications, create files, etc.

To demonstrate this, here I have an example to install and start a httpd server on the EC2 instance deployed in the previous example.

To configure an EC2 instance, you can enter the commands you want to execute on launch in the resource’s Properties’ UserData section as below. The full template for this example is aws-cloudformation-http-install-userdata-only.template.

"Resources" : {
"WebServerInstance": {
"Type" : "AWS::EC2::Instance",
"Properties": {
"ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" }, { "Fn::FindInMap" : ["AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
"InstanceType" : { "Ref" : "InstanceType" },
"SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
"KeyName" : { "Ref" : "KeyName" },
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash\n",
"yum update -y\n",
"mkdir /tmp\n",
"mkdir /tmp/amol\n",
"yum install httpd php php-mysql stress -y\n",
"echo '<html><title>Hello Amol Title</title><body><h1>header H1</h1></body></html>' > /var/www/html/index.html\n",
"echo 'i am healthy' > /var/www/html/healthy.html\n",
"service httpd start\n",
"chkconfig httpd on\n"
]]}}
}
}
}

A better and a recommended way to achieve the above functionality is by creating and using Config Sets. You can create a config set for every operation you want to perform on the instance. The benefit of this approach is that once the config set information gets stored in the stack’s metadata, it can be accessed even after the stack has been created. You can choose to create as many config sets as you want and may also choose to not run them at instance launch.

For example, you can have config sets to install/uninstall WordPress, say: install_wordpress, uninstall_wordpress:

  • You can then only call install_wordpress on instance startup to configure and install WordPress when the instance is provisioned on the stack.
  • If you wish to re-install WordPress again on this instance, you can refer to uninstall_wordpress, install_wordpress(using cfn-init script, described later) config sets in order to have a clean install after the instance are already up.

A config set may have multiple configs and you can call them in any order using multiple config sets. Say you have configs named “install”, “configure”, “deploy”, “teardown”. You can then have multiple config sets to achieve specific things as below:

"SetupConfigSet" : [ "install", "configure" ],"InitialDeployConfigSet" : [ "install", "configure", "deploy" ],"DeployOnlyConfigSet" : [ "deploy" ],"TeardownConfigSet" : ["teardown"],

For the purpose of demonstration, in the example here, I will create two config sets with one config each. Full template for this example is at aws-cloudformation-httpd-install-using-configset.template.

Here, I have created two config sets for our instance namely ‘InstallHttpd’ and ‘UninstallHttpd’ as below:

First of all, in the AWS::CloudFormation::Init section, define the config sets. Each config set InstallHttpd and UninstallHttpd have one config each.

"configSets" : {
"InstallHttpd" : [ "install_httpd" ],
"UninstallHttpd" : [ "uninstall_httpd" ]
}

Now, define the install_httpd config as below. This config has steps to install httpd server on the machine. It also creates the main HTML file and ensures that the httpd server starts on machine startup.

"install_httpd" : {
"packages" : {
"yum" : {
"httpd" : [],
"php" : [],
"php-mysql" : [],
"stress" : []
}
},
"files" : {
"/var/www/html/healthy.html" : {
"content" : "I am healthy"
},
"/var/www/html/index.html" : {
"content" : "<html><title>Hello Amol Title</title><body><h1>header H1</h1></body></html>"
}
},
"commands" : {
"httpd" : {
"command" : "chkconfig httpd on",
"ignoreErrors" : "false"
}
},
"services" : {
"sysvinit" : {
"httpd" : { "enabled" : "true", "ensureRunning" : "true" }
}
}
}

Then, define the uninstall_httpd config set as below. This config has steps to remove httpd server.

"uninstall_httpd" : {
"commands" : {
"httpd" : {
"command" : "rm -rf /var/www/html",
"ignoreErrors" : "false"
}
}
}

Once the configs and config sets are defined, on instance startup we need to execute the cfn-init script to call the InstallHttpd config set, which will install and configure the httpd server on the instance. cfn-init script can be used to execute any of the config sets at or after the instance launch.

"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"yum update aws-cfn-bootstrap\n",
"# Install the files and packages from the metadata\n",
"/opt/aws/bin/cfn-init ",
" –stack", {"Ref":"AWS::StackName" },
" –resource WebServerInstance ",
" –configsets InstallHttpd ",
" –region", {"Ref":"AWS::Region" }, "\n",

"# Signal the status from cfn-init\n",
"/opt/aws/bin/cfn-signal -e $? ",
" –stack", {"Ref":"AWS::StackName" },
" –resource WebServerInstance ",
" –region", {"Ref":"AWS::Region" }, "\n"
]
]
}
}

Full template for this example is aws-cloudformation-httpd-install-using-configset.template.

Once the stack is deployed, your console will look like in the screenshot below. As you can see, you have all the events, input parameters provided during stack creation, resources, and also the template script. In the outputs, I had added the link to the webpage hosted on the web server, so if you go to that link(or in this case, click on the URL generated), you will be navigated to the HTML page.

AWS CloudFormation Fundamentals

If you create a stack with this template, then this will install httpd and configure your web server on launch, as soon as the stack is created.

Now, if you want to uninstall httpd, then you can SSH on the instance and run the command (with root permissions):

/opt/aws/bin/cfn-init --stack [stack name/id] --resource WebServerInstance --configsets UninstallHttpd --region [region where stack is deployed]

To install and configure httpd again with the same launch config, run the command:

/opt/aws/bin/cfn-init --stack [stack name/id] --resource WebServerInstance --configsets InstallHttpd--region [region where stack is deployed]

You can see that happening in the instance below:

AWS CloudFormation Fundamentals

That’s it for now. Hope you enjoyed this post, and it was helpful to you! :-) Please feel free to let me know or post here if you may have any questions.

Look forward to waking up every day to an interesting challenge!