Adding a https to an S3 static site via CloudFront

Ok, so we’ve set up a static site hosted from an S3 bucket with a custom domain using Route 53. But sadly, it’s:

Not Secure

Request a Certificate in Certificate Manager

  • Go to Services -> Certificate Manager
  • Click [Request a Certificate]
  • In the window that opens from “Request or Import a Certificate with ACM”, enter your domain name ( and click [Next]
  • Select DNS validation and click [Next]
  • Click [Review]
  • Click [Confirm and Request] if the details look correct.
  • Expand the domain in validation:
  • Click [Create record in Route 53] and confirm by clicking [Create] again.
  • You’ll be waiting from several minutes to half an hour for the validation to happen, during which time status will display as “Pending validation”
  • Click [Continue] to finish the request process and go back to the Certificate Manager main screen.
  • Click the (refresh icon) button to update status, and when status turns to “Issued” you are ready to use it in CloudFront.
Pending validation
Ready for use

Setting up a CloudFront distribution

  • In the AWS Console, go to Services → Cloudfront
  • Click [Create Distribution]
  • Click [Get Started] under Web

Create Distribution

  • Under “Origin Domain Name” select the selection under “Amazon S3 buckets” that corresponds to your static web site bucket. (e.g.,
  • Optional: Restrict Bucket Access [Yes] so that you can control access through the CloudFront distribution alone.
    • Set “Origin Access Identity” to “Create a new identity”
    • Set “Grant Read Permissions on Bucket” to “Yes, Update Bucket Policy”
  • Under Viewer Protocol Policy I select “Redirect HTTP to HTTPS” just to keep things uniform.

Set up SSL

  • Under Alternate Domain Names, enter your domain name (e.g.,
  • Select “Custom SSL Certificate”
  • Click “Request or Import a Certificate with ACM”
  • If you go back to CloudFront you should be able to select “Custom SSL Certificate” now and the certificate corresponding to your domain name should show up in suggestions:
  • Scroll down and leave defaults until you get to “Default Root Object”. You’ll want to set this to the name of the document to bring up (e.g., index.html) if the user browses to / on the domain.
  • Optional: I set Logging to On and selected my logging bucket that I used for the static site as the bucket, adding a log prefix for it.
  • To finish, click [Create Distribution]
  • You may be waiting quite a while for changes to propagate to the edge locations, but at some point before the “In Progress” changes to “Deployed” you will be able to pull up via the domain listed under the “Domain Name” column in your list of CloudFront Distributions.

Pointing the domain name at your distribution

  • Go back to Route 53 and go into the hosted zone for your domain name
  • Check the checkbox next to your A record and then go up to Actions -> Edit
  • Change “Value/Route traffic to” from “Alias to S3 endpoint” to “Alias to CloudFront distribution” in the “Choose Distribution” input box.
  • Enter the domain name (“”) as your domain name. (The new interface wasn’t suggesting distributions like the last version of the interface did… it may change next week, of course.)

Locking down S3

If you selected “Restrict bucket access” and had CloudFront update your S3 policy, your public access setting on the bucket is still unaffected. You’ll want to remove that:

  • Go back to Services -> Amazon S3
  • Go to your bucket
  • Click Permissions
  • Click Block public access
  • Check “Block all public access” and click [Save]

Some other details

If you want to have JavaScript and forms function properly You’ll want to set up CORS configuration by going to your S3 bucket, then selecting Permissions tab and clicking CORS configuration:



Some mistakes I made:

  • A certificate for * does not cover You have to add both if you want wildcard and itself covered.

Next up… preventing someone from running up a $1,000 AWS bill by hammering your site (i.e., monitoring your site’s access… with better granularity than AWS Budgets…

Hosting a static site on S3 with a purchased domain

S3 Static Site Setup

Creating buckets

  • In your AWS Console, go to Services -> S3
  • Optional:
    • Click [ + Create Bucket ]
    • Type in a bucket name for static site logging (i.e.,
    • Accept next all the way to Create Bucket
  • Click [ + Create Bucket ]
  • Type in your domain name (for example, “”)
  • If you created a logs bucket:
    • Check “Log requests for access to your bucket” under “Server access logging”
    • Enter the logging bucket name under the “Target bucket” field.
  • Hit [Next] for Configure Options
  • Under Set Permissions, uncheck “Block all public access” and check the box that says “I acknowledge that the current settings may result in this bucket and the objects within becoming public”
  • Click Create Bucket

Creating a static site

  • Drag and drop your static HTML files and assets within the site root in your project and drag to S3. Before sure you have the html file you want to use as an index and as an error page.
  • Click [Next] on the Upload modal.
  • Under “Manage public permissions” change “Do not grant public read access to this object(s)” to “Grant public read access to this object(s)” and click [Next]
  • Click [Upload] from the “Set Properties” step (skip Storage Class configuration, etc. screen).
  • Under the “Properties” tab for your bucket, click on the “Static website hosting” tile
  • Select “Use this bucket to host a website”, enter the name of your index and error documents, and click [Save].
  • You should be able to click the link under endpoint and see your index page.

Create a hosted zone for your domain in Route 53

  • In Route 53, select Hosted zone link on the left of the console
  • Click [Create hosted zone]
  • Enter your Domain name
  • Select “Public hosted zone”
  • Click “Create hosted zone”
  • If you have a domain purchased elsewhere than AWS, copy the name servers under the “Hosted zone details” and set on your domain. (e.g., on Namecheap, it’s under Domain->Nameservers->Custom DNS… BE SURE TO HIT THE GREEN CHECKMARK AFTER EDITS!!)

Create a Record in the Hosted Zone

  • Select “Create record”
  • Select “Simple record”
  • Under “Define simple record”
    • leave the record name blank
    • Value/Route traffic to
      • Alias to S3 website endpoints
      • Select your region
      • In the field that says “Choose S3 Bucket”, you should see your bucket as an option:
  • To finish, click [Define simple record] and then [Create records]

Running Linux on a Mid-2009 MacBook Pro

Apple and their aggressive EOL/EOS policies

I have had an old mid-2009 MacBook Pro (2.26Mhz, Core 2 Duo P7550) that got left behind at macOS 10.11 (El Capitan). I’ve already gone through dealing with end-of-support with a 2011 MBP (no AirPlay server, no longer getting macOS updates, etc…), but the 2009 is old enough that Homebrew itself has dropped support officially. While that doesn’t prevent everything from working, once something won’t install, you either need to fix it yourself or give up.

I was trying to get RDP up and running when I hit that issue, so I figured that it was time to see if I could install an operating system that was still supported on the 2009 MBP.

Ubuntu Linux attempt

I first went down the rabbit hole of Ubuntu, but the first set of instructions I found did not give me a warm fuzzy about being able to install Ubuntu and still have a working machine. I used Rufus to write an Ubuntu ISO to a USB drive and was able to live boot into Ubuntu. Of course, Linux and wifi isn’t always plug and play and the live boot did not recognize the MBP’s wifi card, so it would have been wired or nothing until I installed the OS.

Hello Manjaro

Time for a different strategy. I tried a different strategy and searched for Linux distros for a 2009 MacBook Pro. I decided to go with Manjaro XFCE and was able to write the ISO to my USB drive and have wifi working on live boot, so I swapped MacBook hard drives back to the original 5400 RPM 160GB hard drive that came with it.

Manjaro with XFCE is far snappier than even my 2011 or 2019 MBPs. This is mainly because no bloat and very little running so far, but I’ve also installed Ruby 2.8.0-dev from Manjaro in rbenv on the 2009 (P7550 Core 2 Duo) vs. from the 2019 (i7-9750H 6 core) and the install/build time was 9m31s for the 2009 vs. 11m19s for the 2019 (yes, the 11 year old Mac was faster). This is probably a Clang vs. gcc and readline + openssl installation/build difference between the two environments, but I still found it interesting that the 2009 could win the race even with a head start.

As for system load, the MBP 2009 not actively running anything on macOS 10.11 would register a consistent 2.0+ load and would take several minutes to be usable for typing in iTerm. By contrast, running XFCE, it required several tabs open in Firefox + Terminal + gvim for the load to break 1.0.

Steps to get up and running:

  • Download Manjaro XFCE iso
  • Write image to USB drive using Rufus (dd or whatever if you are adequately skilled at that… Rufus was just painfully easy to kick off from a Windows 10 box)
  • Hold down option while booting to get boot menu.
  • Select your USB drive
  • Boot and verify that Linux is sufficiently usable for your MacBook
  • Install to a hard drive (I recommend using a different hard drive that is booting macOS, but then you’ll need tools for the mounting screws at a minimum.
  • Boot and enjoy

Getting set up

Having used yum, apt, and brew, pamac was a bit of a change. For example, I had to use pamac build ruby-build and pamac build rbenv vs. an install command.

I have starship installed as well, which was relatively straightforward, but I still need to get the fonts right for unicode.

Overall experience

So far I’ve been pleasantly surprised with how useful my 2009 MacBook Pro is again. I’ve managed to type up this blog post without any lag from the processor, and the memory usage is sane as well (despite Firefox). I also miss having the pre-butterfly keyboard keys to type on for writing, so it was nice to experience those again.

Planet Fitness Cancellation Template

Update: I put this in the mail on Tuesday and got cancellation confirmation less than 48 hours later.

Also, your mileage may vary, some locations are a little more of a stickler about certified mail, but you should get a confirmation from the location within a few days if they’ve accepted.

I needed to cancel Planet Fitness in the middle of the COVID-19 pandemic and I really didn’t want to be showing up at a gym in person right now (time and unnecessary risk) just to cancel.

I called up my local Planet Fitness and was told I could either come in in person *or* send a [yes, snail mail] letter to the Planet Fitness I signed up at.

So, I’ve thrown together a cheap template to help you include everything you need to cancel (it’s just name, date of birth, and signature, according to the person on the phone.)

You can download the doc I put together at: Planet Fitness Cancellation Word Doc template

Who Else is Participating in Advent of Code 2019

Who else is doing Advent of Code 2019?

I’m using it to try to solve problems in Ruby without actually looking up syntax (looking at you, inject) and getting some practice using MiniTestinstead of RSpec which I use 99% of the time.

Day 2 was kind of fun to be able to implement a tiny virtual machine in Ruby.

Day 3 had me stumped until I just when for a naive solution in which I just created a list of all the coordinates visited instead of trying to figure out an elegant way to calculate the answer… an approach which actually paid off in the second half.

Day 4 has been the problem that I spent the most time fighting with (specifically the second part) after thinking that I had it solved. It was one of those scenarios in which I pretty much starting chasing my own tail after the solution I thought should work didn’t. I ended up with 4 state variables in addition the input variable itself.  

Prius Gas Mileage Using a Thule on a Road Trip

I’ve been getting an actual 44 MPG over the first summer of owning my Prius.

Running the heat and defrosting windows dropped my gas mileage down below 40. 


Accompanying on the Roland HP-603A

I’ve had 30 years of experience fighting with keyboard instruments in both church services and for musicals.

Electronic keyboards are very much a case of getting what you pay for, up to a certain point. However, beyond a certain price point (~$2k), each brand has a few unique characteristics and then a few features that are made more or less usable as you upgrade for other features.

In playing a couple weekends with the Roland HP-603A, I discovered a few of these.

Dual sound:

Dual balance setting.

The dual sound on this digital piano has an option that I haven’t found on other keyboards (Kurzweil, Yamaha)… you can specify the balance of the sound between the two voices.

However, just like the Kurzweil and Yamaha, the order of selection when selecting two voices determines the “primary” voice. You can separately select to “edit” voice 1 and voice 2 and scroll through options, but that’s not as useful while playing.

Organ sounds:

The Roland has a few organ settings, 2 or 3 which are “pipe organ” and a few which are “jazz/gospel/electronic organ”, but one interesting one that I found was the “Nason Flute 8′”… which is an oddly specific description, but is a good instrument for quiet underscoring but with a breathy “flute” attack.

The organ notes sustain in response to the damper pedal on the keyboard, which is totally wrong, but very convenient. (The Kurzweil does the same, but the Yamaha that I have does not.) Trills on the organ settings articulate distinctly, but are not as detached as I’ve experienced on a Yamaha.

As far as I could tell, the organ settings *do* respond to attack, which is a little bit weird and/or surprising, but you just have to make sure not to suddenly emphasize random notes.


Transposition has a transpose button and then you + or – the transposition. About half the keyboards I’ve used have made it either non-obvious *how* to transpose, or made it slightly difficult to pull it off relatively quickly.

Strings and Bass:

The “Symphonic Strings” setting had a nice sound without being too slow on the attack or too harsh. I almost always use strings as an ambient addition to a piano-like instrument selection, so I didn’t try actually leading with the instrument.

This is the first keyboard that I’ve seen in a while, however, that *didn’t* have a string bass + ride option. The walking bass on the left hand just becomes more obvious when a ride cymbal is tapping along.


While I’m not sure that I’d purchase one for my own home, the Roland works better in an accompaniment situation than the mid 4-figure Yamahas in my experience.

Flutter Initial Installation Experience

Installing Flutter on MacOS and using IntelliJ
From there, just iterate on brew doctor until it stops warning you about things not being installed.
In IntelliJ, I also got the following warning about the Dart SDK
Dart SDF not configured
Of course, when you click on the “Download Dart SDK” link, it points you to install Flutter, which I have done.
Recommendations for Dart Installation
After checking the flutter/bin directory, I confirmed that there was nothing that looked like “dart” in there.
After a bit of searing on “Dart SDK is not configured”, I found that Android Studio users were running into the same thing:
Dart SDK is not configured
In “Dart SDK path” click in “…” and navigate to flutter bin directory. Under that directory you’ll find “cache/dart-sdk”. This is the dart sdk path you should use.

Sure enough, in “…/flutter/bin/cache/dart-sdk/bin” were all the dart* binaries and alias scripts.

After that (I was already set up in Xcode for iPhone development), it was trivial to build and deploy a basic test app to the iPhone X Simulator.

Flutter Demo Counter App

Flutter Demo Counter App in iPhone X Simulator

Making Your zsh Return You to a Non-Production Setting

I have my prompt in zsh setup to display what Kubernetes context I am pointing to, so that I will notice if I’m somehow pointed at production environments, but I’m still wary of being asked to check things in production and then forgetting to point back to a “safe” environment before doing something else.

So I set up a preexec and precmd hook to store state for the session that I’m in. In the preexec, I save off whatever command was run.

In the precmd, I check to see that command matches “switching over to production”, mark the time, and clear the command out (otherwise, pressing <return> on the shell will keep the same command).

If I have marked the time I switched to production (PROD_TIME is not an empty string), then I check the current time and if it’s past a threshold (15 seconds in the current case), then I run the command to switch back.

The last few lines clear the prexec and precmd function hooks and set them to the “safety” functions.

function safety_preexec() {
# Store the command that we're running.
function safety_precmd() {
if [[ "$CMD_NAME" =~ 'kubectl config use-context whatever-your-prod-is' ]]; then
PROD_TIME=$(date +%s)
if ! [[ -z "$PROD_TIME" ]]; then
CURRENT_TIME=$(date +%s)
kubectl config use-context whatever-your-dev-is
[[ -z $preexec_functions ]] && preexec_functions=()
preexec_functions=($preexec_functions safety_preexec)
[[ -z $precmd_functions ]] && precmd_functions=()
precmd_functions=($precmd_functions safety_precmd)

view raw
hosted with ❤ by GitHub

Tracking HipChat Activity with AppleScript

The Problem

Chat tools are great for being able to work remotely, at least until you get bombarded by one chat after another. I’ve often wondered if I could come up with a way to track who my chats are with and how much time was spent chatting with each person. My initial attempts involved trying to connect the HipChat API, but I would get rate-limited before I even got through the full set of contacts, much less the rooms themselves. And as far as I could tell, I had to cycle through all public and/or subscribed rooms and not just the rooms that I subscribed to.

(You might be familiar with RescueTime doing similar for webpages, but it doesn’t appear to do that for HipChat or Microsoft Teams as far as I’ve been able to tell.)

A Simpler Algorithm

What if I could just log periodically when I’m chatting with a specific person or on a specific topic? I started playing with the Accessibility Inspector to try and figure out if I could get a specific path to the name display so that I could track who I was chatting with/what room I was in.

Name in HipChat

Name display in HipChat

I could an incredibly long tree down to the name display, so I went directly into Script Editor with some AppleScript to dump the UI elements of HipChat (commented out below), but found that the display was too generic… so I switched to grabbing the entire contents:

tell application "System Events"
entire contents of process "HipChat" of application "System Events"
UI elements of process "HipChat" of application "System Events"
end tell

For HipChat, the above produces a long list of element hierarchies, but static text is mentioned in the hierarchy (I used somebody else’s name because your own name appears in more windows in the view, but there may be multiple hierarchies that display the name you’re looking for):

static text “Thomas Powell” of group 1 of group 13 of group 4 of UI element 1 of scroll area 1 of group 1 of group 1 of group 1 of group 1 of window “HipChat” of application process “HipChat” of application “System Events”,

So I would remove the last bit of the hierarchies, and ask for the “name of” or “value of” the remainder of the hierarchy.

name of UI element of group 2 of group 1 of group 3 of UI element 1 of scroll area 1 of group 1 of group 1 of group 1 of group 1 of window HipChat of application process HipChat of application System Events

Ultimately, “name of” was the key to getting the display value I was looking for, but the chat rooms had a slightly different hierarchy, and both required trial and error to find the correct hierarchy. Ultimately, the value I was looking for was in a list within the “name of” the UI element at the bottom of the hierarchy, so I continued the inspection in Script Editor until I got to the correct value.


(Disclaimer: I am barely able to write AppleScript that parses, much less AppleScript that looks good.)

I ended up creating an application the allows me to select a log file for output. Then I created a giant loop that checks if HipChat (or Microsoft Teams or RubyMine) or the front applications and then logs a usage with name or project in a log file + timestamp separated by a semicolon. (I used Ruby to generate the statistics based on this… sorry.)

I don’t wait for any period if none of the applications I’m looking for are currently in front. If one of them is, I delay 15 seconds.

set myFile to open for access (choose file name) with write permission
tell application "System Events"
set frontApp to name of first application process whose frontmost is true
if (frontApp = "HipChat") then
— set things to entire contents of group 1 of group 1 of group 1 of window 1
— set things to UI elements of group 1 of group 1 of group 1 of window 1
— I think this is the PM
set myList to name of UI element of group 2 of group 1 of group 3 of UI element 1 of scroll area 1 of group 1 of group 1 of group 1 of group 1 of window "HipChat" of application process "HipChat" of application "System Events"
write (item 1 of myList) & ";" & ((current date)) & linefeed to myFile
delay 15
on error
— Chat room?
set myList to name of UI element of group 1 of group 3 of UI element 1 of scroll area 1 of group 1 of group 1 of group 1 of group 1 of window "HipChat" of application process "HipChat" of application "System Events"
write (item 2 of myList) & ";" & ((current date)) & linefeed to myFile
delay 15
end try
end if
if (frontApp = "Teams") then
set myTitle to title of window 2 of process "Microsoft Teams" of application "System Events"
write myTitle & ";" & (current date) & linefeed to myFile
delay 15
end if
if (frontApp = "RubyMine") then
— need to also capture the directory which is in the first element
set staticTexts to value of static text of window 1 of process "RubyMine" of application "System Events"
set gitMe to {}
repeat with theText in staticTexts
if theText begins with "Git" then
set gitMe to theText
end if
end repeat
write gitMe & ";" & (current date) & linefeed to myFile
delay 15
end if
end tell
end repeat
on error errstr number errNum
close myFile
end try

view raw
hosted with ❤ by GitHub


HipChat had *by far* the hardest hierarchy to find the name / chat room info in. For RubyMine, the file path and file name are in the window title and the git project are in one of the static texts near the top level. Microsoft Teams was similarly friendly in that the title of the window reflected the context it was being used in.

Future plans:

Hammerspoon looks a little more promising for doing anything more complex and/or DRYing this up, but there’s something to be said for being able to quickly hack your way to the data you want vs. actually having to plan things out.