React Continuous Deployments with AWS CodePipeline

Jeffrey (Bongo) Russom
9 min readJul 26, 2018

--

Continuous Deployment Pipeline for React

Facebook’s create-react-app makes it incredibly easy to start a React project. With just a single command you get a fully bootstrapped React application that you can start developing on. No more hunting around Github looking for a decent React starter application.

While resources on developing withcreate-react-app are widely available, good resources on how to deploy these apps to production are somewhat lacking. The create-react-app docs provide many suggestions on how to do this, but the basic tutorials on how to do this for AWS are bare bones and mostly involve manually uploading assets to S3 via the AWS console.

I recently spent a lot of time playing around with learning AWS and CloudFormation, and I was interested in building a continuous integration pipeline for a create-react-app starter app using AWS. In this post I will go over the pipeline I came up with and once we are done, all you need to do to get your changes deployed to production is commit your code and push it to your Github repo

$ git commit -am "My new awesome feature"
$ git push

and your changes will magically end up in CloudFront!

Setting up your React Application

The first step to creating our pipeline is to create the actual React application that we will be deploying.

$ npm install -g create-react-app
$ create-react-app my-shiny-app

Let create-react-app do its thing and once it is done cd into the newly created app and start it up with npm start to check that it is working on localhost.

Everything’s working on localhost

Sweet! the app works locally so now we can create a git repository for the source code and push it up to a github repo.

$ git init
$ git add .
$ git commit -m "Initial commit"
$ git remote add origin git@github.com:qswitcher/my-shiny-app.git
$ git push -u origin master

Deployment Requirements

Before we start implementing our pipeline, we need to list out the requirements of our pipeline and what we will need it to do.

  • Git push triggering: Start a build whenever we push new commits to our repository.
  • npm build: We need a node environment that can run the create-react-app build script to generate the js, html, and css files for our application.
  • S3: We need a place to actually store and host the files. For AWS we will use the Simple Store Service (S3).
  • A CDN: Though it is possible to serve files directly from S3, a much more performant solution will be to serve the files from a Content Delivery Network, which in our case will be CloudFront.

CloudFormation

Most tutorials on deploying React applications to AWS will walk you through setting up your infrastructure using the AWS console. While this a great learning exercise to do if you are new to AWS, it is not scalable for most real world applications. It is error prone and time consuming to set all this up manually, and when you go to make your next React project, you have to do it all over again!

Infrastructure as Code (IaC) systems solve these problems by providing a means to provision infrastructure through the use of machine-readable configuration files. In the case of CloudFormation these are YAML files that you upload to CloudFormation. IaC systems provide many benefits such as

  • Ability to quickly duplicate your infrastructure for deployment into testing and staging environments or into different AWS regions
  • Ability to templatize your stack to allow for standardization of your applications and easier reuse
  • Ability to track changes by storing your configuration file in source control

Adding the first resource to our template

The first draft of our CloudFormation template will just consist of the S3 bucket we will need to host our application.

In our template, we create an S3 bucket which we will name “DeployBucket” and configure it for static website hosting. At this point we could just upload our files to this bucket and serve the content directly from the bucket at the S3 website URL <bucket-name>.s3-website-<AWS-region>.amazonaws.com. However any actual production would want to use a CDN to minimize page load times.

CloudFront

To meet the CDN requirement we will use Amazon’s CDN, CloudFront. Even if you think you don’t need a CDN for your website, CloudFront is still useful if you want to configure your site to use SSL.

We can use CloudFront and configure it to serve files from our S3 bucket by creating another resource config YAML object under Resources

In our config we have configured a single CloudFront distribution to use our DeployBucket as an origin. Once we put our assets in the S3 bucket, CloudFront will automatically pull requested files from the bucket when they’re requested and cache the files in the CloudFront network. It is important that we configure reasonable cache settings to avoid excessive data transfer between our S3 bucket and CloudFront since AWS charges traffic between S3 and CloudFront.

With our S3 and CloudFront distribution configured, now we just need a way to get our React files into the S3 bucket.

CodeBuild

To run the React npm build script we need a node environment with npm installed. For this we will use CodeBuild which is a build service that provides build environments for all of the major programming languages and frameworks such as NodeJS. Configuring CodeBuild is similar to CloudFormation, all you need is a YAML file which CodeBuild refers to as a build spec file.

The build steps are broken up into 3 phases

  • pre-build which installs npm dependencies
  • build where we run npm run build
  • post-build which copies over the build artifacts to S3

With our CodeBuild build YAML in hand, we just need to set up our CodeBuild configuration in our CloudFormation template

The configuration may look scary but it can be broken down into two resource declarations

  • CodeBuild: configures the node build container and the build spec
  • CodebuildRole: an IAM role that gives CodeBuild access rights to S3, CloudWatch (for logging), and CloudFront.

CodePipeline

Alluded to in our CodeBuild configuration, we need a way to source our React source files to our CodeBuild application as well as a means to trigger the build. To do this we will use Amazon’s Continuous Integration solution, CodePipeline.

Since CodePipeline will need to link up with Github we will need to tell it where our repo lives and give it access rights. To prevent hardcoding this information in our template and allow for easier use on future projects, we can parameterize this information with a Parameters declaration, which we can place at the top of our CloudFormation template.

This allows us to specify these as input fields in the CloudFormation stack creation wizard when we go to create our stack.

Now that we have our repo configuration, we can setup our CodePipeline pipeline.

Our config consists of 3 resources

  • CodePipeline declaration
  • CodePipelineRole: an IAM role that will give our pipeline the necessary permissions to start CodeBuild builds and interact with S3
  • PipelineBucket an S3 bucket which will hold the artifacts from each stage of the pipeline. This serves as a temp space for storing the results of each CodePipeline stage

Looking more closely at our CodePipeline configuration, we see that it is made up of 2 stages.

  • Source stage, which takes our Github configuration and performs a checkout of the source files, storing it in the PipelineBucket
  • Build stage, which takes the source files in the PipelineBucket and passes it to our CodeBuild build and triggers the build.

Putting it All Together

We have all the pieces to our pipeline and are ready to upload it to CloudFormation and create our stack. (The completed template consisting of everything we put together can be found here.)

From the AWS console, navigate to CloudFormation and click on ‘Create Stack

This will bring up a wizard from which we can create our stack. Select ‘Upload a template to Amazon S3’ and upload your cloudformation.yml file and click “Next”

Upload your cloudformation YAML file.

On the Specify Details screen we can give a name for our stack, say “my-test-app”. CloudFormation will use the stack name in the naming of the AWS resources we create to help differentiate them from other resources.

In the “Parameters” section, we see the Parameters we configured in our CloudFormation template which will allow CodePipeline to checkout our code from Github. Go ahead and fill those fields in with the React app you wish to build. If you don’t know what a personal access token is and how to make one, scroll to the bottom of this post and refer to the section “Appendix: Github Personal Access Token”. After you enter the required information, click “Next”.

Enter your app name and github personal token

On the “Options” page, just leave the defaults and click “Next”

Options page. Leave this blank

On the final “Review” page, confirm your settings for the Github repo and stack name and at the very bottom of the page be sure to check “I acknowledge that AWS CloudFormation might create IAM resources.” This checkbox just acknowledges that our template will be creating 2 IAM roles as part of the stack creation process.

With everything filled in and checked, click “Create” to create the stack.

acknowledge the creation of IAM roles by this template

Building the Stack

From the CloudFormation main page, we can click on our stack and view the creation progress. The whole process will take a few minutes, especially the CloudFront distribution so just be patient. After about 10–15 minutes, if everything was set up correctly, the Stack creation should finish with “CREATE_COMPLETE”.

Once everything is done we can take a look at what was created

Go to the CloudFront dashboard in the AWS console and you should see the distribution our stack created

Click on the distribution and find the domain name for our distribution. Copy that and put that into a new browser tab.

It works!

But Wait, There’s More!

Now let us test out making a change by changing the “Welcome to React” messaging and push it up to github.

After a couple of minutes we can refresh our browser and see the updated changes all without having to manually run any builds.

What’s Next

From here you can build on the CloudFormation template we developed to add further features such as a backend service to go with the front end we have created. If you are interested in learning more about AWS or CloudFormation, I recommend checking out the great courses on A Cloud Guru such as “Create a Serverless Portfolio with AWS and ReactJS” by Robin Norwood which inspired this post.

Links

Appendix: Github Personal Access Token

To create a personal access token, login to your github and navigate to https://github.com/settings/tokens and click on “Generate new token”

You’ll need to choose a token name and assign the scope to your token. For our CloudFormation template, we only need repo scope. Now click ‘Create’ at the bottom of the form.

You’ll be presented with the access token which you can copy and paste into the CloudFormation input field for the access token. You’ll only need to enter this token in once when you create your stack, but if your token ever gets compromised you can revoke your old token and replace it in your CodePipeline configuration with a new token.

Don’t get your hopes up, the token pictured has been revoked since taking the screenshot ;)

--

--

Jeffrey (Bongo) Russom

Father, husband, and web developer from Austin, TX currently a UI developer at Indeed.com