CI/CD GitHub AWS S3 Site Custom Domain

by John Pennock 9/13/2023
GitHub logo + AWS CodePipeline Logo

CI/CD for a Static Site in GitHub to AWS S3 and Route 53

A CI/CD pipeline that will automatically build and deploy a Nuxt static site from a push to a GitHub repository, build the site, and deploy to AWS infrastructure for a custom domain, requires the following steps.

  1. Custom Domain
  2. AWS S3 bucket configured for Static Site Hosting
  3. AWS CodePipeline - Git Sync, AWS CodeBuild, Deploy to S3 Bucket
  4. AWS Route 53 hosted zone
    • Non-certificate (http warning)
    • CloudFront with certificate (https)
  5. AWS CloudFront Distribution
  6. AWS Certificate for https

Resources

The following videos illustrate many of the steps used here:

Custom Domain

Create, transfer, or attach nameservers to your custom domain in AWS Route 53. Yes, do this first.

AWS S3 Bucket for Static Site

S3 Bucket Name

You must name the S3 Bucket exactly the same as your custom Domain name, i.e. for 'pennockprojects.com' custom domain, create a bucket named pennockprojects.com. Otherwise, your endpoint will not be visible as an alias for the Route 53 record

S3 Bucket Permissions

Block all public access

S3 Bucket Permissions Block all public access should be set to Off

Bucket Policy

Add a bucket policy for object access, replace {{BucketName}} with your bucket name.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::{{BucketName}}/*"
        }
    ]
}

S3 Bucket Properties

PropertyState
Static website hostingEnable
Hosting typeHost a static website
Index documentindex.html
Error document - optional404.html (or whatever you have)

AWS CodePipeline

The AWS CodePipeline will perform three tasks.

  1. Attach and watch a GitHub repo branch for changes
  2. Initiate AWS Codebuild and generate your Nuxt 3 Static Site
  3. Copy the built Nuxt Static Site into your S3 site bucket

Creating a pipeline

  • Create a unique name for the pipeline
  • Choose Queued
  • Leave the pipeline role

Next

Pipeline Git sync

Choose a stage source

FieldState
Source Provider'GitHub (version 2)'
ConnectionUse existing or choose 'Connect to GitHub'
Repository name(pick your repo)
Default branch(pick your branch)
Output artifact formatCodePipeline default
Trigger - Trigger typeNo filter (launches on push)

Next

Pipeline CodeBuild

Build - Optional

Build provider → AWS codeBuild

FieldState
Build Provider'AWS CodeBuild'
Region(pick your region)
Input artifactsSourceArtifact (from the GitHub sync above)
Project Name(pick your project name or choose 'Create Project')
Environment variables(leave blank or add)
Build typeSingle build

Create Build Projects

FieldState
Project name(pick a project name)
Source 1 - PrimarySource provider - AWS CodePipeline
Environment Provisioning modelOn-demand
Environment imageManaged image
Environment ComputeEC2
Operating systemAmazon Linux
Runtime(s)Standard
Image'aws/codebuild/amazonlinux2-x86_64-standard:5.0'
Image VersionAlways use the latest image for this runtime version
Buildspec Build specificationsUse a buildspec file
Buildspec name(leave blank or buildspec.yml)

Pipeline buildspec.yml

Add the following buildspec.yml file to your repo and this will be in the instruction to build the Nuxt static site as well as inidcating where the built files will exist after build.

version: 0.2

phases:
  install:
    commands:
      - npm install
  build:
    commands:
      - npm run generate
artifacts:
  files:
    - '**/*'
  base-directory: 'dist'

Pipeline deploy

The third stage deploys the built files to the S3 website bucket

FieldState
Deploy provider'Amazon S3'
Region(pick your Region, same as build)
Input artifacts'BuildArtifact'
Bucket(pick your bucket)
Extract file before deployenabled

Verify

Your code pipeline should run, and when all three steps are done you can validate at:

S3 → {static site bucket} → properties → Static website hosting | Bucket website endpoint

AWS Route53 hosted zone

To use your S3 static site bucket with a custom domain name, you need an AWS Route53 hosted zone. If you acquired your custom domain through Route53, the hosted zone will be automatically setup. If you transferred your custom domain names into Route53, you may need to create one.

Creating Public hosted zone

Creating a hosted zone is simple, double-check that it is 'Public hosted zone'.

Default hosted zone

A new hosted zone in Route53, should have two records in it.

RecordTypeValue
{custom domain name}NS{set of AWS name servers}
{custom domain name}SOA{single AWS name server}

You can create a hosted zone without certification or with certification.

Option A - No-cert

Flow Chart with Route53 and S3 static bucket

Chart showing how users interact with Route53 and S3 for your site

No-cert ANAME record

In your hosted zone choose 'Create record' for your root

FieldState
Record name(leave blank for root)
Record typeA - Routes traffic to AWS resource
Route traffic to endpointAlias to S3 website endpoint
Route traffic to region(choose your region)
Enter S3 endpoint(pick your endpoint, should be in dropdown list) (only if your S3 bucket name matches domain name)

Verify

Your hosted zone should now have at least three records in the record set.

  1. NS record
  2. SOA record
  3. ANAME record - Domain to S3

Option B - Cert

In order to create a certificate for your site, you need a CloudFront distribution and a certificate issued for that distribution. You will also create a CNAME record that will point your domain name to your CloudFront Distribution.

Flow Chart with Route53 CloudFront and S3 static bucket

Chart showing how users interact with Route53, CloudFront, Certificate Manager and S3 for your site

Request certificate

To request a new certificate 🎫 use the AWS certificate manager.

  • AWS Certificate Manager → Certificates → Request

Certificate properties

FieldState
Certificate type'Request a public certificate'
Domain names : Fully qualified domain names{custom domain name}
Optional - Domain names : Add another name to this certificatewww.{custom domain name}
Validation methodDNS validation

CNAME cert records

In order to DNS validate your certificate you need to add one or two CNAME records to your hosted zone. Conveniently from the certificate request details you can automatically create the records. Choose the 'create CNAME records' button.

🛑 Important - DNS validation requires these CNAME records to be created.

Wait until it the certificate is issued.

AWS CloudFront Distribution

In AWS CloudFront choose 'Create CloudFront Distribution' and change the following

FieldState
Origin : Origin domain{insert your S3 Bucket website endpoint (no 'http://')}
Default cache behavior : Viewer protocol policyRedirect HTTP to HTTPS
Settings: Alternate domain name (CNAME) - optional{custom domain name}
Optional - Settings: Alternate domain name (CNAME) - optionalwww.{custom domain name}
Settings: Custom SSL certificate - optional{pick your certificate from dropdown}
Settings: Price classUse only North America and Europe

Verify

Your hosted zone should now have at least four or maximum five records in the hosted zone record set.

  1. NS record
  2. SOA record
  3. CNAME record(s) for certificate - 1 or 2 depending on if you included 'www' or not
  4. ANAME record - Domain to CloudFront

ANAME 'www' redirect

Optionally, if you've created your certificate and CloudFront distribution with 'www', you need to create an ANAME 'www' redirect. In your hosted zone choose 'Create record' for your root

FieldState
Record name'www'
Record typeA - Routes traffic to AWS resource
Route traffic to endpointAlias to another record in this hosted zone
Route traffic to Choose record(pick your ANAME domain without the 'www')

Verify

After your certificate is issued and the CloudFront distribution is deployed, you can validate that your custom domain opens your static site hosted from S3.