Skip to main content

Automating Astro Deploys to AWS from GitHub Actions

Some Background Of How This Came To Be

image

It had been awhile since I refreshed my personal site (screenshot above - circa 2019). Seeing that I’ve been itching to write more code outside of my day-job, I figured I could put to the test how quickly I could build + deploy a static website from start to finish. I had previously used the same system / set of processes to build & deploy Cabeça de Queijo and Toucan Trips.

How much time does this take to setup?

Honestly, getting Name.com DNS + AWS setup properly to host the site too ~60 minutes - max. Since I had worked out the kinks in the process w/ Cabeça de Queijo this was relatively painless with only one hiccup (setting up a 404 page in CloudFront and in the codebase).

How long does a build/deploy step take?

image Immediately after code is merged into the main branch, the GitHub Action to deploy the site is kicked off.

In total, it takes whopping 41 seconds - of which GitHub Actions adds 10s and installing npm modules is 18s. You can see that by having specific steps in your GitHub Action, you can really nail down what is taking up time.

image

Limitations

Certainly, as with most technology, there are limitations to what this deployment system can / cannot do. This guide is geared around launching an Astro website. And even tho Astro is relatively new, I found their documentation to be fantastic - as well as the various examples that folks in the ecosystem have produced.

Brief Guide on How to Setup AWS CloudFront with Name.com as the Domain Registry.

Steps -

  • Purchase Domain(s) on Name.com
  • Sign into the AWS Console
  • Navigate to AWS Certificate Manager (ACM)
  • Setup a certificate for both *.domainName.com && www.domainName.com and with the standard DNS Validation and RSA-2048 Key Algorithm.
  • Navigate back to Name.com and go to the DNS Setting for the domainName.com
  • In order to validate ownership of the domain, copy & paste the DNS Verification Records from the Newly Created Certificate from AWS Certificate Manager into the DNS Management on Name.com (Note - this could take a few mins to validate)
  • Now that the domain is validated, we need to setup the remaining AWS Resources (in this case an S3 Bucket and CloudFront).
  • Setup the S3 bucket like any other bucket - with Static Website Hosting disabled
  • Setup a CloudFront Distribution in the same region as the S3 Bucket (i.e. us-east-1) in order to make setup easier
  • The CloudFront Distribution should use All Edge Locations, HTTP/2, HTTP/1.1, HTTP/1.0, be connected to the SSL Certificate that was created earlier (pickable in a dropdown option), TLSv1.2_2021 security option, Redirect HTTP to HTTPS, and other setting for the index page and error pages.
  • The S3 Bucket Policy needs to be updated to allow the CloudFront Distribution to access it. This can be done by going into the CloudFront Distribution that was just created and then select the Origins tab and select the only entry + Edit. From here, you will setup Origin access control settings (recommended) and then Create control setting and then Save to create a linkage between CloudFront and the S3 Bucket.
  • Now, we need to ensure there is a CI/CD pipeline from GitHub.com to the AWS resource(s) - in this case, we built a static website built with Astro and a CI/CD workflow on GitHub.com as the source code repository location.
  • The code below is the GitHub Action required to make the code build & deploy when something is merged or pushed to the main branch. Step 14 is where the various Secrets -
name: Deploy Website to Amazon S3

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - 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: us-east-1
      - name: Install modules
        run: npm ci
      - name: Build application
        run: npm run build
      - name: Deploy to S3
        run: aws s3 sync --delete ./dist/ s3://${{ secrets.BUCKET_ID }}
      - name: Create CloudFront invalidation
        run: aws cloudfront create-invalidation --distribution-id ${{ secrets.DISTRIBUTION_ID }} --paths "/*"
  • As you can see from the .github/workflows/deploy.yml file above, you need a few secrets stored in GitHub.com on the repository. You can access this Secret Store under the Settings tab of your repo. (NOTE - In the deploy.yml file above, the BUCKET_ID is just the plain-text name of your bucket - project-name-frontend)
  • In the AWS Console, navigate to the IAM Policies Page - and with the policy below -
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:ListBucket",
                "s3:DeleteObject",
                "cloudfront:CreateInvalidation"
            ],
            "Resource": [
                "arn:aws:cloudfront::177283549651:distribution/[GET_THE_CLOUDFRONT_DISTRIBUTION_FROM_THE_MAIN_PAGE]",
                "arn:aws:s3:::[S3_BUCKET_NAME]*",
                "arn:aws:s3:::[S#_BUCKET_NAME]"
            ]
        }
    ]
}
  • The policy, when added to an IAM Entity (i.e. User) will allow for various operations that are required for CI/CD.
  • Create a new IAM User with the newly created policy and navigate to the Security Credentials tab to create a new Access Key.
  • Copy and paste those values from the AWS Console into the Github Secret Store for you repository.
  • Navigate to both the S3 Bucket and the CloudFront Distribution to copy & paste the relevant secrets (i.e. BUCKET_ID & DISTRIBUTION_ID) into GitHub.com Secret Store.
  • We’re nearly there, now we need to create a Route 53 Hosted Zone (AWS’ name server tool) in order to handle requests to both *.domainName.com & www.domainName.com.
  • Create a Hosted Zone for your domainName.com and create both A and AAAA records for both *.domainName.com and www.domainName.com (in total, 4 new records). You will do Simple Routing and Alias to CloudFront Distribution and select your new CloudFront Distribution in the typeahead/dropdown.
  • Now that there is a “new” name server (Route 53), we need to change the Name Server config on Name.com
  • Copy and paste the AWS Route 53 Name Servers from the Hosted Zone Details pane into Name.com.
  • Save the new AWS Name Server Records & delete the old [ns1-ns4].name.com name server records
    • NOTE - If you have a custom email / simple email forwarding already setup on Name.com (via a MX DNS record), it will break it since Name.com is now longer the name server.
  • As mentioned above, if you want to setup email forwarding for any email that goes to *@domainName.com to your personal email@provider.com, utilize a tool like improvMX.com. It takes ~5 minutes to setup and you can even send emails from *@gmail.com but make them look like their from *@domainName.com.

If there are issues with the above, ensure that your DNS has propagated with a tool like this.

Note - If you have a multipage website, be sure to create a CloudFront Functionhttps://us-east-1.console.aws.amazon.com/cloudfront/v3/home?region=us-east-1#/functions) in order to handle redirects properly.