DEV Community

Cover image for Build a Simple AWS CI/CD Pipeline with deployment to Elastic Beanstalk
Jones Ndzenyuy
Jones Ndzenyuy

Posted on

Build a Simple AWS CI/CD Pipeline with deployment to Elastic Beanstalk

Introduction

Building a pipeline for automated tests and deployment is every developers' dream, not having to manually verify security vulnerabilities or functioning before making it live. A wrongly configured pipeline or choosing tools without prior load considerations can either end up over spending or crashing. In this project I will guide you through the building of an AWS native pipeline, it will build our code and host directly on AWS, cost considerations are made to ensure very little expenditure with every build, AWS Build only charges for the build time, no servers running.

How it works

The code is hosted in Bitbucket, whenever there is an update, the pipeline is triggered and fetches the code, Code build runs a test on Sonar cloud, if the quality gate is passed, the pipeline launches the build phase, stores the built artifact in an S3 bucket next Elastic Beanstalk is triggered to pick the artifact from S3 and update its environment. After a 360 seconds waiting, the final stage of the pipeline is triggered which tests the deployed software, the credentials of a test user, initially stored in DB is used to login to the web App and takes screenshots which are stored in another S3 bucket.

Architecture

Pipeline architecture

How to Build It

Host code in Bitbucket

Create an account on bitbucket a guide to create it can be found here. and clone the code by running

git clone https://github.com/Ndzenyuy/cicd-on-aws.git
cd cicd-on-aws
Enter fullscreen mode Exit fullscreen mode

The above command will clone the source code and change the working directory in to cicd-on-aws folder.

Now we have to configure a Bitbucket repository and upload the code. Go to your browser and create a repository in Bitbucket. To learn how to create a repository in bitbucket check this guide here. The repository should the following
workspace: aws-cicd-beanstalk
repo name: cicd-with-elastic_beanstalk
access level: private

Now back to the terminal, run the following:

git remote rm origin
git remote add source https://path/to/your/bitbucket/repo.git
git push -u 
Enter fullscreen mode Exit fullscreen mode

Now the code should be present on Bitbucket. Verify if it is the case or troubleshoot were you may have gone wrong. Leave the bitbucket tab open and open a different one.

Configure CodeArtifact

The App is a java based app, we will be using Maven to build it, so we are configuring CodeArtifact so that we can download and store maven dependencies, this will help reduce build times in subsequent builds. On the AWS console, search CodeArtifact and create a repository and give it the following configurations:

repo name: cicd-aws
Public upstream repositories: maven-central-store -> next
aws account: this account
domain: give your domain, could be any name -> next
verify and create repository

The created repository will display as follows:
created repository

Now open maven-central-store repository and click on View connection instructions -> Mac & Linux = true -> Select package manager client = mvn.

Here you'll have the connection instructions on how to connect to the repository from your project.

Copy Step 3: Export a CodeArtifact authorization token for authorization to your repository from your preferred shell. and open the buildspec.yml file in vscode and replace the line 15. The code should be as:

 - export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain sparxinc --domain-owner 781655249241 --region us-east-1 --query authorizationToken --output text`
Enter fullscreen mode Exit fullscreen mode

Copy Step 4: Add your server to the list of servers to your settings.xml to settings.xml. Scroll to line 5 and replace the server section with what was copied from the artifact repos instructions. It should look like this

<servers>
  <server>
    <id>sparxinc-cicd-aws</id>
    <username>aws</username>
    <password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
  </server>
</servers>
Enter fullscreen mode Exit fullscreen mode

Next copy Step 5: Add a profile containing your repository to your settings.xml. and replace the contents of the profile section and the mirrors, it should look like this

#Profiles
<profiles>
  <profile>
    <id>sparxinc-cicd-aws</id>
    <activation>
      <activeByDefault>true</activeByDefault>
    </activation>
    <repositories>
      <repository>
        <id>sparxinc-cicd-aws</id>
        <url>https://sparxinc-XXXXXXXXXXXX.d.codeartifact.us-east-1.amazonaws.com/maven/maven-central-store/</url>
      </repository>
    </repositories>
  </profile>
</profiles>
#mirrors
<mirrors>
  <mirror>
    <id>sparxinc-maven-central-store</id>
    <name>sparxinc-maven-central-store</name>
    <url>https://sparxinc-XXXXXXXXXXXX.d.codeartifact.us-east-1.amazonaws.com/maven/maven-central-store/</url>
    <mirrorOf>*</mirrorOf>
  </mirror>
</mirrors>
Enter fullscreen mode Exit fullscreen mode

Save the settings.xml file and open pom.xml. Back on the console, copy only the url from the instructions and replace those that is on line 303 and . The copied url is similar to:
<url>https://sparxinc-XXXXXXXXXXXX.d.codeartifact.us-east-1.amazonaws.com/maven/maven-central-store/</url>

Create build jobs

Here we are going to creat two build jobs, the first will analyse the code on sonar cloud and the second will build the artifact.

Image description

We need to first create a connection for bitbucket app, go under developer tools, search for settings -> connection and create a new connection.

Image description

Select a provider: bitbucket
connection name: awscicd -> connect to bitbucket

Image description

In the next page click install new app and grant access to bitbucket when prompted. It'll return to the previous page, now click on the connect button. Our pipeline can now connect to bitbucket.

Code analysis job on sonar cloud

On the console, search CodeBuild -> Create project and give the following configurations:
Project name: code-analysis
source provider: bitbucket
Credential: Bitbucket app
connection: awscicd
repository: cicd-with-elastic_beanstalk
operating system: ubuntu
new service role name: codebuild-code-analysis-service-role
Build spec -> use a buildspec file -> buildspec name: buildspec.yml
Cloudwatch logs -> stream name prefix = awscicd-logs
click create build project

Image description

If you open buildspec.yml file, you'll notice the parameters are stored in the parameter store, we need values for LOGIN, HOST, Organization and Project. For extra security, we need to store in Parameter store. But before then, we need to create a project in Sonarcloud before storing it's access information on parameter store. Go to sonarcloud and create an account if you don't have any yet by following this tutorial.
Login token: In the sonar cloud welcome page, on the top right corner of the screen, click you account icon and select My Account -> Security. Now give a token name of your choice and click generate. Copy the generated code to a sticky note for later use

Organization: On the same tab, open organizations and create a new organization, select create one manually
name: aws-cicd-organization
key: aws-cicd-organization
plan: free plan -> create organization

Project: On the page that opens, click analyse new project
organization: aws-cicd-organization
display name: beanstalk-app
project key: aws-cicd-organization_beanstalk-app -> next
The new code for this project will be based on: Previous version
create project

Open a new tab and open AWS console and search systems manager -> parameter store and create parameter with the following configurations.
HOST
parameter: HOST
Tier: standard
Type: String
datatype: text
value: https://sonarcloud.io

Organization
parameter: Organization
Tier: standard
Type: String
datatype: text
value: aws-cicd-organization

LOGIN
parameter: LOGIN
Tier: standard
Type: Secure String
datatype: text
value: (paste the value of the token generated in sonarcloud)

Project
parameter: Project
Tier: standard
Type: String
datatype: text
value: aws-cicd-organization_beanstalk-app

Image description

Return to the codebuild tab, we need to modify the IAM role of the build project in order to access parameter store, on the page

Image description

Under service role, top right, click on the IAM arn, it opens a new tab in IAM. Create a System manager policy with the following allow permissions:
under list: DescribeParameters
under read: GetParameter, GetParametersByPath, GetParameterHistory, GetParameters, DescribeDocumentParameters

Image description

Click next, for policy name parameteraccess and create. Return and open the service role for the build project and add the created policy to it. Next search for another policy named "AWSCodeArtifactReadOnlyAccess" and add it to the same role. You should have policies as in the image below

Image description

Now return to the build project in codebuild tab and start build. If everything turns out well

Image description

So the Code analysis on sonar cloud should be populated with the statistics of the job that ran.

Image description

Build artifact job

Back to creating a build job console -> create build project and configure with the following:
name: build-artifact
source provider: bitbucket
select "custom source provider"
repository: aws-cicd-beanstalk
source version: main
operating system: ubuntu
select "new service role"
build spec: select 'use buildspec file'
buildspec name: aws-files/build_buildspec.yml
create build project

Image description

Next we need to add the policy to access CodeArtifact to read the dependencies. To do so, click on the Service role url and add policy to this role. Add the policy named AWSCodeArtifactReadOnlyAccess.

Image description

Close the tab and return to CodeBuild and run build-artifact project by clicking start build. The project should finish successfully.

Image description

Create pipeline

Create an S3 bucket to store the artifact after the build job runs, give the parameters:
name: pipeline-storage-for-artifacts-xxx (xxx are random numbers to make the bucket name unique)
region: us-east-1
create bucket

Now create a folder in the bucket named artifacts

On the AWS console, search pipeline -> create pipeline -> Build custom pipeline -> next
name: aws-cicd-pipeline
leave defaults and click next
source provider: bitbucket
connection: connect to bitbucket
-> enter a connection name
-> create connection
-> choose the app or create one if none exists and connect
repository name: aws-cicd-beanstalk
default branch: main -> next
Build provider: select other build providers and choose AWS CodeBuild
Project name: build-artifact
input artifacts: source artifacts -> next
Deploy provider: S3
input artifacts: BuildArtifact
key: artifacts
next, review and create pipeline

The pipeline will be automatically triggered, we need to stop it and add the code analysis stage. So click on stop execution select the pipeline and stop it. Click on edit and add a stage just before the build stage

Image description

stage name: codeanalysis -> add -> add action group
action name: analyse-code
action provider: aws codebuild
input artifacts: SourceArtifact
project name: code-analysis -> done

Edit the deploy stage action and select Extract file before deploy then -> done

Make sure all the edited stages are validated with done. Save the changes and return to the pipeline. Click on release change to trigger the pipeline.

Image description

Image description

And voila, the pipeline has been successfully built. If you open the S3 bucket, you will find the artifact stored there

Image description

Configure SNS

Go to the console and search SNS -> create a new topic
type: standard
name: awscicd-notification -> create notification

Image description

Click on create subscription
protocol: email
endpoint: youremail@email.com

An email will be sent to the provided email. The status will be pending until you open the email and validate the subscription.

Image description

To the left pane, under pipelines -> settings -> notifications -> create notification rule
name: aws-cicd-rule
Events that trigger notifications: select all
targets: awscicd-notification -> submit

Image description

The notification rule will be created.
Now back to our pipeline, if we run the pipeline anew, notifications will be sent about the status of the pipeline

Launch Elastic Beanstalk

Create an IAM role

Console -> IAM -> roles -> Create role:
`Trusted entity type: AWS service
use case: EC2
Permissions policies:

  • AdministratorAccess-AWSElasticBeanstalk
  • AWSElasticBeanstalkCustomPlatformforEC2Role
  • AWSElasticBeanstalkRoleSNS
  • AWSElasticBeanstalkWebTier

role name: elasticbean-role-awscicd -> Create role`

Create keypair

Console -> EC2 -> Keypairs -> create keypair
Name: beanstalk-key -> create keypair

Launch Elastic beanstalk

Search Elastic Beanstalk on the console -> Create Application then
Application name: awscicd-beanstalk
-> create

Image description

Click create new environment
Environment tier: web-server-environment
platform: Tomcat
Platform branch: Tomcat 10 with Corretto 21 running on 64bit Amazon Linux 2023
Platform version: 5.4.1 (Recommended)
Presets: Custom configuration
Leave any other as default and -> click next
Service role: Create and use new service role
EC2 key pair: beanstalk-key
EC2 instance profile: elasticbean-role-awscicd
Virtual Private Cloud (VPC): choose VPC with internet access
Public IP address: activated =true
Instance subnets: select all -> Next
Root volume type: General Purpose 3(ssd)
size: 10GB
IOPS: 3000
Throughput: 125
Autoscaling group -> Environment type: load balanced
min instances: 1
max instances: 4
Instance types: t2.micro
Processes: select default -> actions -> edit
-> sessions -> session stickiness = Enabled
Monitoring: Enhanced
email notifications: <enter your email>
Deployment policy: Rolling
Deployment batch size: 50 -> next

Review all the settings

Image description

Image description

Image description

Image description

Image description

Review all the settings and click on create. This will take about 4-5 minutes to create the infrastructure.

Image description

When Beanstalk is ready, you can click on the DNS link to access the page:

Image description

Our beanstalk environment is up and running, we will configure the pipeline to update the code with the built artifact.

Create RDS Database

Console -> RDS -> Create database and configure it as follows
choose a database creation method: standard create
Engine options: MySQL
Engine version: 8.0.35
Templates: free tier
DB instance identifier: awscicd-mysql
master username: admin
password: admin123
Burstable classes (includes t classes): db.t3.micro
storage: 20GB
Security group: create new(name: awscicd-sg)
Additional configuration: Initial database name: accounts

Review and create database. Wait for a while till the database is created. When the status changes from creating -> available before we can proceed.

Image description

Click on the created database and copy the endpoint. It should be like awscicd-mysql.cjrs4jngtznx.us-east-1.rds.amazonaws.com

Initialize Database

We have to login into the beanstalk instance and initialize the database with the initial data. But first we need to configure the awscicd-sg to receive traffic from beanstalk.

Go to console -> EC2 -> Security groups and open the Security group for the running beanstalk instance and copy the security group ID. Now open awscicd-sg -> Edit inbound rules.
Add a new rule allowing 3306 from the security group of the beanstalk instance. This will permit us to connect to the database through the beanstalk instance.

Image description

Save the changes. Now ssh into the running instance and run the following: (make sure to replace with the rds endpoint copied earlier)

sudo -i
dnf install mariadb105 -y
wget https://raw.githubusercontent.com/Ndzenyuy/vprofile-project/refs/heads/cd-aws/src/main/resources/db_backup.sql
mysql -h <enter-rds-edpoint> -u admin -padmin123 accounts < db_backup.sql
Enter fullscreen mode Exit fullscreen mode

Add other parameters to Parameter store

We need to add the RDS endpoint, the database user and password to our parameters because Elastic beanstalk connects to the database. So we go to parameter store -> create new parameter and create the following parameters:

RDS-Endpoint
parameter: RDS-Endpoint
Tier: standard
Type: String
datatype: text
value: <enter your RDS End point>

RDPUSER
parameter: RDUSER
Tier: standard
Type: String
datatype: text
value: admin

RDPASS
parameter: RDPASS
Tier: standard
Type: Secure String
datatype: text
value: admin123
// or enter the password you used when creating db

Save all the parameters and edit the last stage of the pipeline, the deploy stage so that we can now deploy to beanstalk. Go to pipelines -> select the pipeline we created earlier(aws-cicd) and select it, choose edit and scroll to the deploy stage and update with the following parameters:
action name: deploy
Action provider: AWS Elastic beanstalk
Input artifact: BuildArtifact (what we defined in the build stage)
Application name: awscicd-beanstalk
Environment: web-server-environment
and update the environment.

Now Click on done:

Image description

Scroll up and Save the pipeline. Now to test the pipeline, Click on Release change. This will trigger the pipeline to build all the steps and deploy to our beanstalk environment.

Image description

After the build successfully completes, after about 5 minutes, check the Enviroment DNS on beanstalk and see how the application is displayed

Image description

To check if the RDS database is connected, try to log-in with the credentials
Username: admin_vp
password: admin_vp

If the database is connected, we'll be channeled to the welcome page:

Image description

Wooow!!!!!!!! Congratulations, you just deployed a cicd pipeline with AWS Pipeline. Each code update detected on your branch and pushed to bitbucket will automatically deploy to beanstalk.

If you succedded to build this project, please let me know in the comments, if you have difficulties i will be glad to help, just put it in the comments.

I haven't included Selenium test stage for the moment, but will certainly do when all is ripe

Top comments (0)