Exercise #3

After analyzing our sales over the past several seasons, we found that our most popular product is the Muskie Casting Rod! We need to ensure that people buying this product don’t cause our website to be overloaded and stop other customers buying our Tourney Casting Rod.

In order to accomplish this, we’re going to leverage another feature of AWS CloudFront. AWS CloudFront allows you to specify traffic routing behavior based on the request URI. Since we want to ensure the traffic coming to our most popular product scales with the demand, we can leverage a feature in AWS CloudFront that allows for multiple origins and multiple behaviors and use these to dictate how traffic reaches them.

Thankfully our initial deployment during Exercise #0 deployed an Amazon S3 Bucket already configured securely against our AWS CloudFront distribution. You can read more about how this serves traffic from a Private S3 bucket in the documentation at Restricting Access to Amazon S3 Content by Using an Origin Access Identity.

Part 1 - Split Origin CloudFront

Before we get started, we need to upload the content we want to serve to our target S3 bucket. This does not mean that that content has to be in a public bucket!

  1. The static website needs building so, in the Cloud9 Console:
1
2
3
cd ~/environment/frontend_minisite
yarn
API_URL=`../tools get_value ShopBackendEdgeApiUrl`/ping yarn build

Note

This last command can take 2-3 minutes to execute.

Attention

During the last command you may see yellow WARNING lines about oversized assets - these can all be safely ignored.

We have now compiled our static website into a format suitable to be hosted.

  1. We need to upload our static website to an Amazon S3 bucket:
1
2
cd dist
aws s3 cp --recursive . s3://`~/environment/tools get_value CDNStaticBucket`/shop/rods/muskie-casting-rod
  1. Go to Services -> CloudFront in another tab - you can hold ⌘ (Ctrl on Windows) and Click the link to achieve that quickly.
  2. Click the distribution ID (starts with an E) you were working with earlier in Exercise #1.
  3. Click the Behaviors tab.
_images/cf_behaviors_tab.png
  1. Click Create Behavior
  2. Enter shop/rods/muskie-casting-rod/* in the Path Pattern field
  3. Select origin1 as the Origin.
  4. Select Redirect HTTP to HTTPS for Viewer Protocol Behavior
  5. Leave everything else as the default and click Create at the bottom
_images/cf_create_behavior.png
  1. Let’s now guarantee nothing is cached by using an Invalidation. Click the Invalidations tab.
  2. Click Create Invalidation and put in /shop/rods/muskie-casting-rod/* for the Object Path.
  3. Click Invalidate.
_images/cf_invalidations.png
  1. Wait for the In progress to change to Complete. This takes a couple of minutes.
  2. Browse to the Shop in another tab and go to Rods then click on the Muskie Casting Rod. It won’t work properly and you’ll get an Access Denied error. Let’s go about fixing that.

Part 2 - Fixing our product page

We better do this quickly as we’ve broken production! Oops!

The reason we got an error in the previous step is because, when you request a URL from the browser with nothing on the end, there is no document to serve. This is normally something done by setting a Default Document. Although AWS S3 supports doing this if we were using S3 websites, we are not and so we need to do this via AWS Lambda@Edge.

  1. In the Cloud9 Terminal
1
2
3
cd
cd environment
./tools update_edge_lambda

This will upload a Lambda function which allows for us to show index.html as a default item. Shown here is the code that we just uploaded to our Lambda function.

'use strict';

/**
* Add index.html to the source uri to handle a default URI
* in a single behaviour with other content.
*
* Taken from Ronnie Eichler's blog post:
* https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/
*/
exports.index_rewrite = (event, context, callback) => {
    // Extract the request from the CloudFront event that is sent to Lambda@Edge
    var request = event.Records[0].cf.request;

    // Extract the URI from the request
    var olduri = request.uri;

    // Match any '/' that occurs at the end of a URI. Replace it with a default index
    var newuri = olduri.replace(/\/$/, '\/index.html');

    // Replace the received URI with the URI that includes the index page
    request.uri = newuri;

    // Return to CloudFront
    return callback(null, request);
};
  1. The output from the first step will be some JSON. Grab the string for FunctionArn - it will look something like:
1
arn:aws:lambda:us-east-1:195566315316:function:TheFishingShopWorkshop-CDNLambdaBackendC6AFFDEF-Q3GQNY0WBIL0:2

Note

The number appearing on the end is the version number. If there isn’t a :2 at then likely you have the wrong entry from the output.

  1. Go to Services -> CloudFront in another tab - you can hold ⌘ (Ctrl on Windows) and Click the link to achieve that quickly.
  2. Click the distribution ID (starts with an E) of the Distribution that points to an origin starting with ‘thefishing’ – it’s the only one if this is a fresh account.
  3. Click the Behaviors tab.
_images/cf_behaviors_2.png
  1. You want to Edit Behavior which is the one we created in Part 1. It will have a path pattern starting with shop
  2. Scroll down to Lambda Function Associations, select Origin Request as the Event Type in the Select Event Type drop down and then paste in the ARN you copied in Step 2 above.
_images/cf_edge_definition.png
  1. Click Yes, Edit
  2. Now head back to the Services -> CloudFront page and wait for the distribution status to change to Deployed - this may take 3-5 minutes but if it doesn’t change after 5 minutes, feel free to continue.
  3. Let’s now guarantee nothing is cached by using an Invalidation. Click the Invalidations tab.
  4. Select the Invalidation you already made and Click Copy.
  5. Now click Invalidate.
  6. Wait for the In progress to change to Complete. This takes a couple of minutes.