How to host a static website on AWS with S3, CloudFront, Route53 and Terraform
Tags: aws, terraform, websitehosting, s3staticwebsite
Published: 2022-05-16 08:03:30

# How to host a static website on AWS with S3, CloudFront, Route53 and Terraform


Hosting a website on AWS using Cloudfront, S3 and Route53 is a popular solution for Amazon web hosting. It typically costs $3 a month outside of the generous free tier and $0.50 within the free tier. Terraform can automate the deployment of this finicky architecture making it much easier to deploy, manage and automatically upload website content. We will use an open-source Terraform module to deploy a secure, scalable and performant website on AWS based on Route 53, CloudFront and S3.

## Architecture

The vast majority of Terraform tutorials I see covering similar topics all focus on recreating an identical architecture from stack utilising resources which is simply a waste of time and effort. Existing tutorials and modules generally follow the architecture shown below which is based on the AWS tutorials [Configuring a static website on Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/HostingWebsiteOnS3Setup.html) and [Speeding up your website with Amazon CloudFront](https://docs.aws.amazon.com/AmazonS3/latest/userguide/website-hosting-cloudfront-walkthrough.html). Since they are based on tutorials, the following flaws remain:

![The architecture that most tutorials and guides follow](https://interweave.cloud/wp-content/uploads/2022/05/Architectures.png)

The architecture that most tutorials and guides follow

- Two buckets are created when only one is really required.
- The root bucket is publicly exposed. Public S3 buckets have been at the centre of several high profile data leaks. Due to this, it is increasingly becoming corporate policy to completely disallow public S3 buckets.
- The users still have the ability to access the bucket objects directly from the s3 website endpoint. This circumvents the CloudFront distribution and can nullify CloudFront features like geographic restrictions.
- A manual activity remains to upload the website content onto the S3 bucket.

![The improved architecture of what will be deployed in this guide](https://interweave.cloud/wp-content/uploads/2022/05/Architectures-Proposed-Architecture.drawio.png)

The improved architecture of what will be deployed in this guide

My module "[s3-cloudfront-static-website](https://registry.terraform.io/modules/InterweaveCloud/s3-cloudfront-static-website/aws/latest)" uses the architecture shown above which represents a much simpler, secure and straightforward architecture.

- This only uses one bucket for both the root and www. domains.
- The bucket is private and CloudFront accesses the website content utilising an Origin Access Identity with IAM permissions to the S3 Bucket.
- Users can only access the website through CloudFront.
- Website content can be automatically uploaded during Terraform deployment (uses AWS CLI).

## Pre-requisites

- Domain name
- Route 53 Hosted Zone which is the DNS Provider for the domain
- [AWS CLI](https://aws.amazon.com/cli/ "AWS CLI") locally installed with a configured named profile
- [Terraform CLI locally installed](https://learn.hashicorp.com/tutorials/terraform/install-cli)

If you require assistance with the pre-requisites review the following subsections.

### Domain Name

To use a domain name, you must purchase the domain from a registrar like [Google](https://domains.google/), [Go Daddy](http://Go Daddy), [123-Reg](http://www.123-reg.co.uk), [names.co.uk](http://www.names.co.uk) or [AWS Route 53](https://us-east-1.console.aws.amazon.com/route53/home#DomainRegistration:). Using AWS Route 53 is easiest since AWS will automatically create a hosted zone for the domain, assign four name servers to the hosted zone, and then update the domain registration to use those name servers. However, it is normally the most expensive option for a domain. I personally browse for the best price for my desired main domain and find names.co.uk is a great site for free domains for testing or development. Beware of the higher prices after the first year. I also turn off auto-renewal - especially for free domains.

### Route 53 Hosted Zone as DNS Service for the Domain

If you bought your domain through AWS Route 53, you can skip this section. If you bought the domain from another website, you need to create a route 53 hosted zone and make [Route 53 the DNS service for your domain](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/migrate-dns-domain-inactive.html). Put simply, the [Domain Name Service (DNS)](https://aws.amazon.com/route53/what-is-dns/) is the phonebook of the internet, when a user inputs your domain name, DNS will return the correct IP address to access your site. [Name servers](https://kinsta.com/knowledgebase/what-is-a-nameserver/) are underlying servers which contain and return the information of which IP addresses correspond to which services on your domain. To make Route 53 the hosted zone for your domain, you must change the name servers from your registrar's to your Route 53 hosted zone's.

#### How to create a Route 53 Hosted Zone

1. Sign in to the AWS Management Console and open the [Route 53 console](https://console.aws.amazon.com/route53/).
2. If you're new to Route 53, choose **Get started** under **DNS management**.  
    If you're already using Route 53, choose **Hosted zones** in the navigation pane.
3. Choose **Create hosted zone**.
4. In the **Create Hosted Zone** pane, enter the name of the domain that you want to route traffic to. You can also optionally enter a comment. The domain name for this website is interweave.cloud - you do not need to include 'www.' or 'HTTPS://'.
5. For **Type**, accept the default value of **Public Hosted Zone**.
6. Choose **Create**.

##### Update the name servers for the domain

1. Open the Route 53 console at [https://console.aws.amazon.com/route53/](https://console.aws.amazon.com/route53/)
2. In the navigation pane, choose **Hosted zones**.
3. On the **Hosted zones** page, choose the radio button (not the name) for the hosted zone, then choose **View details**.
4. On the details page for the hosted zone, choose **Hosted zone details**.
5. Make note of the four servers listed for **Name servers**.
6. Use the method provided by the registrar for the domain to change the name servers for the domain to use the four Route 53 name servers you got from step 5. _NOTE: it can take up to 3 days for the change to become effective._

### AWS CLI

To upload your files to the s3 bucket automatically, the AWS CLI tool is used. Therefore, it is required for the [AWS CLI](https://aws.amazon.com/cli/) to be installed on your local machine - instructions can be found [here](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). You also need to configure a local named profile using the `aws configure --profile NAME` command. Using named profiles is generally better than static credentials within Terraform.

## Terraform Code

The terraform module is available on the [Terraform Registry](https://registry.terraform.io/modules/InterweaveCloud/s3-cloudfront-static-website/aws/latest) with the source code available on [GitHub](https://github.com/InterweaveCloud/terraform-aws-s3-cloudfront-static-website) with [examples](https://github.com/InterweaveCloud/terraform-aws-s3-cloudfront-static-website/tree/main/examples) shown. We would like to deploy a static website with the files automatically synced and a custom domain name.

### Providers.tf
```hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.10.0"
    }
  }
}

# Default provider for resource creation
provider "aws" {
  region  = ""
  profile = ""
}

# Provider required in useast1 for CloudFront, SSL Certificate
provider "aws" {
  alias   = "useast1"
  region  = "us-east-1"
  profile = ""
}
```

In providers.tf you will have two providers:

- Default provider for the region where your main infrastructure will be deployed.
- 'useast1' provider for CloudFront and the SSL certificates. These sit in the us-east-1 region because they are global services.

### main.tf
```hcl
module "s3-cloudfront-static-website\_example" {
  source  = "InterweaveCloud/s3-cloudfront-static-website/aws"
  version = "0.0.1"

  resource\_uid   = "DevOpsNavy" 
  domain\_name    = "XXX" 
  hosted\_zone\_id = "XXX" 
  profile        = "XXX" 

  sync\_directories = \[{
    local\_source\_directory = "./website\_content"
    s3\_target\_directory    = ""
  }\] 

  providers = {
    aws.useast1 = aws.useast1
  }
}
```
<table><tbody><tr><td><strong>Variable</strong></td><td><strong>Description</strong></td></tr><tr><td>resource_uid</td><td>UID which will be prepended to resources created by this module</td></tr><tr><td>domain_name</td><td>The domain name for the website.</td></tr><tr><td>hosted_zone_id</td><td>The Hosted Zone ID. This is automatically generated and can be referenced by zone records.</td></tr><tr><td>profile</td><td>Credentials profile to use for AWS s3 sync command</td></tr><tr><td>sync_directories</td><td>Directories to sync with S3</td></tr></tbody></table>

Variables within sample code

Following the normal terraform init, terraform plan and terraform apply workflow will deploy the infrastructure and your site will be up and running! If you update your website content, you can rerun Terraform apply and a change will be detected and the content re-synced.

## What Next?

### Automated deployment through GitHub Actions

We are currently developing an associated GitHubActions pipeline which will automate the Terraform deployment and the build of the source code.

### Monitoring and Budgeting Alarms

We are looking into creating a monitoring dashboard and budgeting alarms.

### General quality of life updates

We are making general QoL updates such as making the domain name optional initially in case this is not readily available at the start.

## Follow Me

Follow me on [LinkedIn](https://www.linkedin.com/in/faizanrazainterweave/)