DEV Community

Cover image for AWS project - Module 1. Host a Static Website on AWS S3 via CloudFormation
Samira Yusifova
Samira Yusifova

Posted on • Updated on

AWS project - Module 1. Host a Static Website on AWS S3 via CloudFormation

Overview

This series of articles provide a step-by-step guide on how to deploy a basic static website to an Amazon S3 (Simple Storage Service) bucket using AWS CloudFormation. Starting with a simple architecture, each module identifies gaps and upcoming issues, and gradually introduces additional layers of complexity to resolve those issues. The steps involved registering a custom domain, creating S3 buckets, configuring Route 53, obtaining SSL/TLS certificate, creating an Origin Access Identity (OAI), setting up CloudFront distributions, and routing DNS traffic using Route 53. By leveraging the power of CloudFormation, developers can automate the infrastructure setup and deployment process, making it easier and more efficient to launch static websites.

In this module, we are going to deploy a simple static website to S3 bucket via AWS CloudFormation in three simple steps:
πŸ‘‰ Step 1. Create a simple static web app via Angular
πŸ‘‰ Step 2. Create S3 bucket and its policy, and configure it to host a static website
πŸ‘‰ Step 3. Build and deploy static website manually to newly created S3 bucket (optional, you can skip it and go to Module 2 for build automation)

In next module we will automate the deployment via CodeBuild.

As I’m strongly against managing environments manually and take Infrastructure as Code for granted (if we are not on the same page, I’d suggest you to read this article first), AWS CloudFormation can be a great choice to provision and manage the complete infrastructure and AWS resources in a text file.

Architecture

The high-level architecture for our project is illustrated in the diagram below:

Image description

Source code

Source code for this project is available on GitHub in a public StaticWebsiteHostingToS3 repository.
πŸ‘‰ Check for frontend source code here
πŸ‘‰ Check for CloudFormation template here

Initial Setup

Install required tools:
πŸ‘‰ any IDE (personally I prefer Visual Studio Code)
πŸ‘‰ git

Optional:
I’m going to create a simple static website using Angular. You can use any other framework/library if you want.
πŸ‘‰ Angular, Node.js and NPM - v16 or greater
πŸ‘‰ Angular CLI

    # run the following command to install Angular CLI globally
    npm install -g @angular/cli
Enter fullscreen mode Exit fullscreen mode

AWS Resources

Here is the list of AWS resources that we are going to create:
πŸ‘‰ S3 bucket
πŸ‘‰ S3 bucket policy

Why host static website on S3?

Why should you host your static content on S3? Let's break it down!

β˜‘οΈ Firstly, you no longer need to allocate a specific amount of storage space or plan for it, as S3 buckets automatically scale to accommodate your needs.

β˜‘οΈ Moreover, since S3 operates as a serverless service, you are relieved of the burden of managing and patching servers that store your files; you simply upload and retrieve your content.

β˜‘οΈ Additionally, even if your application requires a server (such as a dynamic application), it can be smaller because it doesn't need to handle requests for static content.

Step 1. Create a simple static web app

Shortcut: get the source code for frontend here (but you still need to install Angular to run it locally unless you want to skip manual build and go to Module 2 for build automation).

Let’s create a static Angular app from scratch.

1️⃣ Firstly, create a new folder and name it StaticWebsiteHostingToS3 which will store two folders:

  • cloudformation folder (stores CloudFormation templates)
  • frontend folder (stores static website source code)
# create a parent folder 
mkdir StaticWebsiteHostingToS3
cd StaticWebsiteHostingToS3

# initialize a repository
git init

# create a folder that stores CloudFormation templates
mkdir cloudformation 

# create a folder that stores static website source code
mkdir frontend 
Enter fullscreen mode Exit fullscreen mode

2️⃣ Secondly, create an initial Angular project by simply running the following commands in terminal:

# navigate to folder that should store frontend 
cd frontend

# create a new Angular project
ng new app-for-aws
# check β€˜y’ and β€˜SCSS’ to prompt questions such as:
# - Would you like to add Angular routing? (y/N) y
# - Which stylesheet format would you like to use? SCSS

# run the app locally
cd app-for-aws
ng serve
Enter fullscreen mode Exit fullscreen mode

3️⃣ Navigate to http://localhost:4200/

Image description

You can customize the website as you wish. Now, let's provision some resources in AWS to host our website.

Step 2. Create S3 bucket and its policy, and configure it to host a static website

Amazon S3 is a perfect AWS service to host a static website.

In order to host our website on S3 bucket, let’s create and configure all required resources via CloudFormation.

Shortcut: get the full CloudFormation template here.

1️⃣ The following piece of code helps to create a new S3 bucket and configure it to host a static website:

Resources:
  myStaticWebsiteHostingBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      BucketName: your-bucket-name
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
Enter fullscreen mode Exit fullscreen mode

2️⃣ Next step is to set S3 bucket permissions for website access.

When you configure a bucket as a static website, if you want your website to be public, you must disable block public access settings for the bucket and write a bucket policy that grants public read access.

That is why we are going to disable ACLs for our bucket under PublicAccessBlockConfiguration property and set the ownership of S3 bucket content to Object writer (i.e. AWS account that uploads an object owns the object and has full control over it).

      OwnershipControls:
        Rules:
          - ObjectOwnership: ObjectWriter
      PublicAccessBlockConfiguration:
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false
Enter fullscreen mode Exit fullscreen mode

Basically we are disabling S3 Block Public Access settings to let users get a public access to the website.

Image description

One issue I faced with while configuring access settings and building the stack was the following:

Bucket cannot have public ACLs set with BlockPublicAccess enabled (Service: Amazon S3; Status Code: 400; Error Code: InvalidBucketAclWithBlockPublicAccessError)

The root of the issue was in using AccessControl property with PublicAccessBlockConfiguration property in the template. It should work with existing buckets and fail with all new buckets.

FYI, AWS has changed the default settings of ACLs of S3 bucket:

Starting in April 2023, Amazon S3 will introduce two new default bucket security settings by automatically enabling S3 Block Public Access and disabling S3 access control lists (ACLs) for all new S3 buckets. Once complete, these defaults will apply to all new buckets regardless of how they are created, including AWS CLI, APIs, SDKs, and AWS CloudFormation. These defaults have been in place for buckets created in the S3 management console since the two features became available in 2018 and 2021, respectively, and are recommended security best practices. There is no change for existing buckets. (source)

To resolve the issue, I've commented out AccessControl property:

      # AccessControl: PublicRead # throws an error: Bucket cannot
# have public ACLs set with BlockPublicAccess enabled
Enter fullscreen mode Exit fullscreen mode

3️⃣ Next step is to create an S3 bucket policy

We need to give a public read access to S3 bucket objects. Here is the policy:

   myStaticWebsiteHostingBucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket: !Ref myStaticWebsiteHostingBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: '*'
            Action: 's3:GetObject'
            Resource: !Join 
              - ''
              - - 'arn:aws:s3:::'
                - !Ref myStaticWebsiteHostingBucket
                - /*
Enter fullscreen mode Exit fullscreen mode

We can turn versioning on for our S3 bucket so that in case of bugs in new version we can easily roll back the deployment to previous version. Simply add VersioningConfiguration under bucket's property:

Resources:  
  myStaticWebsiteHostingBucket:
    Type: 'AWS::S3::Bucket'
    .... 
    Properties:
      VersioningConfiguration:
        Status: Enabled
Enter fullscreen mode Exit fullscreen mode

4️⃣ We are done with Resources, now let's output the website URL to make it easier to navigate using the link once the stack is built.

Outputs:
  outputWebsiteURL:
    Value: !GetAtt 
      - myStaticWebsiteHostingBucket
      - WebsiteURL
    Description: Static website URL
Enter fullscreen mode Exit fullscreen mode

5️⃣ We are ready to create and run CloudFormation stack based on our template.

If you need to understand how to create a stack in AWS Console, please read Hands-on AWS CloudFormation - Part 1. It All Starts Here.

I prefer to run the stack from AWS Console as it provides an end-to-end overview of a process flow. But you can always run a stack using the AWS CLI (here is how).

Upload our template file to create a stack. Change the value of paramStaticWebsiteHostingBucketName input parameter to something unique. Then run the stack.

Image description

Once the stack built, you should see a newly created bucket and it's policy under Resources tab:

Image description

Under Outputs tab you can find the link to our website.

Image description

Currently you should get 404 error as we haven't deployed static content to S3 bucket yet.

Image description

Step 3. Build and deploy static website manually to newly created S3 bucket

This step is optional - you can skip it and go to Module 2 for build automation.

1️⃣ First, we need to build the production version of our Angular app locally.

Open terminal for our StaticWebsiteHostingToS3 app. By default, ng build command uses the production build configuration:

cd frontend/app-for-aws
ng build
Enter fullscreen mode Exit fullscreen mode

A new dist folder should be autogenerated with static content.

Image description

2️⃣ In AWS Console navigate to our S3 bucket and upload all the files from dist/app-for-aws folder:

Image description

Don't forget to click on "Upload" button:

Image description

Image description

3️⃣ Now refresh the website link - our Angular app should be up and running:

Image description

Cleanup

You might be charged for running resources. That is why it is important to clean all provisioned resources once you are done with the stack. By deleting a stack, all its resources will be deleted as well.

Note: CloudFormation won’t delete an S3 bucket that contains objects. First make sure to empty the bucket before deleting the stack. Of course, you can automate the process by creating a Lambda function which deletes all object versions and the objects themselves. I'll try to cover that part in further article.

Summary

In this module, we have created a simple static web app and hosted it on S3 bucket. However we took baby steps to deploy our static content to S3 bucket manually. Ideally we want to use a tool that would rebuild the source code every time a code change is pushed to the repository and deploy built files to S3 bucket automatically. In Module 2 I'll show you step by step how to automate the build and deployment via AWS CodeBuild.

Top comments (0)