Deploying a React Application to AWS Cloudfront
Tags: react, aws, javascript, webdev
Published: 2022-03-10 02:56:55

Ever wondered how to deploy your single page React application to AWS Cloudfront service?. In this article I would be showing you how to deploy a sample React application to AWS Cloudfront

We would be deploying 
* Manually using AWS cloud console
* Automated using Github Action

##### Components

[AWS Cloudfront](https://aws.amazon.com/cloudfront/):  This is a content delivery network (CDN) provided by Amazon. Users access content through AWS nearest edge location. It can be used for delivering fast and secure websites, accelerate dynamic content delivery and api’s, stream live and on demand video.

[AWS S3](https://aws.amazon.com/s3/): This is a cost effective, highly performant object storage service provided by Amazon. It allows you to store objects such as documents, video, images, blobs, etc. It can be used for backups, cold storage, file storage, archiving, web frontend host, etc.

[Github Action](https://github.com/features/actions): Github Action is an automation tool for automating developers workflows, just like CircleCI, Jenkins,  it can be used to create CI/CD pipelines.

**Deployment**
As shown on the diagram below, we would be uploading our static assets (html,js,css) to S3 and then creating a Cloudfront distribution to use the uploaded asset. On subsequent deployments we would invalidate the existing distribution to enable Cloudfront fetch new content from S3.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4b7n8wm9v8pspsumz53e.png)

**Manual Deployment**
In order to manually deploy to Cloudfront ensure you have the following

* An AWS account with access to create S3 buckets and Objects as well as create Cloudfront distribution. Your normal AWS account would do, if you’re an IAM user ensure you have those permissions
* A sample React application, you can clone one [here](https://github.com/Oliver-ke/cf-deploy)

First, let's get our static assets ready. Depending on your package manager (npm or yarn) run `npm install` to install dependencies and `npm run build` to build project, doing this would populate our build folder with the built assets (html,css,js etc)

Next, Open your browser and login to your AWS account, search and select S3 from services, create a new bucket with the following fields

*Bucket name: Enter a globally unique name*
Leave the other fields default, and click “create bucket”.

Click to Select the newly created bucket, click upload and upload all files in the build folder.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjycdvoffzb0uto2g8dz.png)

Next, create a Cloudfront distribution, search and select Cloudfront from services, click “create distribution”,  on the creation form, do the following

*Origin domain: select your new s3 bucket name*
*S3 bucket access: select “Yes use OAI (bucket can restrict access to only CloudFront)”*
*Origin access identity: click “Create new OAI”*
*Bucket policy: select “Yes, update the bucket policy”*
Click “Create distribution”
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6bjghcjgl0vlebo6uk0.png)

This would create a  new distribution and update your S3 bucket policy, allowing Cloudfront to get objects.

Next, lets update the distribution error pages, Click on your newly created distribution and at the top tab menu click “Error pages”, then click “Create custom error response”, fill in the following

*HTTP error code: select “443: Forbidden”*
*Error caching minimum TTL: enter 30*
*Customize error response: select “Yes”* 
*Response page path: enter “/index.html”* without the quotes
*HTTP Response code: select “200:OK”* 

Finally, click “Create custom error response”.
Repeat these steps with HTTP error code “404”
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uyvu636v01w5okts2sfz.png)

Finally, let's check our application. With the distribution selected, click on the “General” tab, under details, click on the square like icon to copy the distribution domain name, paste this on a new browser tab to test application deployment.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/61wwf6towmubnkml60uj.png)

*Note: ensure you’re not seeing a “Deploying” status under Last Modified, if so wait till it’s complete.*

After all these is done, when next you update your application code, and needs to redeploy, just build and re-upload your new assets to the same bucket created earlier, this would overwrite the existing files, then move over to your cloudfront distribution, selected distribution and click on the “Invalidations” tab, click “Create invalidation”, under “Add object paths” enter `/*`, finally click “Create invalidation”. This would invalidate the cached assets on Cloudfront while updating the distribution with the newly uploaded assets on S3.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yu4k2z2udszri2zf9opv.png)

**Automating Deployment with Github Action**
Here we would be automating the manual steps above using Github Action, to follow along ensure you have the following
* AWS CLI user, this can be done by going to IAM service on AWS console, and creating a new user with programmatic access and Administrative permission, copy the account secret id and secret key, we would be needing it
* Github repository with sample react application


With our S3 bucket and Cloudfront distribution created, we would be using Github Action to automate building and deployment of our frontend application whenever a push is made to the repository’s master branch.

First, on S3 we’ll need to enable Access Control List (ACL) to allow our CLI user with programmatic access to push objects to an S3 bucket created by another user. Open your bucket, click on the permissions tab, scroll down to Object Ownership, click edit, select “ACLs enabled”, select “Object writer” and click “Save changes”,  scroll down to  “Access control list (ACL)” while on permissions tab check that your CLI user canonical id is on the ACL, if it isn’t add it by clicking edit, click add “grantee”, paste in your CLI user canonical id and select “list”, “write” access then click “Save changes”. 
*Note:  you can run the following command on your terminal to check your CLI user canonical id `aws s3api list-buckets --query Owner.ID --output text`, ensure you have AWS CLI with credentials setup on your terminal before doing this*
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oa1chf86fekvokt32t84.png)

Next,  push your sample application code to Github, login to Github and select the repository, click on the settings tab

Let’s add secrets that’ll be used in Github action, on the sidebar menu, click “Secrets”, then click “Actions”, click “New repository secret” to add a repository secret that would be used by Github Action.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/138f5ap639zqwousk9hh.png)

Next, enter the name of the key `AWS_ACCESS_KEY_ID`, and for value, paste in the value of your programmatic AWS user access key id, click “Add secret”. Repeat this step for `AWS_SECRET_ACCESS_KEY` and “AWS_DEFAULT_REGION”, for `AWS_DEFAULT_REGION` you can type in any region of choice such as `us-east-1`. 
Here you can also add any environment variable used by your application
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wengof1avkqu96hli0va.png)

Next, create an action workflow file for our project, still on the repository, click “Actions”, click “set up a workflow yourself” and paste in the script below.

```
name: Frontend Cloudfront Deployment

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x]
    steps:
    - uses: actions/checkout@v2

    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v1
      with:
        node-version: ${{ matrix.node-version }}

    - name: Yarn Install
      run: yarn install

    - name: Yarn Build
      run: yarn build
      env:
        REACT_APP_API_URL: ${{ secrets.REACT_APP_API_URL }}

    - name: Share artifact inside workflow
      uses: actions/upload-artifact@v1
      with:
        name: build-artifact
        path: build

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      # get build artifact
      - name: Get artifact
        uses: actions/download-artifact@v1
        with:
          name: build-artifact

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_DEFAULT_REGION }}

      # Copy build to s3
      - name: Deploy to S3
        run: |
          S3_BUCKET=s3://<s3-bucket-name>
          aws s3 sync . $S3_BUCKET --acl private
        working-directory: build-artifact

      - name: Create Cloudfront Invalidation
        run: |
          DISTRIBUTION_ID=<distribution-id>
          aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths /*
        working-directory: build-artifact
```
Replace `<s3-bucket-name>` and `<distribution-id>` with your bucket name and distribution id respectively. From the script you can find references to our secrets created earlier. 
Click “Start commit”, add a commit message and commit directly to master.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q6oakeb1hsdwxjkgg9us.png)

Finally, click on “Actions” to see your newly created workflow running, once this is completed, open your Cloudfront url to see the updated application.


I’m glad we made it this far 👏, we’ve covered a lot. For a more detailed explanation please check the video [here](https://youtu.be/l8mFrSfSGOU). I made a video to properly explain the steps, so if you’re having issues just watch the video.

Perhaps you find this helpful, please leave a LIKE, Cheers!