DEV Community

Cover image for Automating My Web App Deployment with AWS CI/CD
Aarush Luthra for AWS Community Builders

Posted on • Originally published at aarush7.hashnode.dev

6 1 1

Automating My Web App Deployment with AWS CI/CD

Manually deploying my web application wasn’t difficult, but it quickly became annoying. Every update meant SSH-ing into my EC2 instance, copying files, installing dependencies, and restarting the server. I knew it was time to shift to a better, automated way —so I explored AWS CI/CD tools to make my life easier.

At least that was what I thought…

My first few attempts were filled with with failed builds, missing permissions and unexpected deployment failures. It took me time to get it up and running but once it was up, the advantages were clear. CodePipeline handled the entire workflow from getting the latest changes from GitHub to deploying it. CodeBuild ensured that the dependencies were installed correctly while CodeDeploy made sure the deployment on EC2 was a success!

Then came security! Out of curiosity, I tested my site using Gobuster to check for exposed directories and files. And behold—my whole logic was , up for the taking🤯! That was the moment I realized—security🔐 and deployment🚀 need to go hand in hand.

Here’s how I did it, the mistakes I made, and the steps I took to secure my site !

Why automate with AWS CodePipeline?

The next question after deciding to integrate CI/CD into my project was: Which tool to use? . There were plenty of options —GitHub actions, Jenkins, GitLab CI/CD—but AWS CodePipeline was the bestfit for my need. It checked all the right boxes:

🔐Security? Covered. IAM roles, encrypted SSM parameters.

🗄No CI/CD server. Unlike Jenkins, there’s no need to manage the underlying infrastructure.

☁️Seamless AWS Integration. Works effortlessly with EC2, SSM, DocumentDB, and more.

Prerequisites

Before we begin, ensure you have the following:

✅ Access to AWS Management Console

A GitHub repository containing your application

Besides this you’ll need appropriate permissions to allow services to interact seamlessly. The diagram below gives an overview of how everything connects.

Image description

Setting up the Pipeline

It's time to build the CI/CD pipeline step by step.

Step1: Connect GitHub to CodePipeline

We create a pipeline from AWS Management console and select GitHub as source. We authorize AWS to access the repository and choose the branch. And let the magic begin 🪄

Image description

Image description

Step2: Configuring AWS CodeBuild

AWS CodeBuild compiles and packages the code before deployment. It ensures that every change is built and tested before moving to the deployment stage. To configure CodeBuild, firstly setup the environment and then define build steps in a buildspec.yml file. This file defines how to install dependencies, prepare artifacts.

Image description

My buildspec.yml

version: 0.2
phases:
  install:
    runtime-versions:
      nodejs: 16   #Nodejs version
  build:
    commands:
      - cd Backend      #navigate to your directory
      - npm install     #install the dependencies
artifacts:
  files:
    - '**/*'
Enter fullscreen mode Exit fullscreen mode

Make sure to integrate it with CodePipeline

Image description

Step3: Creating an EC2 instance and integrating it with CodeDeploy

Next, we setup an EC2 instance. AWS CodeDeploy automates the complete deployment process ensuring updates are rolled out efficiently and seamlessly

In this setup, I have choose Amazon Linux instance with permissions to access Systems Manager (Parameter Store), CodeDeploy and DocumentDB. Systems Manager allows us to securely retrieve environment variables while DocumentDB serves as our database.

Image description

Image description

Additionally, it is important to ensure that appspec.yml and configure.sh are placed in the root directory of the repository. appspec.yml defines the entire deployment lifecycle while configure.sh gets executed to configure the instance with necessary dependencies such as Apache server.

My appspec.yml file:

version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/html
permissions:
  - object: /var/www/html
    pattern: "**"
    owner: ec2-user
    group: ec2-user
hooks:
  AfterInstall:
    - location: configure.sh
      runas: root
Enter fullscreen mode Exit fullscreen mode

Myconfigure.sh file:

#!/bin/bash
sudo yum update -y
sudo yum install -y httpd
sudo systemctl start httpd
sudo systemctl enable httpd
cd /var/www/html/Frontend
sudo echo "CONFIG = { PUBLIC_IP: '$(curl -s ifconfig.me)' };" > config.js
cd /var/www/html/backend
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
aws ssm get-parameters --names "MONGO_URL" "PORT" --with-decryption --query "Parameters[*].[Name,Value]" --output text | awk '{print $1 "=" $2}' 
sudo yum install -y nodejs
sudo npm install -g pm2
sudo pm2 start index.js
echo "Deployment complete and server restarted successfully."
Enter fullscreen mode Exit fullscreen mode

Make sure to create an application in AWS CodeDeploy and set up a deployment group. I chose the In-Place deployment type for now and used a single instance (the one we created earlier). Don't forget to associate an IAM role with the deployment group —it is crucial for CodeDeploy to manage deployment on your instance.

Image description

Image description

Image description

Navigate back to CodePipeline page and ensure that you have correctly added the the CodeDeploy application name and the deployment group. This ensures that the deployment stage knows where to deploy the application

Image description

Step4: Testing the pipeline

Everything is configured and ready to be run !! Push a commit to your repository and see the magic happen🪄🪄

Image description

AWS CodePipeline executes the following steps to deploy your application with zero manual efforts

1️⃣Detect the change and trigger the pipeline

2️⃣Use CodeBuild to compile and package the code

3️⃣Deploy it to EC2 via CodeDeploy

Pro Tip📝

Don't tell me you pushed your .env file to GitHub… 😬 Make sure to store your DocumentDB credentials in SSM Parameter Store. It keep your secrets safe 🔏 and ensures your application retrieves it at runtime.

Here’s how mine looks

Image description

Troubleshooting Errors

If you do run into some failed builds, deployment issues don’t worry, we have all been there ! My Pipeline at one point my pipeline looked like this

Image description

And here’s how it looks once everything works

Image description

One trick to debug your errors is to check logs from the CodeDeploy Agent on EC2. It will provide you insights into potential misconfigurations. Check it out by running:

sudo tail -f /var/log/aws/codedeploy-agent/codedeploy-agent.log
Enter fullscreen mode Exit fullscreen mode

If BeforeInstall, AfterInstall, or ApplicationStart fail check the logs by running the following command:

sudo tail -n 50 /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
Enter fullscreen mode Exit fullscreen mode

GoBuster vs My Deployment: Exposing My Own Mistakes

After deploying my application and feeling I ran Gobuster against the IP… and let’s just say, it was an eye-opener.

Directories and all my files were sitting there in plain sight!

I ran the following command:

gobuster dir -u http://<ip> -w lists/directory-list-2.3-medium.txt
Enter fullscreen mode Exit fullscreen mode

Image description

And this is how one can easily see my code files present

Image description

Mitigation Steps:

It was time to make things secure. I SSH-ed into my EC2 instance and modified the apache configuration files. Check the commands below:

sudo nano /etc/httpd/conf/httpd.conf
Enter fullscreen mode Exit fullscreen mode

Now, replace the directory block (for the root directory) with the following

<Directory /var/www/html>
    Options -Indexes
    AllowOverride All
    Require all granted
</Directory>
Enter fullscreen mode Exit fullscreen mode

To secure your other files (if there) such as use the following code block

<FilesMatch "^(\.env|\.git|config\.php|\.htaccess)">
    Order allow,deny
    Deny from all
</FilesMatch>
Enter fullscreen mode Exit fullscreen mode

Wrapping It Up 🎯

This journey was a mix of automation and a little bit of shock (thanks to Gobuster!). The biggest lesson? Ensuring that the application is SECURE🔐 is as important as getting it running.

A small misconfiguration exposes more than what you anticipate! Keep it secure, keep it running !!🚀☁️

Thank your for reading !!

Aarush Luthra

Top comments (0)