What is CloudFormation & How to use it to deploy NodeJS
Imagine you have built a great product. The user base of your product has been increasing rapidly. You want to scale your product to the people around the world. To achieve that you should have a good cloud infrastructure.
But you’ll definitely be exhausted if you manage your cloud infrastructure manually? You would be wondering, “Well. How are the enterprise companies managing now?”.
They automate their cloud infrastructure management via code. Well, that’s exactly AWS CloudFormation offers — a way to manage your cloud infrastructure as code!
In this blog, we’ll explore the basics of AWS CloudFormation and how it can help you automate your infrastructure management. Let’s dive into the world of infrastructure as code!
What is CloudFormation
AWS CloudFormation is a service that helps you automate creating and managing your cloud resources. Imagine you’re building a house, and you want to ensure everything is in the right place — the walls, the roof, the doors, and the windows. With CloudFormation, you can create a blueprint for your house, and specify exactly what you want and where you want it.
Similarly, CloudFormation allows you to create a blueprint for your cloud infrastructure. You can specify what resources you want to create, (Eg. EC2 servers, databases, storage, etc.) and how they should be configured. CloudFormation takes care of creating and managing those resources for you automatically.
CloudFormation will be extremely helpful in few cases. Listing few of them below,
- Managing the infrastructure changes in multiple environments (Development, Staging, Production)
- Re-create the same infrastructure in a different region / account
- Unfortunate accidental deletion of a resource can be re-created with the exact configuration in seconds (not manually, as you have all configurations in your code)
The best part is that CloudFormation makes updating your infrastructure super simple and automatic. If you want to add a new resource, change a configuration, or delete a resource, you can update your blueprint, and CloudFormation will handle the changes for you.
How CloudFormation works
You may wonder how CloudFormation works? It’s simple, we’ll upload our CloudFormation templates in Amazon S3 behind the scenes and then CloudFormation will pull our template from the S3. One important point to note is we cannot edit the template once uploaded. We need to re-upload the updated template to AWS and then CloudFormation will compare and figure out what needs to be updated
One awesome feature is you can delete all resources created by CloudFormation in one click by deleting the stack. CloudFormation stack is nothing but a collection of AWS resources that you can manage as a single unit. You define a stack by creating a CloudFormation template that describes the resources you want to create, and then you create the stack by running the template
Deploying CloudFormation Templates
We can deploy the CloudFormation template in two ways. One is using CloudFormation Designer and the other is writing the code in a YAML file. If you’re not familiar with YAML then you can go for CloudFormation Designer. It’s also recommended for those who don’t know to code. It allows you to create and edit templates graphically, making it easier to visualize your infrastructure and simplify the template creation process. However, in this blog we’ll be writing YAML code to deploy our app.
How to create CloudFormation Template to deploy NodeJS
CloudFormation Template can be created using YAML or JSON file but we’re going to use YAML file. In this template, we’ll be creating an EC2 instance, security group and add a script to deploy a simple NodeJS app.
CloudFormation Template to create EC2 instance
There are over 224 types of resources but now we need to create EC2 resource. Resources represent the different AWS Components that will be created and configured. The Resource types identifiers are in below form.
AWS::aws-product-name::data-type-name
So for the EC2 instance, the resource is Aws::EC2::Instance
. To learn more about AWS resources and syntax check out the AWS official documentation and play with it. If you look at EC2 documentation and scroll down you can see how to declare EC2 instance. Both JSON and YAML syntax is available but we'll stick with YAML for this blog. There will be lot of properties available to customize the creation of EC2 instances. But for now, we'll be configuring AvailabilityZone, ImageId and InstanceType which are very basic properties to create an EC2 instance.
Resources:
SampleNodejsDeploy:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: us-east-1a
ImageId: ami-a4c7edb2
InstanceType: t2.micro
Here SampleNodejsDeploy
refers to the name of the block. You can name your resource as your wish.
Let’s see the process to deploy the NodeJS app.
How to deploy NodeJS App using CloudFormation template
We’re going to deploy the NodeJS app using the UserData
property in the EC2 resource. For people who don't know about EC2 user data, it is a feature of AWS EC2 which allows us to pass information during the launch of the EC2 instance. It can be used to perform custom actions, such as installing software and executing the script. Let's write the bash script to deploy NodeJS app and attach it to the user data.
Here is the simple script to deploy the NodeJS app
#!/bin/bash
set -e
curl -sL https://deb.nodesource.com/setup_16.x | bash -
sudo apt install nodejs
node -v
npm -v
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
yarn --version
sudo -i -u ubuntu bash << EOF
set -e
cd /home/ubuntu
sudo npm install -g pm2
git clone https://github.com/5minslearn/node_with_docker.git
cd node_with_docker
yarn install
pm2 start yarn --time --interpreter bash --name sample_node -- start -p 8000
EOF
The above script installs the nodejs, yarn and pm2. Clones the NodeJS project from git, installs the dependencies and starts the app with PM2.
Having this script, let’s attach it to the CloudFormation template.
Attaching UserData into CloudFormation Template
Resources:
SampleNodejsDeploy:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-014d05e6b24240371
UserData:
Fn::Base64:
|
#!/bin/bash
set -e
curl -sL https://deb.nodesource.com/setup_16.x | bash -
sudo apt install nodejs
node -v
npm -v
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
yarn --version
sudo -i -u ubuntu bash << EOF
set -e
cd /home/ubuntu
sudo npm install -g pm2
git clone https://github.com/5minslearn/node_with_docker.git
cd node_with_docker
yarn install
pm2 start yarn --time --interpreter bash --name sample_node -- start -p 8000
EOF
You can notice the UserData
property is added to the EC2 block. Fn::Base64
is a function in AWS CloudFormation that allows users to encode a string to base64 format. This function can be used to pass sensitive information, such as credentials, to AWS resources in a secure manner. Since EC2 user data is not encrypted it's always best practice to encode it. Right below the line, you can notice a small vertical bar (|
). It is used for multi-line string support as our script is more than 1 line.
Alright. Now we have a script to deploy the NodeJS. But, we have to remember one super important item. By default NodeJS applications run on port 8000. We should expose the port 8000 from EC2. Here comes the need to create a security group configuration for our EC2 instance.
How to create a security group using CloudFormation Template
It’s exactly similar to creating an EC2 instance, except replacing the type from Instance
to SecurityGroup
.
SampleNodejsDeploySG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for the app nodes that allow ssh, http, 8000
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '8000'
ToPort: '8000'
CidrIp: 0.0.0.0/0
The above code is self explanatory that we defined a Security group, allowing ports 22(SSH port), 80(HTTP port), 8000 (for NodeJS). We named the Resource as SampleNodejsDeploySG
.
Link Security Group to EC2
You may encounter an obvious question, “We’ve created a template for creating Security group but how will this be linked to EC2 instance?”.
The solution is simple, CloudFormation provides an intrinsic function called !Ref
that allows us to reference a resource or parameter within a CloudFormation template.
Resources:
SampleNodejsDeploy:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-014d05e6b24240371
SecurityGroups:
- !Ref SampleNodejsDeploySG
UserData:
Fn::Base64:
|
#!/bin/bash
set -e
curl -sL https://deb.nodesource.com/setup_16.x | bash -
sudo apt install nodejs
node -v
npm -v
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
yarn --version
sudo -i -u ubuntu bash << EOF
set -e
cd /home/ubuntu
sudo npm install -g pm2
git clone https://github.com/5minslearn/node_with_docker.git
cd node_with_docker
yarn install
pm2 start yarn --time --interpreter bash --name sample_node -- start -p 8000
EOF
SampleNodejsDeploySG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for the app nodes that allow ssh, http
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '8000'
ToPort: '8000'
CidrIp: 0.0.0.0/
You can notice the SecurityGroups
property is added to the EC2 instance and the created Security Group configuration is linked to the EC2 instance by using the !Ref
parameter. Now we have the CloudFormation template. But we're not yet complete. We still miss one more thing. Can you figure it out? We created an EC2 instance, and we allowed an SSH port but to log in using SSH we need to attach a key-value pair right? Let's do that.
We can attach Key-value pair name directly into the template. For example, let’s say your key-value pair name is 5minslearn
you can attach the property KeyName
directly to the EC2 resource block like this or we can use parameters.
Resources:
SampleNodejsDeploy:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-014d05e6b24240371
KeyName: 5minslearn
SecurityGroups:
- !Ref SampleNodejsDeployS
How to use parameters in the CloudFormation template
We can use parameters to get the name of the key-value pair from the user while creating the stack. Basically, parameters allow us to pass input values into CloudFormation templates at runtime. Let’s see how to do that
Parameters:
SSHKey:
Type: AWS::EC2::KeyPair::KeyName
Description: name of the key pair to ssh into the instance
Resources:
SampleNodejsDeploy:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
ImageId: ami-014d05e6b24240371
KeyName: !Ref SSHKey
SecurityGroups:
- !Ref SampleNodejsDeploySG
UserData:
Fn::Base64:
|
#!/bin/bash
set -e
curl -sL https://deb.nodesource.com/setup_16.x | bash -
sudo apt install nodejs
node -v
npm -v
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn
yarn --version
sudo -i -u ubuntu bash << EOF
set -e
cd /home/ubuntu
sudo npm install -g pm2
git clone https://github.com/5minslearn/node_with_docker.git
cd node_with_docker
yarn install
pm2 start yarn --time --interpreter bash --name sample_node -- start -p 8000
EOF
SampleNodejsDeploySG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: for the app nodes that allow ssh, http
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '8000'
ToPort: '8000'
CidrIp: 0.0.0.0/0
In the above template, we added a parameter to get the key-pair name and referenced it to KeyName
property.
Great! We successfully created a CloudFormation template to create an EC2 instance and security group. In addition to that, we also added a script to deploy the NodeJS app. Now it’s time to create a CloudFormation stack.
How to create CloudFormation stack
The first step is to log in to the AWS console and search for CloudFormation in the search bar. You can see the below page. Click on stacks in the left sidebar to get started with CloudFormation.
Click on create stack button to create the CloudFormation stack.
As we have our template ready, select “Template is ready” and choose “Upload a template file” in the Template source and upload the template file.
Once you upload the file, “View in Designer” button will be enabled. Click on it to view our template design.
How to validate the CloudFormation template
To validate our template click on the “Tick” icon on top left in the designer. It will validate and show us errors if any. Once the validation is done, click on the “Cloud” icon to the left of “Tick” icon. It will take you to create stack page.
In the stack details page, enter the stack name and select your key-value pair. If you don’t have key-value pair create one and select it.
Leave the Configure stack options section as it is and click continue since we don’t need any IAM permissions or advanced options.
Then review the page and submit the template. The template will start creating the resources
Once creation is done click on resources tab, you’ll be able to see the resources we created (EC2 and Security group resources).
Click on the EC2 instance, you can see our instance will be up and running. Copy the public IPv4 address
Now go to browser and type http:<ip-address>//:8000 (In my case it is http://54.176.19.18:8000/), you can see the below page
This means our NodeJS app successfully got deployed!
Note:
EC2 user data will take some time to install dependencies. So for the first time, the page will take long time to load. Please be patient till the site is loaded.
Deleting CloudFormation Stack
If you no longer need the stack, you can delete it from the CloudFormation console. Select the stack you want to delete, click “Delete Stack,” and confirm the action. This action will delete all resources created using this stack. In our case, it’ll delete both EC2 and Security Group. You don’t need to delete the EC2 instance and Security Group individually.
Conclusion
In this article, we learned what is CloudFormation, it’s working, creating and deleting a template stack.
Hope you enjoyed reading this article! If you are stuck at any point feel free to drop your queries to me at my email. I’ll be happy to help you!
If you wish to learn more about AWS, subscribe to my newsletter
Originally published at https://www.gogosoon.com on April 19, 2023.