Dev Keyboard: Moon Lander Mark 1 Review 😍

I’ve had pain in my right wrist from computer usage for a while now, last 5 years or so. To fix it I’ve done lots of stuff, like:

  • Move to using mouse left handed
  • Move to roller ball left handed mouse
  • Move to ergonomic keyboard
  • Move to fully split keyboard

In the arms race that is keeping my wrists working I decided to go for the next step and get a (expensive) programmable and columnar and see if it helped.

So I picked up the Moonlander Mark1 to give it a try: https://www.zsa.io/moonlander/

What is a columnar keyboard?

Instead of each row of keys being offset from each other each set of keys is directly above and below in a column. The logic being it’s easier to move your fingers up and down than side to side.


This took some adjustment, frequently I’d miss-type X/C/V but I do now find myself more comfortable when typing on the keyboard and find it notably less comfortable returning to a normal keyboard.

What makes it programmable?

This means I can change the layout, create macros and setup different layers.

For example, moving my right hand from the home keys to use the arrow keys on a normal keyboard causes strain. With the Moonlander I can create a “layer” which I toggle with a keypress and while toggled my home row become my arrow keys.

You can see my current layout here on the configurator site: https://configure.ergodox-ez.com/moonlander/layouts/ZrGNr/latest/0

So how did it go?

First up, getting used to the keyboard took me a good few weeks. Initially I tried to get used to the default layout which didn’t go well but when as I embraced programming it things went much smoother.

Within about 3 weeks my typing was back up to normal with slightly higher error count.

At this point though I wasn’t really getting the most out of the keyboard, month 2 and 3 was when I really fell in love.

I started to remap/reprogram it A LOT moving my commonly used keys to be close to my home row. During a normal day I’d keep track of how I used shortcuts, keys and punctuation and then work out how to move things around to make them easier to type. I stayed with QWERY for fear of not being able to type on a normal keyboard but outside that everything was fair game – backspace moved, enter moved, CTRL etc etc you get the point.

As an example of this which worked well for me as a programmer, under my left hands home I created a layer with all the punctuation I use when programming, no more shift and reaching to the top keyboard row. Now for a curl brace I tap my right little finger then E key.

This has been so nice!

Next up is the thumb bar, there are 3 keys under each. This means those under used digits which normally just mashing space can now do seriously useful stuff. Under my left thumb I have Enter, Delete and Super for example. The move of Backspace to under my left thumb had a drastic impact for me, I think reducing the reach for the backspace top right with my hand massively helped my wrist pain.

I’ve always tried to minimize my usage of the mouse for productivity, being able to map custom layers means things like Regoliths i3 based linux setup can easily be mapped via macros and layers to support single key press shortcuts to manage windows and the system. Where in the past I’ve bounced off i3 based systems I found armed with macros and layers on the keyboard they clicked so easily.


It’s been a hard and long journey, having to actively think about typing and what keys do what again hurt as it’s been second nature for so long BUT it’s been worth it.

Now I frequently find myself optimising a key/punctuation or combination I use regularly to reduce the movement used when typing it and it pays dividends every time.

If you like tweaking your workflow and spend you time writing code I’d highly recommend it as an investment.


Add Diagnostic Settings to Azure Storage account via Terraform

So you want to add a diagnostic setting to your Azure storage account via Terraform and you pass the storage account ID to target_resource_id only to get the following error:

Status=400 Code=”BadRequest” Message=”Category ‘StorageWrite’ is not supported.”

Here is the fix, the diagnostic target resource actually needs to be a sub-resource of the storage account, the ID for that is constructed as:


Using this you can create a terraform file similar to the below and it will create the diagnostic setting for you on the blob account.

For a more detailed explanation the issue here goes into more detail.

resource "random_string" "random" {
length = 5
special = false
upper = false
number = false
resource "azurerm_log_analytics_workspace" "core" {
name = "corelaw${random_string.random.result}"
location = "westeurope"
resource_group_name = "test1"
sku = "PerGB2018"
retention_in_days = 30
resource "azurerm_storage_account" "core" {
location = "westeurope"
resource_group_name = "test1"
name = "corestor${random_string.random.result}"
account_tier = "Standard"
account_replication_type = "LRS"
allow_blob_public_access = "false"
is_hns_enabled = true
enable_https_traffic_only = true
resource "azurerm_monitor_diagnostic_setting" "core-diagnostic" {
name = "readwritecore${random_string.random.result}"
# See workaround details: https://github.com/terraform-providers/terraform-provider-azurerm/issues/8275#issuecomment-755222989
target_resource_id = "${azurerm_storage_account.core.id}/blobServices/default/"
log_analytics_workspace_id = azurerm_log_analytics_workspace.core.id
log {
category = "StorageRead"
enabled = true
log {
category = "StorageWrite"
enabled = true
metric {
category = "Transaction"
enabled = true
retention_policy {
days = 5
enabled = true

Running Pester Tests in Parallel

I’m working on a project which uses pester tests to validate our deployment and system health.

We’ve accumulated a lot of tests. Nearly all of these sit and wait on things to happen in the deployment and we’re running them sequentially which takes around 20mins. Each of our tests is in it’s own file.

What I wanted to do was, as these are IO bound tests waiting on things to trip in the environment or polling http endpoints, run them all in parallel. One file per thead or something similar.

This issue discusses this topic. I took a lead from the Invoke-Parallel snipped and started playing, unfortunately the output from the tests was mangled and overlapping.

Then I realised I could use Powershell Jobs to schedule the work and poll for the job to be completed then receive each jobs to have to output displayed nicely.

So now the output looks good:

Note: We’re using Pester v4 you’ll have to do a little bit of fiddling to port to Pester 5

Install-Module Name Pester RequiredVersion 4.6.0 force
$testFilePath = "./tests"
# Start a jobs running each of the test files
$testFiles = Get-ChildItem $testFilePath
$resultFileNumber = 0
foreach ($testFile in $testFiles)
$testName = Split-Path $testFile leaf
# Create the job, be sure to pass argument in from the ArgumentList which
# are needed for inside the script block, they are NOT automatically passed.
Start-Job `
ArgumentList $testFile, $resultFileNumber `
Name $testName `
ScriptBlock {
param($testFile, $resultFileNumber)
# Start trace for local debugging if TEST_LOG=true
# the traces will show you output in the ./testlogs folder and the files
# are updated as the tests run so you can follow along
if ($env:TEST_LOGS -eq "true") {
Start-Transcript Path "./testlogs/$(Split-Path $testFile leaf).integrationtest.log"
# Run the test file
Write-Host "$testFile to result file #$resultFileNumber"
$result = Invoke-Pester "$testFile"
if ($result.FailedCount -gt 0) {
throw "1 or more assertions failed"
# Poll to give insight into which jobs are still running so you can spot long running ones
do {
Write-Host ">> Still running tests @ $(Get-Date Format "HH:mm:ss")" ForegroundColor Blue
Get-Job | Where-Object { $_.State -eq "Running" } | Format-Table AutoSize
Start-Sleep Seconds 15
} while ((get-job | Where-Object { $_.State -eq "Running" } | Measure-Object).Count -gt 1)
# Catch edge cases by wait for all of them to finish
Get-Job | Wait-Job
$failedJobs = Get-Job | Where-Object { -not ($_.State -eq "Completed")}
# Receive the results of all the jobs, don't stop at errors
Get-Job | Receive-Job AutoRemoveJob Wait ErrorAction 'Continue'
if ($failedJobs.Count -gt 0) {
Write-Host "Failed Jobs" ForegroundColor Red
throw "One or more tests failed"
view raw run.ps1 hosted with ❤ by GitHub

Here is the full repro: https://github.com/lawrencegripper/hack-parallelpester