The Ops Community ⚙️

Cover image for Deploy a web app to S3 with CloudFront Invalidation via GitHub Actions
Jeiman Jeya
Jeiman Jeya

Posted on

Deploy a web app to S3 with CloudFront Invalidation via GitHub Actions

In this guide, we will show you how to set up a GitHub Actions workflow to deploy your web application to S3 and invalidate your cache on CloudFront for your end users. The guide includes pre-requisites, creating an IAM user, creating a custom policy for the IAM user, fetching your CloudFront distribution ID, saving secrets in GitHub Secrets, and a YAML pipeline workflow. Please note that this post assumes that your source code is hosted on GitHub and is running on a Node.js framework.

Pre-requisites

  1. An AWS account with admin privileges
  2. An S3 bucket
  3. A CloudFront distribution
  4. A GitHub account
  5. Basic understanding of GitHub Actions workflows

Steps

The steps involved are straightforward and shouldn't take too long to complete.

  1. Create an IAM user in AWS
  2. Attach a custom policy tailored to your pipeline needs
  3. Create or retrieve your CloudFront distribution ID
  4. Save the Access Key ID, Secret Access Key from IAM, and CloudFront distribution ID in GitHub Secrets
  5. Create the pipeline using a GitHub workflow

Creating the GitHub Actions IAM user

  1. Visit the IAM page on AWS.
  2. Create a new user. Give it a meaningful and distinct name.
  3. Don’t tick the box that says “Provide user access to the AWS Management Console - optional”. This user does not need access to the console to function.
  4. For permissions, choose the option, “Attach policies directly” and click “Create Policy”. Copy the JSON policy mentioned below in the next section.
  5. Review the new user and create.
  6. Next, visit the users page on IAM and click on the newly created user.
  7. Navigate to the Security credentials tab.
  8. Scroll down to Access keys and click Create access key.
  9. Take note of the Access key ID and Secret access key. You will need it later for saving it to GitHub Secrets.

AWS IAM user policy

To set up the pipeline, you will need an IAM user that would authenticate with your AWS account and perform the updates automatically. It is best to follow security principles and provide this user with the least privileged access to safeguard your AWS account from accidental or unwanted malicious activities.

As we basically need to allow the pipeline to be able to communicate with AWS S3 and CloudFront respectively, we need to supply the following permission scope and create a policy for the IAM user.

S3 permissions

  • List the buckets
  • Get the objects in the buckets
  • Create objects in the buckets
  • Delete objects in the buckets

CloudFront permissions

  • Create invalidation
  • Get invalidation
  • List the invalidation

Custom JSON policy for the IAM user

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::example.com"
            ]
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::example.com/*"
            ]
        },
        {
            "Action": [
                "cloudfront:CreateInvalidation",
                "cloudfront:GetInvalidation",
                "cloudfront:ListInvalidations"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:cloudfront::<aws_account_id>:distribution/*"
            ]
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

With the custom policy above, you can attach it to the IAM user you have created. Ensure the following is updated:

  • S3 bucket name - example.com
  • AWS account ID - for the CloudFront ARN

Fetching your CloudFront distribution ID

  1. Visit your CloudFront page.
  2. Click on Distributions.
  3. Select your Distribution that points to your S3 bucket.
  4. Take note of the Distribution ID in the first column. You will need this ID when you save it in your GitHub Secrets

Saving secrets in GitHub Secrets

You need to save the following secrets in your GitHub repository secrets:

  • IAM credentials that you created earlier - Access Key ID (AWS_ACCESS_KEY_ID) and Secret Access Key (AWS_SECRET_ACCESS_KEY)
  • CloudFront Distribution ID that you retrieved earlier (AWS_DISTRIBUTION_PRODUCTION)

GitHub Actions workflow

A workflow is a configurable, automated process that can run one or more jobs. Workflows are defined by a YAML file that is checked into your repository. They run when triggered by an event in your repository, or they can be triggered manually or on a defined schedule. Below is a template of a S3 Sync workflow.

YAML pipeline workflow

name: S3 Sync - Production

on:
  push:
    branches: 
      - 'master'

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-node@v2.5.1
      with:
        node-version: '15'

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-southeast-1

    - name: Install packages
      run: npm install

    - name: Run build
      run: npm run build

    - name: Generate
      run: npm run generate

    - name: Upload artifact
      uses: actions/upload-artifact@master
      with:
        name: web-app-dist
        path: './dist'

  deploy_to_production:
    name: Deploy to S3 Production
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: production
      url: https://example.com
    steps:
    - name: Download landing page artifact
      uses: actions/download-artifact@v2
      with:
        name: web-app-dist
        path: dist

    - name: Display structure of downloaded files
      run: ls -R
      working-directory: dist

    # S3 sync
    - name: S3 Sync
      uses: jakejarvis/s3-sync-action@v0.5.1
      with:
        args: --acl public-read --follow-symlinks --delete
      env:
        AWS_S3_BUCKET: 'example.com'
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_REGION: 'ap-southeast-1'   # optional: defaults to us-east-1
        SOURCE_DIR: 'dist'      # optional: defaults to entire repository

    # Invalidate Cloudfront
    - name: Cloudfront Invalidation
      uses: chetan/invalidate-cloudfront-action@master
      env:
        DISTRIBUTION: ${{ secrets.AWS_DISTRIBUTION_PRODUCTION }}
        PATHS: '/*'
        AWS_REGION: 'ap-southeast-1'
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Enter fullscreen mode Exit fullscreen mode

Using this workflow, you can easily deploy your applications to S3 by syncing the files and invalidating your cache on your CloudFront distribution. This ensures that your end users receive the latest content from your release.

Workflow breakdown

The workflow is divided into two sections:

  • CI section, which builds your application, generates the static files, and compresses them to be uploaded as artifacts.
  • CD section, which downloads the compressed artifact, configures the AWS credentials, syncs the static files to S3, and invalidates your CloudFront distribution.

The reason for this approach is to enable the reuse of our artifacts, if necessary, for other GitHub workflows. It also provides a clear separation between continuous integration builds and continuous deployments.

Conclusion

Setting up a GitHub Actions workflow is fairly simple once you get the hang of it. This workflow allows you to deploy your S3 applications with confidence to various environments.

Latest comments (0)