Deploy React.js application using AWS S3 & GitLab pipelines for automatic deployment 2024

Deploy React.js application using AWS S3 & GitLab pipelines for automatic deployment 2024

In this article, we will look at how we can deploy our React.js application to the AWS S3 bucket using GitLab pipelines🚀

We’ll look at how to automate our development process as much as possible🔧

1 – Project setup and creating GitLab repository 📁

I’ll be using the React.js application I’ve initialized using the following command:

npm create vite@latest

Login to your GitLab account (Register if you already don’t have one)🔐

Create a repository for our project📦

Clone our repository using HTTP or SSH🔗

Commit our code to the repository using the following command💾

git add .
git commit m Initial commit
git push u origin main

After, when we configure our S3 bucket, we will come back to GitLab to configure our pipeline which will do auto-deployment for us whenever we push new code to the main branch🔄

2 – Login to your AWS account and find the “S3” service🌐

Search for the “S3” service in the search input field which is located at the top of the page.

Choose an option with the green colored icon of a bucket with the description “Scalable Storage in the Cloud”🪣

You will see the following page. Click on the “Create bucket” button

3 – Creating our S3 bucket🛠️

Bucket name: our-react-app-s3-bucket📝

IMPORTANT: Disable the “Block all public access” checkbox input field -> this is important to enable our application to be publicly visible🚫🔓

Click the “Create bucket” button✅

Now, we have successfully created our S3 bucket🎉

Modify bucket policy📜

Now, you need to edit the Bucket Policy. Click the “Edit” button in the Bucket Policy section. In our case bucket name is our-react-app-s3-bucket so I will use it. Please change it according to your bucket name✏️

Paste the following code into your new policy:

{
Version: 2012-10-17,
Statement: [
{
Sid: PublicReadGetObject,
Effect: Allow,
Principal: *,
Action: s3:GetObject,
Resource: arn:aws:s3:::our-react-app-s3-bucket/*
}
]
}

4 – Create a new Identity Provider🔑

Search for the “Identity Provider” service in the search input field which is located at the top of the page.

On the left navigation pane, under Access management choose Identity providers and then choose Add provider button➕

For provider type, select OpenID Connect🌐

For Provider URL, enter the address of your GitLab instance, such as https://gitlab.com or https://gitlab.example.com🌍

For Audience, enter something that is generic and specific to your application. In my case, I’m going to enter our-identity-provider🎯

To prevent confused deputy attacks, it’s best to make this something that is not easy to guess🚫🕵️‍♂️

Take note of this value because you will use it to set the ID_TOKEN in your .gitlab-ci.yml file📝

Lastly, click on the “Add provider” button to finish up✅

Create the permissions policy📜

After you create the identity provider, you need to create a permissions policy🛡️

From the IAM dashboard, under Access management select Policies and then Create policy

Select the JSON tab and paste the following policy replacing our-react-app-s3-bucket on the Resource line with your bucket name. Click the “Next” button to continue. For a “Policy name” input field type: our-policy. At the end, click the “Create policy” button to finish up📝

{
Version: 2012-10-17,
Statement: [
{
Effect: Allow,
Action: [s3:ListBucket],
Resource: [arn:aws:s3:::our-react-app-s3-bucket]
},
{
Effect: Allow,
Action: [
s3:PutObject,
s3:GetObject,
s3:DeleteObject
],
Resource: [arn:aws:s3:::our-react-app-s3-bucket/*]
}
]
}

Note: Don’t change anything except the bucket name!🛑

5 – Creating a role🎭

Now it’s time to add the role. From the IAM dashboard, under Access management select Roles and then click Create role button. Select Web identity🆔

In the Web identity section, select the identity provider you created earlier. In our case, it would be gitlab.com🌐

For the Audience, select the audience you created earlier. Select the Next button to continue. In our case, it would be our-identity-provider🎯

Click the “Next” button➡️

If you wanted to limit authorization to a specific group, project, branch, or tag, you could create a Custom trust policy instead of a Web identity. Since I will be deleting these resources after the tutorial, I’m going to keep it simple😊

Click the “Next button”➡️

During the Add permissions(step number 2), search for the policy you created and click the “Next” button to continue. In our case, it would be our policy🔍

In the next step(step number 3), give your role a name and click the “Create role” button. I will call it our-role-name📝

Search for the role you just created. Click on it🔍

In the summary section, find the Amazon Resource Name (ARN) and save it somewhere secure🔒

You will use this in your pipeline🔄

In our case, it is arn:aws:iam::162340708442:role/our-role-name✏️

6 – Deploy to your Amazon S3 bucket using a GitLab CI/CD pipeline🚀

Inside of your project repository on GitLab, create two CI/CD variables🔧

The first variable should be named ROLE_ARN. For the value, paste the ARN of the role you just created in the last step📝

The second variable should be named S3_BUCKET. For the value, paste the name of the S3 bucket you created earlier. In our case it would be our-react-app-s3-bucket📝

Retrieve your temporary credentials🗝️

Inside of your .gitlab-ci.yml file, paste the following code:

.assume_role: &assume_role
>
STS=($(aws sts assumerolewithwebidentity
rolearn ${ROLE_ARN}
rolesessionname GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}
webidentitytoken $ID_TOKEN
durationseconds 3600
query Credentials.[AccessKeyId,SecretAccessKey,SessionToken]
output text))
export AWS_ACCESS_KEY_ID=${STS[0]}
export AWS_SECRET_ACCESS_KEY=${STS[1]}
export AWS_SESSION_TOKEN=${STS[2]}

This is going to use the AWS Security Token Service to generate temporary (3,600 seconds) credentials utilizing the OIDC role you created earlier🛡️

Create the deploy job🛠️

Now, let’s add a build and deploy job to build your application and deploy it to your S3 bucket📦

First, update the stages in your .gitlab-ci.yml file to include a build and deploy stage as shown below:

stages:
– build
– test
– deploy

Next, let’s add a job to build your application. Paste the following code in your .gitlab-ci.yml file:

build artifact:
stage: build
image: node:latest
before_script:
npm install
script:
npm run build
artifacts:
paths:
build/
when: always
rules:
if: $CI_COMMIT_REF_NAME == “main”
when: always

This is going to run npm run build if the change occurs on the main branch and upload the build directory as an artifact to be used during the next step🔄

Next, let’s add a job to actually deploy to your S3 bucket. Paste the following code in your .gitlab-ci.yml file:

deploy s3:
stage: deploy
image:
name: amazon/awscli:latest
entrypoint:
/usr/bin/env
id_tokens:
ID_TOKEN:
aud: react_s3_gl
script:
*assume_role
aws s3 sync build/ s3://$S3_BUCKET
rules:
if: $CI_COMMIT_REF_NAME == “main”
when: always

Your complete .gitlab-ci.yml file should look like this:

stages:
build
test
deploy

.assume_role: &assume_role
>
STS=($(aws sts assumerolewithwebidentity
rolearn ${ROLE_ARN}
rolesessionname GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}
webidentitytoken $ID_TOKEN
durationseconds 3600
query Credentials.[AccessKeyId,SecretAccessKey,SessionToken]
output text))
export AWS_ACCESS_KEY_ID=${STS[0]}
export AWS_SECRET_ACCESS_KEY=${STS[1]}
export AWS_SESSION_TOKEN=${STS[2]}

unit test:
image: node:latest
stage: test
before_script:
npm install
script:
npm run test:ci
coverage: /All files[^|]*|[^|]*s+([d.]+)/
artifacts:
paths:
coverage/
when: always
reports:
junit:
junit.xml

build artifact:
stage: build
image: node:latest
before_script:
npm install
script:
npm run build
artifacts:
paths:
build/
when: always
rules:
if: $CI_COMMIT_REF_NAME == “main”
when: always

deploy s3:
stage: deploy
image:
name: amazon/awscli:latest
entrypoint:
/usr/bin/env
id_tokens:
ID_TOKEN:
aud: react_s3_gl
script:
*assume_role
aws s3 sync build/ s3://$S3_BUCKET
rules:
if: $CI_COMMIT_REF_NAME == “main”
when: always

7 – Make a change and test your pipeline🧪

Inside App.js, modify some code✏️
Commit your changes to the main branch. The pipeline should kick off and when it finishes successfully you should see your updated application at the URL of your static website🌐

Voila! 🎉

You now have a CI/CD pipeline built in GitLab that receives temporary credentials from AWS using OIDC and automatically deploys to your Amazon S3 bucket🚀

To take it a step further, you can secure your application with GitLab’s built-in security tools🔒

Where to find the URL to visit your website?🔗

Go to the “S3 buckets” page. Click on your bucket. Go to “Properties”🏠
Scroll to the bottom. You will find “Static website hosting”. Click the “Edit” button✏️
Select the “Enable” radio checkbox✅

Click the “Save changes” button💾

In “Index document” fill: index.html📝

You will be redirected back. Again, scroll to the bottom of the page and you will find your website URL in the Static website hosting section🌐

Thank you for reading. Hope I helped 😊

🚀 Follow me on GitHub