Yost's Posts
Quick, step-by-step JavaScript tutorials

Deploy React Apps to AWS: Part 3 - Create a Hosting S3 Bucket with CloudFormation

November 29, 2019

Before going on with this tutorial, be sure you’ve got AWS CLI setup and you’re comfortable with stuff already covered.

Here are the previous parts in this tutorial series.

  1. Setting up the AWS CLI
  2. Create an S3 Bucket and Host a React App Manually

What you’ll do in this tutorial

In this tutorial, you’re going to do what we did in Part 2, but instead of doing all of the S3 configuration and creation manually in the AWS Console, you’re going to run a single command that provisions the S3 Bucket automatically. Of course, there’s a configuration file (CloudFormation template) you need for that command to do what we need it to do, but we’ll get to that.

Why care?

  • If you’re building single-page React apps (or any other type of static site ) on a regular basis, then you’re going to need to create S3 Buckets. Doing that manually is annoying, time consuming and prone to human mistakes.
  • Leveraging CloudFormation templates to determine how AWS resources are provisioned and configured will certainly save you time in the long-run, especially as your cloud architecture becomes more complex, e.g. a later part of this series will involve CloudFront distributions, staging environments, and custom domain names.
  • And if you work with others on a project deployed on AWS, then a CloudFormation template will help document your deployment setup and make it trivial for anyone to redeploy the app.
  • Treating buckets and other AWS resources as disposable is a helpful way to approach cloud hosting. Your apps can be taken down and deployed back to the cloud really quickly.

Tutorial Time

Create a React app to deploy

As you probably guessed, we’ll quickly create an app with create-react-app.

npx create-react-app deploy-bucket-example
cd deploy-bucket-example
npm start

If the setup worked, the app should open at http://localhost:3000

Create a CloudFormation Template

CloudFormation Templates are “for the service or application architectures you want and have AWS CloudFormation use those templates for quick and reliable provisioning of the services or applications (called “stacks”)“.

So, CloudFormation templates list and configure a stack of resources that can be provisioned when that template is executed/run. These templates can be written in json or yaml. We’ll use yaml.

In the root of your project, create a yaml file called cloudformation_basic.yml.

> touch cloudformation_basic.yml

Paste the following stuff into it.

# Create an S3 Bucket that hosts a React app
# Use AWS CLI to execute the file like the below snippet
# aws cloudformation deploy --template-file ./cloudformation_basic.yml --stack-name basic --parameter-overrides BucketName=<BUCKET_NAME>
AWSTemplateFormatVersion: 2010-09-09
Parameters: # params passed to "--parameter-overrides" in CLI
BucketName:
Description: Unique name for your bucket. This will be in the S3 url to your React app.
Type: String
Resources:
# Create an S3 Bucket that serves a static website (i.e. React app)
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
AccessControl: PublicRead # visitors need to be able to access the sie
WebsiteConfiguration: # this makes the S3 Bucket a static website/app
IndexDocument: index.html # default object served when visiting S3 domain
ErrorDocument: index.html # just send to app, let React handle errors and routing
# Add a Bucket Policy that lets public visitors access the web app
MyBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref MyBucket # attach to bucket being created
PolicyDocument:
Id: MyPolicy
Version: 2012-10-17
Statement: # lets the public access/view the contents of your Bucket, i.e. web app
- Sid: PublicReadForGetBucketObjects
Effect: Allow
Principal: '*' # wildcard, allow all requests
Action: 's3:GetObject'
Resource: !Join ['', ['arn:aws:s3:::', !Ref MyBucket, /*]]
Outputs:
WebsiteURL:
Value: !GetAtt MyBucket.WebsiteURL
Description: URL for website hosted on S3

You can use the docs and comments in the gist to get a feel what’s going on, but let’s quickly walk through the main aspects of the file.

  • AWSTemplateFormatVersion - a CloudFormation requirement. There’s only one format version so far so it’s just a given for any template.
  • Parameters - optional parameters for your template, so you can customize the provisioned resources based on who’s executing the template. In this example, everyone will need a unique BucketName, so that’s a parameter that we’ll pass through to the template via the CLI.
  • Resources - the meat of a template, where AWS resources are declared and configured for provisioning.

    • MyBucket is the bucket that will host your React app. The MyBucket is the name of the resource and can be anything, but it’s nice to keep track of what resources are for what with specific names. Just one bucket here, so not an issue. Also, note that we need to reference MyBucket in other areas of the template.
    • MyBucketPolicy is the policy for MyBucket. The name of the resource MyBucketPolicy is arbitrary just like MyBucket. The bucket policy is configured to allow public access to our hosting bucket.
  • Outputs - Not too important here, but here are the docs

Here’s some notable syntax stuff in this template that are helpful in all templates.

Provision a bucket with the CloudFormation Template

Now we’ll actually execute our CloudFormation Template and create a static website hosting S3 Bucket.

Add this command to the scripts in your package.json, but add a unique bucket name rather than the placeholder.

{
  ...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "provision": "aws cloudformation deploy --template-file ./cloudformation_basic.yml --stack-name hosting-bucket --parameter-overrides BucketName=<BUCKET_NAME>"
  },

  ...
}

Here’s a quick breakdown of the aspects of this command…

  • cloudformation is the AWS service we’re using
  • deploy is a CloudFormation action to execute a template and provision the resources specified. Check out ze docs
  • --template-file specifies the template to execute
  • --stack-name is an arbitrary name for your stack, which can be used to reference later in the AWS Console or if updating it
  • --parameter-overrides is how you pass parameters defined at the top of the CloudFormation templates

Now execute the command to create the stack specified in the template.

> npm run provision

Once executed, you should see some output like the following.

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - basic

Verify everything in AWS Console

To see that the bucket was actually created, visit the AWS console and check that the bucket is in your list of S3 Buckets.

Also, find the CloudFormation section of your AWS Console. The main page of that lists your stacks, where you should see the “basic” stack. You can click through to the details of the stack you just executed by running the provision script.

Image

Upload the React app to your bucket

Add another script called upload, again replacing the <BUCKET_NAME> with the one you added to the provision script.

The app needs to be built for production before it can be uploaded to your bucket, so add a script that both builds then uploads your app, called deploy.

{
  ...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "provision": "aws cloudformation deploy --template-file ./cloudformation_basic.yml --stack-name hosting-bucket --parameter-overrides BucketName=<BUCKET_NAME>",
    // new scripts below
    "upload": "aws s3 sync build/ s3://<BUCKET_NAME> --delete",
    "deploy": "npm run build && npm run upload"
  },

  ...
}

Now you can deploy your app to your S3 bucket by running the below command.

npm run deploy

Now you can visit your bucket’s endpoint (find at AWS Console > S3 > Bucket > Properties > Static Website Hosting > Endpoint) and see the app there!

And that’s it!

You can now host a React app in an S3 bucket and deploy it via the CLI. But that’s just the tip of the iceberg!

Stay tuned for future installments of the series that will cover topics such as…

  • distributing an S3 website via CloudFront (CDN)
  • custom domains
  • production and staging deployments
  • automated provisioning with CloudFront
  • CI/CD integration (CircleCI)

If you want to see a single app with all of those topics implemented, check out react-spa-starter, an open source React/Redux boilerplate I’m working on that inspired this tutorial series.

Get tutorials in your inbox

Simple, quick, step-by-step tutorials that teach you how to implement JavaScript tech like React, Node and supporting players.

No spam and unsubscribe whenever ya want.

powered by TinyLetter

Ryan J. Yost

Hi, I'm Ryan. I live and work in Chicago as a Front End Engineer. I'm always building side projects and write JavaScript-related tutorials that help folks build things, too. Follow me on Twitter