Deploying a Hugo site with Github Actions, S3, Cloudfront and AWS IAM
GitHub Actions are a nice way to get “push to deploy” behaviour for a static site. Here’s a quick run-through of how I have it working for Hugo sites being deployed to AWS.
This assumes you have created an S3 bucket and Cloudfront distribution for the site, and have pointed the domain name to the Cloudfront distribution.
It’s best to have an isolated IAM user for deploying with minimal permissions. Create a new policy in AWS IAM, and give it minimal permissions for deploying the site.
Go to the IAM console: https://console.aws.amazon.com/iam/home
Create an AWS IAM policy with JSON like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutBucketPolicy",
"s3:ListBucket",
"s3:DeleteObject",
"s3:PutObjectAcl",
"cloudfront:CreateInvalidation",
"s3:GetBucketPolicy"
],
"Resource": [
"arn:aws:cloudfront::{aws-account-id}:distribution/{cloudfront-distribution-id}",
"arn:aws:s3:::{s3-bucket-name}",
"arn:aws:s3:::{s3-bucket-name}/*"
]
}
]
}
You need to replace {aws-account-id}
, {s3-bucket-name}
and
{cloudfront-distribution-id}
in that example. It’s probably easiest to paste in
the JSON and then use the visual policy editor in AWS console to get the right
resource ARNs in place.
Then create a new programmatic-access user and give it the new policy directly.
Save the AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
for this new user.
You also need to enable static website hosting in the S3 bucket, and set the bucket policy to allow public access to the files:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::{s3-bucket-name}/*"
]
}
]
}
The next thing is the Github Action config file, which lives at
.github/workflows/build.yml
in your repo:
name: Build and Deploy
on: push
jobs:
build:
name: Build and Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install Hugo
run: |
HUGO_DOWNLOAD=hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz
wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_DOWNLOAD}
tar xvzf ${HUGO_DOWNLOAD} hugo
mv hugo $HOME/hugo
env:
HUGO_VERSION: 0.71.0
- name: Hugo Build
run: $HOME/hugo -v
- name: Deploy to S3
if: github.ref == 'refs/heads/master'
run: aws s3 sync public/ s3://{s3-bucket-name}/ --delete --acl=public-read && aws cloudfront create-invalidation --distribution-id={cloudfront-distribution-id} --paths='/*'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Again, you need to replace {s3-bucket-name}
and {cloudfront-distribution-id}
in that example. Set up AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
as
secrets in the Github Actions config using the values you got from AWS IAM for
the new role.
This Github Action builds the Hugo site, syncs the generated site files with S3, and creates a Cloudfront invalidation to refresh the site.