Manage resources with AWS CloudFormation
Cloud Shaper
The cloud has changed where and how companies invest their IT budgets. Instead of spending money on data centers and servers, without really knowing how they will be used, the customer only pays for the computing resources needed. Infrastructure can thus be provisioned quickly then disabled when no longer needed, thus reducing costs.
The cloud also serves the ever more comprehensive automation of development processes advocated by the DevOps movement. Infrastructure as code is a way to simplify the development process, reducing the dependency of developers on infrastructure teams to provide the environment they require. To allow companies to benefit from the concept of infrastructure as code, Amazon Web Services (AWS) has developed the AWS CloudFormation service.
AWS CloudFormation templates describe IT resources and the related dependencies or run-time parameters necessary for applications to run. AWS CloudFormation handles time-consuming and error-prone work such as dependency management and the correct order of resource provisioning. Once the AWS resources are provisioned, companies can modify and update them, thus keeping full control of their cloud infrastructure. The service itself is free, meaning users pay only for the underlying AWS resources.
Stacks and Templates
As a first step, you need an AWS account [1]. Once you have an account, you can log in to the AWS console and choose the CloudFormation service, which you will find in the Deployment & Management service category.
Once there, you can access a template to create a stack. A template is a high-level description of the resources that you will be using and their mutual relationships. A stack is a collection of logically related resources described by a template and managed as a unit. In other words, you create, update, or delete a set of resources by creating, updating, or deleting a stack.
All AWS resources in a template are identified by logical names, which makes it possible to create many stacks from a template without naming collisions within the AWS resources. A template has the high-level JSON structure shown in Listing 1.
Listing 1: Template Structure
{ "Description" : "A text description for the template usage", "Parameters": { // A set of inputs used to customize the template per deployment }, "Resources" : { // The set of AWS resources and relationships between them }, "Outputs" : { // A set of values to be made visible to the stack creator }, "AWSTemplateFormatVersion" : "2010-09-09" }
The Stack
The sample template that I will be using for a basic WordPress blog uses a single Amazon Elastic Compute Cloud (EC2) instance and an Amazon Relational Database Service (RDS) instance. The template also creates an EC2 and RDS security group to define firewall settings for the EC2 and database instances.
As described earlier, the JSON template includes configuration information for the AWS resources I want to create in the stack. This specific sample template includes six top-level sections: AWSTemplateFormatVersion
, Description
, Parameters
, Mappings
, Resources
, and Outputs
; however, only Resources
is needed. The Resources
section includes the definitions of AWS resources I want to create using the template. Each resource is listed separately and specifies the properties that are required to create this particular resource. Listing 2 shows the configuration for the Amazon RDS database instance with the logical name DBInstance
.
Listing 2: Resource Declaration
"Resources" : { ... "DBInstance" : { "Type": "AWS::RDS::DBInstance", "Properties": { "DBName" : { "Ref" : "DBName" }, "Engine" : "MySQL", "MasterUsername" : { "Ref" : "DBUsername" }, "DBInstanceClass" : { "Ref" : "DBClass" }, "DBSecurityGroups" : [{ "Ref" : "DBSecurityGroup" }], "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" }, "MasterUserPassword": { "Ref" : "DBPassword" } } }, "DBSecurityGroup": { "Type": "AWS::RDS::DBSecurityGroup", "Properties": { "DBSecurityGroupIngress": { "EC2SecurityGroupName": { "Ref": "WebServerSecurityGroup"} }, "GroupDescription" : "Frontend Access" } }, ... },
If database instances have already been created, you will see properties such as Engine
, DBInstanceClass
, and AllocatedStorage
, which determine the configuration of the database instance. Resource declarations are an efficient way to specify all of these configuration settings at once. Once resource declarations have been added to a template, all the specified resources can be easily created and configured by using the template to create a stack. To provision the same configuration of resources, you only need to create a new stack that uses this sample template.
The parameters shown in Listing 3 are used in the template to specify the values used in the properties of the Amazon RDS database instance resource. The DBName
property specified by the DBName
parameter exists in the DBInstance
resource declaration:
"DBInstance" : { "Type": "AWS::RDS::DBInstance", "Properties": { "DBName" : { "Ref" : "DBName" }, ... } },
Listing 3: Database Instance Parameters
"Parameters" : { ... "DBName" : { "Default": "wordpress", "Description" : "The WordPress database name", "Type": "String", "MinLength": "1", "MaxLength": "64", "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." }, "DBUsername" : { "Default": "admin", "NoEcho": "true", "Description" : "The WordPress database admin account user name", "Type": "String", "MinLength": "1", "MaxLength": "16", "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." }, "DBPassword" : { "Default": "admin", "NoEcho": "true", "Description" : "The WordPress database admin account password", "Type": "String", "MinLength": "8", "MaxLength": "41", "AllowedPattern" : "[a-zA-Z0-9]*", "ConstraintDescription" : "must contain only alphanumeric characters." }, "DBAllocatedStorage" : { "Default": "5", "Description" : "The size of the database (Gb)", "Type": "Number", "MinValue": "5", "MaxValue": "1024", "ConstraintDescription" : "must be between 5 and 1024Gb." }, ... },
The brackets include a call to the Ref
function with DBName
as input. The Ref
function returns the value of the object to which it refers. In this case, Ref
sets the DBName
property to the value that was specified when creating the stack.
The Ref
function can also set a resource property to the value of another resource. For example, the DBInstance
resource declaration contains the following property declaration:
"DBInstance" : { "Type": "AWS::RDS::DBInstance", "Properties": { ... "DBSecurityGroups" : [{ "Ref" : "DBSecurityGroup" }], ... } },
The DBSecurityGroups
property takes a list of Amazon RDS database security groups. The Ref
function takes input from DBSecurityGroup
, which is the logical name of the database security group in the template, and adds the name of the DBSecurityGroup
to the DBSecurityGroups
property.
Creating a Stack
The stack is created on a CloudFormation template [2], which contains multiple AWS resources, including an RDS database instance and an EC2 instance. To create a WordPress stack, the following steps are necessary:
1. Log on to the AWS management console and open the AWS CloudFormation console [3].
2. If this is a new AWS CloudFormation account, click on Create New Stack (Figure 1); otherwise, press Create Stack.
3. Choose a name for the stack and type it without spaces in the appropriate field. For this example, I used MyWPTestStack.
4. Select Specify an Amazon S3 template URL in the Template section and add a URL for the WordPress sample template (in this case, https://s3.amazonaws.com/cloudformation-templates-us-east-1/WordPress_Single_Instance_With_RDS.template); click Next.
5. On the Specify Parameters page in the KeyName field, enter the name of a valid Amazon EC2 KeyPair from the same region in which you are creating the stack; click on Next.
6. On the Options Page, you can assign tags to your resources and set notifications. In this scenario I am not using tags, so I continued with Next.
7. Verify the information for the stack.
After you have finished the Create Stack wizard, AWS CloudFormation starts to create the resources you defined in the template. The new MyWPTestStack appears in the list in the upper part of the CloudFormation console with a status of CREATE_IN_PROGRESS. You can see the details in the Events history at the bottom of the console.
If AWS CloudFormation cannot make a resource, it reports CREATE_FAILED and resets the stack by default. All the resources you created previously are deleted. The problem that caused the error is shown in the Status Reason column. For example, if you set an invalid database password, you might see the following event for the AWS::RDS::DBInstance type resource:
2013-04-24 19:21 UTC-7 CREATE_FAILED AWS::RDS::DBInstance DBInstance \ The parameter MasterUserPassword is not a valid password because it is \ shorter than 8 characters.
If the stack has a status of CREATE_ COMPLETE, CloudFormation finishes the create process, and you can use the resource. The WordPress sample stack creates a WordPress website that you can start by running the WordPress installation script.
Deleting a Stack
Now that you have launched a stack with AWS CloudFormation, you can delete the stack and its resources once you are done with it. To do so, select MyWPTestStack from the console and click Delete Stack. The status changes to DELETE_IN_PROGRESS. In the Events tab, you can track the deletion and creation of stacks. Once the process is complete, CloudFormation clears the stack from the list.
Conclusions
In this article, I have shown you how to write a template and configure security groups that generate all the required AWS resources for running a WordPress blog on an EC2 instance with a MySQL database. I then described how you pass this template to CloudFormation, which the service then uses to create all the required resources, taking into account any dependencies.