Fabrizio Fortunato

Build a serverless website with SAM on AWS

July 17, 2019

A website can be considered serverless when it doesn’t need, directly, any server to operate. Serverless is quickly becoming a popular choice for running your services and also your entire website. Adopting a serverless architecture enables you and your organisation to focus on delivering business value. Every application must have an inherent amount of irreducible complexity states the Law of conservation of complexity, the only question is who will have to deal with it. Using serverless we are offloading this complexity to someone else can take care of this for us.

Working at RyanairLabs, I have the opportunity to work with amazing people every day and to try out serverless architecture for the busiest travel website in Europe. Multiple pages of the Ryanair website are built using serverless technologies. Check out how we are using AWS serverless technologies in my talk at AWS Summit London.

Serverless services

AWS has a wide range of serverless services entirely managed by AWS. What we will be focusing here are the basic building blocks to deploy a serverless website. The services are:

AWS serverless services for a website

Cloudfront: a high available CDN delivering your content worldwide through edge locations. We will use Cloudfront to take advantage of the edge caching for our website assets, routing each user request through the nearest edge location, without the need of contacting back the origin each time. Cloudfront also ensures the first level of security for our website, establishing a secure connection with the clients and by using WAF.

S3 an object storage service designed to be easy to use and managed. A website page is just a collection of static resources glued together by HTML. While developing web applications, a popular choice is to use a single page application (SPA) frameworks to reduce server-side complexity and improve interactivity. Ryanair website, for example, is built using Angular and Angularjs. Another rising technology is Javascript API Markup (JAM), fueled by static site generators. Both those technologies share a simple approach: they produce static assets which can be easily stored and served from an S3.

Lambda at the Edge (L@E), you can execute any function closer to the viewer to add any additional logic between the edges and the origin. L@E acts as a glue between Cloudfront and the origin(s).

SAM

We will use sam-cli, an open-source framework for building a serverless application by AWS to reduce the boilerplate while working with Lambda @ Edge in the next articles.

The assumption is that we have already configured aws-cli and sam-cli, if that is not the case you can follow the guide here

Basic Infrastructure template

Building an application using sam-cli we will be using Cloudformation templates to describe our infrastructure resources. It gives us the ability to replicate, reuse and share infrastructure between projects quickly. A Cloudformation template powers all the Frontend projects that we are running in RyanairLabs, treating your infrastructure enables us to understand changes quickly, version them and collaborate on the infrastructure.

Working with sam-cli or Cloudformation we tend to keep all the resources of a project in a single file making it easier to manage and deploy the resources. What we are looking to generate in our first iteration is a simple infrastructure that can serve any assets.

Basic infrastructure

Let’s start by creating the template file called template.yaml with some of the basic services that we will need:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  Serverless website

Resources:
  CloudFrontOriginAccessIdentity:
    Type: 'AWS::CloudFront::CloudFrontOriginAccessIdentity'
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: 'Serverless website OA'

  CloudfrontDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        Comment: "Cloudfront distribution for serverless website"
        DefaultRootObject: "index.html"
        Enabled: true
        HttpVersion: http2
        # List of origins that Cloudfront will connect to
        Origins:
          - Id: s3-website
            DomainName: !GetAtt S3Bucket.DomainName
            S3OriginConfig:
              # Restricting Bucket access through an origin access identity
              OriginAccessIdentity: 
                Fn::Sub: 'origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}'
        # To connect the CDN to the origins you need to specify behaviours
        DefaultCacheBehavior:
          # Compress resources automatically ( gzip )
          Compress: 'true'
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
          ForwardedValues:
            QueryString: false
          TargetOriginId: s3-website
          ViewerProtocolPolicy : redirect-to-https

  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      # Change bucket name to reflect your website
      BucketName: <YOURSWEBSITE.COM>

  S3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
      # Restricting access to cloudfront only.
        Statement:
          -
            Effect: Allow
            Action: 's3:GetObject'
            Resource:
              - !Sub "arn:aws:s3:::${S3Bucket}/*"
            Principal:
              AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${CloudFrontOriginAccessIdentity}"

Going through the template file defined we have different services listed here:

CloudfrontOriginAccessIdentity, used to restrict access to the Bucket files only for requests coming through Cloudfront. We don’t have to make our Bucket public or neither enable website hosting by using an OriginAccessIdentity. The OriginAccessIdentity will be used in the CloudfrontDistribution Origin and the S3BucketPolicy.

CloudfrontDistribution, it is AWS CDN to deliver data and content with low latency globally. There are a series of attributes in the DistributionConfig which controls the CDN. I left different comments on the file to explain further if you want to have a look at a complete list of the attributes you can follow the User Guide.

S3Bucket, this is the Bucket where we will store all the assets of the website. Remember to give the BucketName a correct and unique identifier for example we can use the domain name of the website to help you remember which Bucket belongs to which website.

S3BucketPolicy, which defines who can access and what type of operations are permitted in the Bucket. Using the OriginAccessIdentity we are restricting read access only to Cloudfront.

Deploying infrastructure

We can already start deploying our first serverless website only with the four resources listed before. We will host the website assets on the S3Bucket, Cache and serve content in Cloudfront. To deploy the infrastructure with sam-cli we need to generate first an S3 Bucket used to store the deployment package containing all the different inputs for your infrastructure.

On the commands below remember always to substitute <YOURSWEBSITE.COM> with the actual domain name of your website.

aws s3 mb s3://<YOURSWEBSITE.COM>-sam --region=us-east-1

After the Bucket is created we can package the application:

sam package --output-template-file packaged.yaml --s3-bucket <YOURSWEBSITE.COM>-sam

And finally deploy the application:

sam deploy --template-file packaged.yaml --stack-name <yourwebsite> --capabilities CAPABILITY_IAM --region us-east-1

We are using us-east-1 as a default region for our infrastructure to reduce some of the limitations that we will encounter later on while defining Lambda @ Edge, where they can only be deployed into that region.

Creating the Cloudfront distribution will take some time, generally between 20-30 mins at first. The Distribution, after the deployment is completed, will have an AWS generated domain name with the format d3k1beyfkv2133.cloudfront.net usable for making our first request to the website.

To test out that everything is working we can upload an index.html in the root folder of the S3Bucket eg:

<html>
<body>
  <h1>Hello serverless</h1>
  <img src="https://github.com/awslabs/serverless-application-model/blob/master/aws_sam_introduction.png">
</body>
</html>

To upload the file we can use the aws-cli command:

aws s3 cp index.html s3://<YOURWEBSITE.COM> --acl public-read

Once the file is uploaded if we now navigate to the Cloudfront DomainName we should be able to see:

Hello serverless page

In the next articles to come, we will explore how we can serve multiple pages, secure, handle rewrites, associate a domain and finally how to correctly deploy assets on our serverless website. This is a lot to cover in a single article so don’t forget to follow me on twitter @izifortune to get updates.

PS: AWS CloudDevelopmentKit

The AWS CDK is now generally available, if you are looking to have programmatic access to create AWS resources have you can have a look at the examples below: https://aws.amazon.com/blogs/aws/aws-cloud-development-kit-cdk-typescript-and-python-are-now-generally-available/


Fabrizio Fortunato
Head of Frontend at RyanairLabs @izifortune