Home How to Deploy to Amazon S3 with GitHub Actions
Post
Cancel

How to Deploy to Amazon S3 with GitHub Actions

In my previous post, I discussed how I am hosting this site. As the title of this next article suggests, we will now be focusing on the steps I took to add basic CI/CD functionality to my website using GitHub Actions. The primary benefit of this being that each time I push a new change to the main branch of my GitHub repository, my website will be automatically updated with the latest content.

Prerequisites

In case you’re interested in following along, you will need:

  1. An existing GitHub account
  2. An existing GitHub repository for your website (this is where the GitHub Action will live)
  3. An existing website hosted in AWS (using S3 and CloudFront)

The Work

In order to add this automated deployment capability to our website we are really just concerned with two services and making sure they can communicate. In this case, those services are GitHub and AWS. In AWS, all entities are generally assigned some combination of an IAM user/role/policy (i.e. some predefined set of permissions) which determines what resources they can access. With this in mind, I’ve split the general steps I used to create the communication channel between these two services and outlined them below.

In AWS

  • Create new IAM entities in your AWS account to grant appropriate access to the GitHub Actions runner, which will be handling the actual file upload/sync work during the deployments
  1. IAM User - Create an IAM user, preferably with a descriptive name like “gitlab-access-runner” (for this implementation to work this user will need an AWS access and secret access key).
  2. IAM Policy - Create an IAM Policy similar to the one below that will grant access to your S3 bucket (for file sync) and cloudfront distribution (to create invalidations) and attach it to your newly created user:
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Resource": [
                    "arn:aws:s3:::<your-bucket-name>",
                    "arn:aws:s3:::<your-bucket-name>/*"
                ],
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": [
                    "s3:*"
                ]
            },
            {
                "Sid": "VisualEditor2",
                "Effect": "Allow",
                "Action": "cloudfront:*",
                "Resource": "arn:aws:cloudfront::<your-aws-account-number>:distribution/<your-distribution-id>"
            }
        ]
    }
    

Once things are configured in AWS you can head back over to GitHub and start creating your new Action!

In GitHub

  • Create a new GitHub Action in your GitHub repository
  • Add the required IAM values to your Action Secrets
    1. AWS_ACCESS_KEY_ID
    2. AWS_SECRET_ACCESS_KEY
    3. AWS_S3_BUCKET_NAME
    4. AWS_CLOUDFRONT_DISTRIBUTION_ID
  • Add the deployment instructions via yaml to your Action which will tell the Action exactly what to do with your code and when:
    name: "Build and Deploy"
    on:
    push:
    # Branch name(s) that you want to trigger this action
    branches:
        - main
    paths-ignore:
        - .gitignore
        - README.md
        - LICENSE

    # Allows you to run this workflow manually from the Actions tab
    workflow_dispatch:

    env:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
    AWS_DEFAULT_REGION: 'your-aws-region' # e.g. 'us-east-1'

    permissions:
    contents: read
    pages: write
    id-token: write

    # Allow one concurrent deployment
    concurrency:
    group: "pages"
    cancel-in-progress: true

    jobs:
    build:
    # build 'image'
    runs-on: ubuntu-latest

    # These are the actions taken by the runner, REMOVE OR ALTER THESE AS NECESSARY!
    # In my case, they include the steps for building my website, which uses Ruby + Jekyll
    steps:
    - uses: actions/checkout@v3
    - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
        ruby-version: "3.0" # Not needed with a .ruby-version file
        bundler-cache: true

    - name: "Build Site"
        run: bundle exec jekyll build
        env:
        JEKYLL_ENV: production

    - name: "Deploy to AWS S3"
        run: aws s3 sync ./_site/ s3://$ --acl public-read --delete --cache-control max-age=604800
        
    - name: "Create AWS Cloudfront Invalidation"
        run: aws cloudfront create-invalidation --distribution-id $ --paths "/*"

Once you’ve configured the above you should be able to test your CI/CD flow all the way through by either manually triggering your Action or making a simple change to your GitHub repo’s main branch.

Congrats! If you weren’t already an expert hopefully this has given you a glimpse into the power provided by tools like GitHub Actions and CI/CD as a whole.

Notes

This is just one possible implementation of many and although it worked well for my simple use case, it may need some adaptation to fit your needs.

References

  1. PagerTree Blog - Jekyll site to AWS S3 using GitHub Actions
This post is licensed under CC BY 4.0 by the author.