Problem Statement:

I want to create a blog to document my tech and personal creations. However, I don't want to spend a ton of time manually deploying. The first thing that should be done is setting up CI/CD pipeline so that everytime I push code to github it gets deployed to production.

Pre-reqs:

  1. Download Jekyll
  2. Create Github repository
  3. Buy domain with AWS Route53

Solution:

Step one: S3

The blog website will take advantage of Amazon S3’s ability to host static websites. The first action item is to go to amazon aws and create two buckets. Note: The naming of the buckets matter. I created examplebucket.com and www.examplebucket.com. After creating the buckets I needed to go to the properties of both of them and enable “Static website hosting”. Once static website hosting is available, I need to allow access to the main bucket (the one created without “www.”). I select that bucket, navigate to the permissions tab, select the edit button next to “Block public access (bucket settings)” option and deselect “Block all public access”. Next, I added a bucket policy to limit the public access. Under the “Bucket Policy” option, I selected “Edit” and pasted the json statement below, ensuring to replace YOUR_BUCKET_NAME with my actual bucket arn.

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

Step two: IAM User and S3 Access

The Github runner needs access to the S3 bucket.

  1. Define IAM user with S3 permissions.
    • Navigate to AWS Console > IAM > Users and select "Create User".
    • Name the user, click next, and then select "Attach Policy Directly"
    • In the search bar type "S3FullAccess", select S3FullAcess from the list, select next and then create user
    • Click the user you just created. Then under Summary select "create access key", choose "Third-party service", accept the waiver (too lazy for iam role), and finish creating the key.
    • ATTENTION: Download the .csv file as you will need these keys for the next steps.

Step three: Github Workflow and Github Runner

Did you download the .csv file from above? Good. This next part will be easy. Navigate on github to your respository. Once there go to Settings > Secrets and variables > Actions.

  1. Create two secrets: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
  2. Open the csv file from before and populate the secrets
  3. Create two variables: S3_BUCKET and AWS_REGION
  4. Fill in these variables with your values

How to tie it all together? In the root of the git repository, define a .github/workflows/deploy-workflow.yaml file and paste the code snippet below.

  
    on:
      push:
        branches:
          - main

    jobs:
      deploy:
        runs-on: ubuntu-latest

        steps:
          - name: Checkout code
            uses: actions/checkout@v2

          - name: Install dependencies
            run: sudo apt-get install ruby-full build-essential zlib1g-dev

          - name: Installing Jekyll and bundler
            run: sudo gem install jekyll bundler

          - name: Install dependencies
            run: sudo bundle install

          - name: Build Jekyll site
            run: jekyll build

          - name: "Configure AWS"
            run: |
              aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
              aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
              aws configure set default.region ${{ vars.AWS_REGION }}

          - name: Deploy to S3
            run: aws s3 sync _site/ s3://${{vars.S3_BUCKET}} --delete
    
    

Now with each push to main, a workflow will get triggered to build, and then copy from the runner to the s3 bucket that is serving the website.

Step four: SSL

Once the blog was deployed, I realized that SSL wasn’t enabled. This isn’t a big deal, but it looks pretty sketchy. It would be nice if S3 hosted websites had a “click to enable SSL” button, but they don’t. Nevertheless, we can enable SSL by leveraging CloudFront and Certificate Manager.

Certificate Manager

In the AWS Console, navigate to Certificate Manager and select “Request”. Under “Domain names” section I added examplebucket.com and *.examplebucket.com, selected DNS validation for “Validation method” and clicked “Request”. Once that finished, I clicked on the certificate ID and in the “Domains” section selected “Create records in Route53”. This is needed to verify the certificate. Now all that’s left to do is to wait 15-30minutes to complete the verification process.

CloudFront

Note: I didn’t realize it, but I needed the URL for the S3 hosted website. You get this by going to S3 > selecting my root bucket > properties > scroll all the way to the bottom. Save this somewhere.

  1. Navigate to CloudFront and select "Created disribution"
  2. Under "Origin domain" paste the URL for S3 hosted site
  3. Add examplebucket.com and www.examplebucket.com as items to the "Alternate domain name" list.
  4. Enable "Redirect HTTP to HTTPS"
  5. Select "Choose certificate" and find the certificate we previously created.

Now that the CloudFront distribution is created, I just need to copy the distribution domain name and associate it with the records in Route53.

  1. Navigate to CloudFront and select the disribution
  2. Copy the distribution domain name
  3. Go to Route53 and select the hostedzone
  4. Select A record for examplebucket.com > Edit Record
  5. Update: Route traffic to point to "Alias to CloudFront distribution" and select the CloudFront distribution below
  6. Do the same steps for www.examplebucket.com A record

Ta Da! Now I have a blog that redeploys with every commit to the main branch. It also doesn’t look as sketchy since SSL is enabled.