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,

  1. Managing the infrastructure changes in multiple environments (Development, Staging, Production)
  2. Re-create the same infrastructure in a different region / account
  3. 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 checkout 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/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 SampleNodejsDeploySG

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://: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 by entering your email address in the below box.

Have a look at my site which has a consolidated list of all my blogs.

Share this article