Mr. X has been working on a very interesting, and meticulously written web application involving some heavy computation and a complete test suite. He architects the system well and chooses to host his application over AWS.
He uses AWS RDS for his database, AWS EC2 to serve his application and AWS Lambda to do the heavy calculations. He also Dockerized his entire application in order to seamlessly build and deploy his work at his will.
Everything was set, the app was working amazingly, so he scheduled a demo with a potential client. He was making some last-minute changes and everything was still working well. So, right before the demo, it was time for deployment.
Mr. X deployed the Lambda application quickly and proceeded to rebuild his Docker images. He pulled the code on his EC2 machine but forgot to rebuild his Docker images. Instead, on the spur of the moment, he simply restarted his containers and proceeded for the demo. It takes no guesswork to learn that the demo didn’t go well.
Every other developer has a similar story to share at some point in time. But could this be avoided? The answer is yes! Could deployment be automated across all your AWS services at once so that your whole application gets deployed in one go? Again, yes!
AWS CloudFormation makes it possible.
Understanding CloudFormation: Stacks and Templates
AWS CloudFormation is a service that allows developers and businesses to create a collection of their AWS and third party resources in an easy way and provision them. AWS CloudFormation helps in automating and simplifying repeated tasks like creating groups of all the related resources that are used in your applications.
Basically, AWS CloudFormation makes creating and interconnecting all the resources that are needed by your application as simple as creating a single EC2 or RDS instance.
So clearly, CloudFormation can help us in automated deployment across all your AWS services at once so your whole application can get deployed in one go. Now let’s understand what exactly it does and how. We will keep it brief for the scope of this article.
CloudFormation essentially “stacks” up your entire application based on the AWS services you’re using. A stack is a collection of your AWS services. In the case of Mr. X from our case study, his stack would comprise of EC2, Lambda, and RDS.
CloudFormation manages a deployment configuration against each service you are using. This configuration is written in a file called a template. It can be written in either YAML or JSON. A template file consists of the individual configuration of all the AWS services, within a single file.
Deploying a Stack
Before we start writing the template, we need to see what we should achieve after creating them. In order to do that, login to your AWS console and search for CloudFormation under “Find services”.
Select CloudFormation and click on “Create stack” as shown below.
On the next screen, let’s go with “Template is ready” for now. The “Create template in Designer” option is out of scope for this article, but it offers a GUI to create templates.
Also let’s select “Upload a template file” under Specify template.
As you can see, templates can be hosted on S3 as well.
Let’s first see what happens when a template file is uploaded.
As shown in the image above, you need to set a stack name, and then enter parameters for each resource that you are going to use/create. In this example, a MySQL database is being created.
Let’s click “Next”. Now a Stack options page will be shown which is used to set advanced options for a stack. We can discuss them in subsequent articles. Let’s click “Next” again.
This is the final screen to review the stack before it is actually created, built, and deployed. This page has a “Create stack” button, let’s hit it to create the stack.
Finally, the stack creation and deployment is in progress. Once you refresh the stack, you’ll be able to pull in the latest state of the stack. If all goes well, the status should change to green CREATE_COMPLETE text with a green checkmark. Now let’s see how to make a template in order to configure a stack.
Writing a Template
Before we start writing templates, let’s look at the elements of an AWS CloudFormation template:
Elements of an AWS CloudFormation template
AWS CloudFormation templates are text files that can be written in JSON or YAML. These files generally comprise of five types of elements:
A template file format version number
Let’s first select the services that we need to configure. Let’s assume we need to set up two EC2 instances (one with the application, and the other with the database).
Let’s assume that we have a Python application communicating with MySQL database. Template configuration for an EC2 instance can
look like this:
And the template configuration for RDS can as as follows:
Resources:
Ec2Instance:
Type: ‘AWS::EC2::Instance’
Properties:
SecurityGroups:
– !Ref InstanceSecurityGroup
– MyExistingSecurityGroup
KeyName: mykey
ImageId: ami-7a11e213
InstanceSecurityGroup:
Type: ‘AWS::EC2::SecurityGroup’
Properties:
GroupDescription: Enable SSH access via port 22
SecurityGroupIngress:
– IpProtocol: tcp
FromPort: ’22’
ToPort: ’22’
CidrIp: 0.0.0.0/0
And the template configuration for RDS can as as follows:
Parameters:
DBInstanceID:
Default: mydbinstance
Description: My database instance
Type: String
MinLength: ‘1’
MaxLength: ’63’
AllowedPattern: ‘[a-zA-Z][a-zA-Z0-9]*’
ConstraintDescription: >-
( You have to begin with a letter and it should not end with a hyphen or double hyphen)
DBName:
Default: mydb
Description: My database
Type: String
MinLength: ‘1’
MaxLength: ’64’
AllowedPattern: ‘[a-zA-Z][a-zA-Z0-9]*’
ConstraintDescription: ( You must begin with a letter and the rest should be alphanumeric characters.)
DBInstanceClass:
Default: db.m5.large
Description: DB instance class
Type: String
ConstraintDescription: Must select a valid DB instance type.
DBAllocatedStorage:
Default: ’50’
Description: The size of the database (GiB)
Type: Number
MinValue: ‘5’
MaxValue: ‘1024’
ConstraintDescription: must be between 20 and 65536 GiB.
DBUsername:
NoEcho: ‘true’
Description: Username for MySQL database access
Type: String
MinLength: ‘1’
MaxLength: ’16’
AllowedPattern: ‘[a-zA-Z][a-zA-Z0-9]*’
ConstraintDescription: ( You must begin with a letter and the rest should be alphanumeric characters.)
DBPassword:
NoEcho: ‘true’
Description: Password MySQL database access
Type: String
MinLength: ‘8’
MaxLength: ’41’
AllowedPattern: ‘[a-zA-Z0-9]*’
ConstraintDescription: (You can only use alphanumeric characters.)
Resources:
MyDB:
Type: ‘AWS::RDS::DBInstance’
Properties:
DBInstanceIdentifier: !Ref DBInstanceID
DBName: !Ref DBName
DBInstanceClass: !Ref DBInstanceClass
AllocatedStorage: !Ref DBAllocatedStorage
Engine: MySQL
EngineVersion: 8.0.16
MasterUsername: !Ref DBUsername
MasterUserPassword: !Ref DBPassword
MonitoringInterval: ’60’
MonitoringRoleArn: ‘arn:aws:iam::123456789012:role/rds-monitoring-role’
Hope That Helps
And that’s about it! To sum up, we create a template file, we specify our resources and parameters according to our requirements that enable the resources, then we use this template to create a CloudFormation stack, which in turn auto-provisions these resources to us. The next time you make a deployment and you’re still not using CloudFormation, do take a moment to think about Mr.X.