Azure Cloud Shell Daily Driver (Zsh + Vim + Git + Tf + K8s)

Customizing Azure Cloud Shell with Zsh and Fish for use with Terraform, Git and Kubernetes

We’ll go over customizing your Azure Cloud Shell in a way that helps you get things done on a daily basis in vim, git, terraform and kubernetes.

Azure Cloud shell is super handy because you can get to it over the web, it stores your settings, and if set up correctly, can allow for resumable sessions!

Note that you can also load it from VS Code / VS Codium in the console area – which we’ll cover at the very end <3

Custom Browser Font

First, you’ll need a proper font for your browser. In my case, Chromium at the moment, but Firefox is similar. Essentially we’ll be adding a patched font to the browser so that we can use it when we’re in the web version of Azure’s Cloud Shell. I use Nerd Fonts, but there’s also Powerline fonts. If you need something to help make up your mind: NerdFonts are generally larger in memory but also contain many more symbols. Powerline fonts are smaller.

In chrome://settings/fonts set the monospaced font to your downloaded patched font. You can try a few if you feel like the symbols aren’t great. I use Ubuntu Mono.

Chrome font settings screen:

Chrome Font Settings
Chrome Font Settings Screen Shot

Selecting Ubuntu Mono from the list:

Ubuntu Mono Font Selection Screenshot macOS 10.15
Ubuntu Mono Font Selection Screenshot macOS 10.15

 

Ubuntu Mono From Nerd Fonts
Ubuntu Mono Screenshot from nerdftons.com

Preview of Ubuntu Mono:

Ubuntu Mono Font Preview from Programming Fonts
Ubuntu Mono Font Preview From https://app.programmingfonts.org/#ubuntu

Test your new font with:

curl https://raw.githubusercontent.com/ryanoasis/nerd-fonts/master/bin/scripts/test-fonts.sh | bash - | more

Linuxbrew / Homebrew

Now let’s install homebrew with:

sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"

sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"

==> This script will install:
/home/linuxbrew/.linuxbrew/bin/brew
/home/linuxbrew/.linuxbrew/share/doc/homebrew
/home/linuxbrew/.linuxbrew/share/man/man1/brew.1
/home/linuxbrew/.linuxbrew/share/zsh/site-functions/_brew
/home/linuxbrew/.linuxbrew/etc/bash_completion.d/brew
/home/ryan/.cache/Homebrew/
/home/linuxbrew/.linuxbrew/Homebrew
==> The following new directories will be created:
/home/linuxbrew/.linuxbrew/bin
/home/linuxbrew/.linuxbrew/etc
/home/linuxbrew/.linuxbrew/include
/home/linuxbrew/.linuxbrew/lib
/home/linuxbrew/.linuxbrew/sbin
/home/linuxbrew/.linuxbrew/share
/home/linuxbrew/.linuxbrew/var
/home/linuxbrew/.linuxbrew/opt
/home/linuxbrew/.linuxbrew/share/zsh
/home/linuxbrew/.linuxbrew/share/zsh/site-functions
/home/linuxbrew/.linuxbrew/var/homebrew
/home/linuxbrew/.linuxbrew/var/homebrew/linked
/home/linuxbrew/.linuxbrew/Cellar
/home/linuxbrew/.linuxbrew/Caskroom
/home/linuxbrew/.linuxbrew/Homebrew
/home/linuxbrew/.linuxbrew/Frameworks

Press RETURN to continue or any other key to abort

Some fun output, note the portable ruby:

==> Downloading https://linuxbrew.bintray.com/bottles-portable-ruby/portable-ruby-2.6.3.x86_64_linux.bottle.tar.gz
######################################################################## 100.0%
==> Pouring portable-ruby-2.6.3.x86_64_linux.bottle.tar.gz
==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
  https://github.com/Homebrew/brew#donations
==> Tapping homebrew/core
Cloning into '/home/linuxbrew/.linuxbrew/Homebrew/Library/Taps/homebrew/homebrew-core'...
remote: Enumerating objects: 5155, done.
remote: Counting objects: 100% (5155/5155), done.
remote: Compressing objects: 100% (4952/4952), done.
remote: Total 5155 (delta 48), reused 300 (delta 6), pack-reused 0
Receiving objects: 100% (5155/5155), 4.37 MiB | 0 bytes/s, done.
Resolving deltas: 100% (48/48), done.
Checking connectivity... done.
Tapped 3 commands and 4933 formulae (5,196 files, 13.6MB).
Already up-to-date.
==> Installation successful!

==> Homebrew has enabled anonymous aggregate formulae and cask analytics.
Read the analytics documentation (and how to opt-out) here:
  https://docs.brew.sh/Analytics

==> Homebrew is run entirely by unpaid volunteers. Please consider donating:
  https://github.com/Homebrew/brew#donations
==> Next steps:
- Install the Homebrew dependencies if you have sudo access:
  Debian, Ubuntu, etc.
    sudo apt-get install build-essential
  Fedora, Red Hat, CentOS, etc.
    sudo yum groupinstall 'Development Tools'
  See https://docs.brew.sh/linux for more information.
- Configure Homebrew in your ~/.profile by running
    echo 'eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)' >>~/.profile
- Add Homebrew to your PATH
    eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
- We recommend that you install GCC by running:
    brew install gcc
- Run `brew help` to get started
- Further documentation:
    https://docs.brew.sh
Warning: /home/linuxbrew/.linuxbrew/bin is not in your PATH.

Once done, we’ll need to fix the path: echo 'eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)' >>~/.bashrc && . ~/.bashrc

If all went well, we should be able to run brew and see the following:

brew
Example usage:
  brew search [TEXT|/REGEX/]
  brew info [FORMULA...]
  brew install FORMULA...
  brew update
  brew upgrade [FORMULA...]
  brew uninstall FORMULA...
  brew list [FORMULA...]

Troubleshooting:
  brew config
  brew doctor
  brew install --verbose --debug FORMULA

Contributing:
  brew create [URL [--no-fetch]]
  brew edit [FORMULA...]

Further help:
  brew commands
  brew help [COMMAND]
  man brew
  https://docs.brew.sh

Homebrew command line apps:

  • azure-cli
  • terraform
  • ansible
  • ripgrep
  • exa
  • bat
  • curl (current)
  • mc (think Norton Commander, but better)
  • zsh with zsh-git-prompt
  • bash with bash-git-prompt
  • fish
  • Current GNU coreutils (https://www.gnu.org/software/coreutils)

All-in-one install:

brew install terraform ansible azure-cli ripgrep exa bat fzf zsh fish tmux mc curl coreutils

Git

Now we can proceed to add the tools we’ll need. I’ll try to categorize them so that you don’t end up with stuff you don’t want. Remember: though this can be your primary console with some work-arounds, most likely you’ll want to make this set up portable in your dotfiles and sync things via git. We’ll start there:

export PS1='\w\[\033[0;32m\]$(__git_ps1) \[\033[0;32m\]─\[\033[0m\033[0;32m\] \$\[\033[0m\033[0;32m\] ▶\[\033[0m\] '

This simple one-liner results in the following for your Bash prompt:

Green Triangle Bash Prompt With Path and Git
Green Triangle Bash Prompt With Path and Git

Note: if you want to further customize the Bash prompt colours and to add blinking: https://misc.flogisoft.com/bash/tip_colors_and_formatting

Now let’s clone a git repo (git is installed by default in Azure Cloud Shell):

git clone https://github.com/Azure/terraform-azurerm-compute.git

You can see from the prompt we’re in the master branch!

Git bash Prompt With Branch in PS1
Git bash Prompt With Branch in PS1

Sample git prompts (oh-my-fish and oh-my-zsh are defaults, rainbowbrite used for bash-it, in order):

Custom Shells

I’d recommend not modifying the bash shell too much as it’s our default, and there’s no way to change it at present. Similarly, though we can install a new bash shell version, we need to chain-load it in order to have it auto-load. That being said, you probably won’t need a bash that’s up to date at this point.

Other helpers

For the Fish fan: Oh-my-fish (make sure fish is already installed via brew install fish)

curl -L https://get.oh-my.fish | fish

For the original zshers, or those that are ok with the new macOS default: oh-my-zsh (note: changing the default shell will fail because we don’t have a password 🙂 )

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

For the Bash users: bash-it

git clone --depth=1 https://github.com/Bash-it/bash-it.git ~/.bash_it
~/.bash_it/install.sh --silent

FZF (fuzzy finder):

 brew install fzf && (brew --prefix)/opt/fzf/install

Kubernetes

Homebrew packages for K8s usage:

brew install kubectx kube-ps1 istioctl linkerd helm stern kubeaudit derailed/k9s/k9s derailed/popeye/popeye

In either your ~/.bashrc and/or ~/.zshrc shell configs:

source "/home/linuxbrew/.linuxbrew/opt/kube-ps1/share/kube-ps1.sh"
PS1='$(kube_ps1)'$PS1

For krane, you’ll need ruby first:

brew install ruby && . ~/.zshrc # or your bash/fish config
gem install krane

Doom Emacs

brew tap daviderestivo/emacs-head
brew install emacs-head --HEAD --with-modules
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom install

Vim

brew install neovim --HEAD
git clone https://github.com/k-takata/minpac.git ~/.config/nvim/pack/minpac/opt/minpac
echo 'set clipboard+=unnamed' >> ~/.config/neovim/init.vim

tmux

tmux --HEAD --with-utf8proc

# Fix Scrolling
tee ~/.tmux.conf <<EOF
unbind +
bind + \
  new-window -d -n tmux-zoom 'clear && echo TMUX ZOOM && read' \;\
  swap-pane -s tmux-zoom.0 \;\
  select-window -t tmux-zoom

unbind -
bind - \
  last-window \;\
  swap-pane -s tmux-zoom.0 \;\
  kill-window -t tmux-zoom
EOF

# Kubernetes tmux
wget -O ~/kube.tmux https://raw.githubusercontent.com/jonmosco/kube-tmux/master/kube.tmux 
echo 'set -g status-right "#(/bin/bash $HOME/.tmux/kube.tmux 250 red cyan)"' >> ~/.tmux.conf

 

Disable Natural Scrolling in macOS Catalina

This is perhaps the first thing I do on all OSX machines: set the scroll to match my Linux and Windows computers.

No judgement call as to which way is “right”, I’m simply used to this by now, and instead of changing it on two other operating systems, scripting it on only one seemed easier.

While this looks very similar to previous iterations, I found my old scripts no longer working on OSX 10.15 for a host of settings and preferences. Note: I had to logout to make this work.

Catalina Normal Scrolling Script

defaults write -g com.apple.swipescrolldirection '0'

How This Was Determined

Sadly, the best method I could find for tracking this down was:

defaults read > beforechange.txt
# Change setting in system prefs
defaults read > afterchange.txt
diff -U 20 beforechange.txt afterchange.txt
OSX Defaults Diff
Using the command line to diff defaults in OSX to determine how to programmatically set System Preferences

This method can be used to track down a bunch of the new default settings/settings names.
Of course, you can always

defaults read | grep -C 10 scrolling

(substituting “scrolling” with the setting you’re looking for).

Remember: many of the defaults require a logout, a finder/dock restart (killall Dock && killall Finder), or a full reboot.

Tada 68 Pro Akko x Maxkey on MacOS

Interesting name for a “Tada 68” board, but it looks like this has a non-programmable PCB, unlike the “Saber 68” (new Tada 68).

That being said, it has most of what you want aside from the ability to set up function combos to send the four-key keyboard shortcuts.

The easiest change is the alt/Windows key swap resulting in Command and Alt, which can be done in “Settings”, “keyboard”, “modifier keys”, then select “BK868B” (this cannot be renamed AFAIK, though you can give it a bluetooth device identifier, more on that a bit later 🙂 ).

Settings required to set up option/alt and command/win keys for macOS.
Settings required to set up option/alt and command/win keys for macOS.

If you have a keyset with a control labelled “caps lock” key, you can optionally set the “caps” modifier to “control” in the settings as well while you’re here:

Tada 68 Pro caps lock to control modification for Apple macOS (OSX)
Tada 68 Pro caps lock to control modification for Apple macOS (OSX)

Now we can also rename the device to something more recognizable, via “setup bluetooth keyboard”, then selecting it from the keyboard list:

Renaming a BK868B Tada 68 Pro Akko Maxkey
Renaming a BK868B Tada 68 Pro Akko Maxkey
macOS renamed Tada68Pro BK868B Apple Keyboard
macOS renamed Tada68Pro BK868B Apple Keyboard

Now that we have it selected and renamed, we unfortunately don’t get battery levels, but it’s actually quite functional.

Bonus for Keyboard Maestro Users

If you want to setup desktop screenshot, area screenshot and area screenshot to desktop keys, and have set the caps-control swap above using Keyboard Maestro (or are OK using that far-away control bottom left, or on the far right, there are a couple of controls):

Here we’ll set up “control-a” where you’d hold control, and press the a key at the same time to grab a screenshots of all of your screens (or just the one if you’re on a laptop with an external monitor):

Setting up screenshot macros in Keyboard Maestro on macOS
Setting up screenshot macros in Keyboard Maestro on macOS

Capture your all (A) your screen(s) to the clipboard using control-a via the Keyboard Maestro built-in actions:

Capture screenshot to clipboard using "control+s" hotkey or keyboard shortcut
Capture screenshot to clipboard using “control+a” hotkey or keyboard shortcut

In the next example, we use “control+a” to map to “control+command+shift+4” in order to take a screengrab of an area (A) and store it in the clipboard. Your cursor will change, and the image is saved to your clipboard to paste into Slack, Gmail, etc.:

Taking a screengrab to your clipboard with with Keyboard Maestro on macOS
Taking a screengrab to your clipboard with with Keyboard Maestro on macOS

 

Setting up a control+d shortcut to save your screengrab area as a file on your desktop (D):

Control+D to save screen grab area to desktop with Keyboard Maestro on Apple MacOS (OSX)
Control+D to save screen grab area to desktop with Keyboard Maestro on Apple MacOS (OSX)

Flashing HHKB Jp Controller Mac Layout Hex

Having recently received Hasu’s fantastic HHKB JP Bluetooth Controller, I noticed one irksome thing: the alt and command keys were reversed on the left hand side, and the kana key that I typically remap to right command was reverted.

This is normal: the keyboard targets Windows users primarily, but one thing you lose when upgrading to the Hasu controller are the dip switches that allow changing the role of caps lock, the arrow keys and setting it to macOS mode.

The default HHKB layout without Hasu controller
The default HHKB JP OSX layout without Hasu controller – link

Of course it’s a pretty easy change to fix: you simply need to head over to the TMK Keymap Editor and edit the current layout, then download the hex file and flash it, but I’ve done the work, so I might as well share!

HHKB Hasu Jp OSX Layout
A layout that reverses alt and command and sets the kana key to right command for use with macOS computers
HHKB Hasu Jp Alternative Controller Layer 2
An overview of the second layer (layer1, counting from 0) used in the Hasu HHKB JP default layout

First off, as in other tutorials, you’ll need the DFU/AVR tools installed in OSX. This is accomplished by using Homebrew.

The code to get these installed is as follows:

brew tap osx-cross/avr
brew install avr-libc dfu-programmer

Next up, you’ll need to download the layout hex file that I have compiled here: “mac_version.hex“. If, for whatever reason, you need to go back to the default firmware, I have that compiled as well: default.hex. Remember to unzip these files prior to using them if Safari doesn’t automatically unzip your downloads.

Finally, put the keyboard in DFU mode by pressing the button on the back of the controller, where the dip switches used to reside. You may need to remove the cover to do so, and note that you can use the cover if you have short fingernails.

Once in DFU mode, this one-liner will erase the firmware, load the new hex layout and reset the keyboard so that you can use it once again. Note that I’m assuming your hex file will be in the Downloads folder in your home directory, and I’d also recommend having a spare keyboard handy in case anything goes wrong.

Bash script:

sudo dfu-programmer atmega32u4 erase --force && \
sudo dfu-programmer atmega32u4 flash ~/Downloads/mac_version.hex && \
sudo dfu-programmer atmega32u4 reset

Finally, if your layout is still a bit odd (underscore and pipe not working), you may have set the keyboard to the wrong layout. In Apple OSX this can be done by opening the keyboard section of the settings and changing the keyboard layout. Note that it doesn’t really matter that ANSI is suggested when you hit shift+z, shift+?, but that you select JIS once completed, like so:

Open the keyboard panel in System Preferences, then click “Change Keyboard Type…” on the bottom left
Click continue (or press return) to start the detection wizard
Press shift+z, then shift+? as instructed
Select the JIS radio button, even though it defaults to ANSI

Getting Started with Salt Stack Windows Minions

This how-to on Windows Salt minions will just scratch the surface of the power behind Salt, and will cover remote execution, installation and management.

Please note: I’m new to Salt, and I’m a recovering Windows syasdamin from the 2000-2013 era. This means I probably have a bunch of out-moded ways of working with Windows.

To this end, this first question would be: why? Active Directory and associated policies do a good job of managing Wintel already. I’ve had luck in the past managing hundreds, thousands of servers, desktops and laptops using the tools Microsoft provides without too much issue, and I’ve heard things only got better since I left for greener pastures.

The easy answer is that many organisations may now find themselves with a combination of Linux and Windows servers, Windows and macOS workstations, coupled with the odd BSD system here and there, Linux desktops and Unix servers.

What Salt allows for is a management platform that covers all of these platforms with a common language that is easy to read and hack away at (YAML) and very extensible using a programming language with a relatively gentle learning curve (Python).

In my case, most things running at home are either running OSX or some version of Linux/BSD, but there are a few of oddball Windows 10 computers doing things Microsoft-based computers do pretty well; gaming, spreadsheets, touchscreen-enabled full-desktops.

The idea of managing Windows computers with Salt came to me pretty late into the journey through the docs, but hit me like a freight-train as these are often the computers that require the most maintenance for me personally, meaning reinstalls, reconfiguration and general “care and feeding”. Having gone down the path of running my own Active Directory at home a few times and finally settling on a couple of Apple “servers”, this seemed like a pretty good work-around that wouldn’t introduce much pain.

The first caveat, however, was that I was unable to get Windows-based Salt minions to be accepted by OSX-based Salt masters. Not the end of the world, but it meant running Docker or a Vagrant box to handle the Windows 10 computers. I ended up simply installing Salt on an Ubuntu-based storage server I already had running that had recently been upgraded to Xenial instead, but I’m most likely going to be revisiting this architecture in the long-term.

Now that we have that out of the way, here’s the general architecture of what I’ll be going over:

Windows Salt Minions
A simple Windows Salt Minions example with one Salt Stack master running Ubuntu Linux

The Nitty Gritty – Deploying Salt Minion Services to All of Your Windows Nodes

I’ll come right out and say that I cheated when it came to installing the Minion service on the nodes at home in that I already had Chocolatey installed, and that I simply used the default salt hostname for the master by adding it the the static DNS entries on my DNS server.

This last bit, the part about the DNS server, might be a luxury depending on your install location. I also can’t expect that Chocolatey is installed everywhere, but just quickly, here’s how that scenario works:

  1. Install Salt Master and start the service on a computer in your network
  2. Ensure that there is a DHCP reservation for the IP of this node
  3. Add a DNS entry salt for that DHCP-reserved IP
  4. Install the Salt minions via choco install -y saltminion on the Windows hosts
  5. Accept the keys on the Linux master via salt-key -A

Should you have to go about this a different way, you can point an alias (CNAME) to the hostname in your network, OR add a manual host entry to your nodes (I’ve used Group Policy to do this in the past, with network scripts), or finally, you can simply change the Salt Minion config to point to the new hostname.

As for the deployment of the Salt Minion service and binaries, you could do this over RDP for a few hosts, Group Policy, or via psexec:

Salt-Minion-2016.11.5-x64-Setup.exe /S /master=SALTMASTER

This snippet will install the Salt Minion service, configure it to point at `SALTMASTER` (replace this with the hostname you’d prefer) then start the service. Note: as mentioned above, I haven’t tested this myself!

The most up-to-date Salt Minion binaries can be found here: https://docs.saltstack.com/en/latest/topics/installation/windows.html Should the version change, you’ll want to update that one-liner as well.

Accepting the Minions

Assuming you’ve got your minions now sending requests to the Salt master node, you need to approve them.

On the master node running Linux, this can be done like so:

sudo salt-key -A

You’ll be greeted with a list of nodes to be accepted, and you can take a moment to note the names and accept them:

user@ubuntu:/srv/salt# sudo salt-key -A
[sudo] password for user: 
The following keys are going to be accepted:
Unaccepted Keys:
WINDOWSGAMES
Proceed? [n/Y] 
Key for minion WINDOWSGAMES accepted.
user@ubuntu:/srv/salt#

Testing the Minions

Now that you have minions installed and working, let’s run a quick test that proves we can run remote commands on them. Note that this is just scratching the surface, and that “push commands” are just one way of working with Salt Stack, you can also have the nodes “pull” from the Salt master on a regular basis.

Sample run command:

sudo salt -G 'os:windows` cmd.run 'dir'

This should give you a directory listing on each of the nodes, like so:

WINDOWSGAMES:
 Volume in drive C has no label.
 Volume Serial Number is 3FF7-A973
 
 Directory of C:\Windows\system32\config\systemprofile
 
 07/16/2016 04:47 AM <DIR> .
 07/16/2016 04:47 AM <DIR> ..
 07/16/2016 04:47 AM <DIR> AppData
 0 File(s) 0 bytes
 3 Dir(s) 66,483,965,952 bytes free

Package Management on the Master Node

You’ve got a few options for package management for Windows Salt minions:

  • Chocolatey packages
  • Cygwin Packages
  • Master-shared packages (WinRepo)

Personally, I’m currently using Chocolatey package management because it’s what I’m used to, but I certainly do still use Cygwin for development purposes from time-to-time since Windows Bash mode has still yet to become commonplace. The third option, however, is probably the most scalable, and more user-friendly.

To get started with Windows repositories on the Linux Salt master, you’ll need to run:

sudo salt-run winrepo.update_git_repos

This means that you now have a repository for Windows packages on the Master node, but also note that these don’t come with binaries – they will fetch and send them as needed. I can see this as both good and bad, in a corporate setting you’ll want to create your own SLS files and point them to known-good sources.

To install a package on all of your Windows remote nodes using the Linux Salt master repository:

sudo salt -G 'os:windows' package.install 'firefox'

Note that if ever you want to specify a specific action for a node in particular, that’s done like so:

sudo salt 'WINDOWSGAMES' pkg.install 'firefox'

I’m trying to follow the “pets vs cattle” paradigm here, though you can also use -G to specify groups of servers/workstations/laptops which can overlap in order to manage things in a more granular approach (bit of a pun here, the G is for Grains).

Useful Commands for Windows Salt Minions

In no particular order, here’s a quick dump of the commands I used during my first weekend managing Salt minions at home running Windows 10.

Show installed applications:

sudo salt -G 'os:windows' pkg.list_pkgs

List current Salt minion keys:

sudo salt-key -L

Delete an old Salt minion key:

sudo salt-key -d WINDOWSGAMES

Check if a service is available:

sudo salt -G 'os:windows' service.available 'salt-minion'

Restart a service:

sudo salt -G 'os:windows' service.restart 'salt-minion'

Flashing an AMJPad with QMK

Thanks to the hard work of /u/TerryMatthews on Reddit, there’s now a port of QMK for the AMJ numpad!

In order to get QMK on the number pad, however, a bit of work will need to be done, though none of it is insurmountable.

First off, I’ve compiled the AMJ Pad QMK default layout hex file for you to download.

The default layout is set up like this:

 /* Keymap _BL: (Base Layer) Default Layer
 * ,-------------------.
 * |Esc |TAB |BS  | =  |
 * |----|----|----|----|
 * | NL | /  | *  | -  |
 * |----|----|----|----|
 * | 7  | 8  | 9  |    |
 * |----|----|----| +  |
 * | 4  | 5  | 6  |    |
 * |----|----|----|----|
 * | 1  | 2  | 3  |    |
 * |----|----|----| En |
 * | 0       |./FN|    |
 * `-------------------'
 */

 /* Keymap _FL: Function Layer
 * ,-------------------.
 * |Esc |TAB |BS  | =  |
 * |----|----|----|----|
 * | NL | /  | *  | -  |
 * |----|----|----|----|
 * | 7  | 8  | 9  |    |
 * |----|----|----|RST |
 * | 4  | 5  | 6  |    |
 * |----|----|----|----|
 * | 1  | 2  | 3  |    |
 * |----|----|----| En |
 * | 0       |./FN|    |
 * `-------------------'
 */

Legend:

  • Esc – escape
  • BS – backspace
  • NL – Number lock
  • En – Enter
  • Fn – Function key, hold this and press keys in the second layout to activate them
  • RST – Reset – put the board in DFU mode

Programming the board can be done like other Atmel PCBs – just remember that this is an Atmel ATmega32u4.

As such, we can follow The Van Keyboards programming guide, which covers Linux, Windows and Mac.

On a macOS system, for example:

sudo dfu-programmer atmega32u4 erase --force
sudo dfu-programmer atmega32u4 flash ~/Downloads/amjpad_default.hex
sudo dfu-programmer atmega32u4 reset

Stop Windows 10 Update Notifications from Interrupting Your Games

I had taken quite the hiatus from Windows gaming while I worked on certification, but got a chance to have some quick Overwatch sessions last night, only to have one of the competitive matches interrupted by a popup that took me out of the game (I was able to alt-tab back in) that stated the following: “Updates Are Available. Required Updates need to be downloaded”.

Thanks for letting me know about the updates, but interrupting all apps is not the best user experience

Now don’t get me wrong, this is normally fine, but when you’re in the middle of something, say a movie, intense game, conference call or presentation, this behaviour is pretty awful.

No fear, however, there’s a fix, though definitely not obvious. Let’s disable the Windows 10 update notification. We’ll be using the cmd.exe tool in order to run the following script found on StackExchange:

cd /d "%Windir%\System32"
takeown /F MusNotification.exe
icacls MusNotification.exe /deny Everyone:(X)
takeown /F MusNotificationUx.exe
icacls MusNotificationUx.exe /deny Everyone:(X)
rem

This essentially denies the system from running the app itself, which will stop the popup.

In order to undo it:

cd /d "%Windir%\System32"
 icacls MusNotification.exe /remove:d Everyone
 icacls MusNotification.exe /grant Everyone:F
 icacls MusNotification.exe /setowner "NT SERVICE\TrustedInstaller"
 icacls MusNotification.exe /remove:g Everyone
 icacls MusNotificationUx.exe /remove:d Everyone
 icacls MusNotificationUx.exe /grant Everyone:F
 icacls MusNotificationUx.exe /setowner "NT SERVICE\TrustedInstaller"
 icacls MusNotificationUx.exe /remove:g Everyone
 rem

And there you have it! No more intrusive popups when updates are needed.

Now that we’ve modified this, a quick reminder that if you want regular updates, it may be worth checking for them once in a while.

New Zeal60 Keyboard Layout – Split Backspace, Split Right Shift, ANSI

I’ve really been enjoying the programmable firmware on the Zeal60 keyboard PCB from ZealPC. It’s definitely not all the bells and whistles, this layout gives you 62 keys in total, though you might need a keycap set that supports all of these keys. I’m currently using the Originative Co  Modern Beige set, though I hear most  (if not all) GMK sets would also  accommodate  this.

Zeal60 layout created with Keyboard Layout Editor – note the Fn button to the right of the shift key, and the backspace and backslash keys swapping spots, with an added tilde.

This layout addresses something I sorely needed: the ` and ~ keys (used with Github,  Bash and Slack on a daily basis).

In order to accomplish this, I need to edit the keymap.c and config.h files, which allowed me to generate a zeal60_rmac.hex  file for use with the QMK flasher tool (Win/Mac/Lin).

In order to follow the Zeal example, I also made a zeal60_keymap_rmac.bat file, which can be used with Windows.

The code is currently submitted to Wilba’s fork of QMK as a pull request, but here’s the code as well, so you can see what it looks like.

keymap.c:

// Rmac split backspace, split  shift layout for Zeal60

#include "zeal60.h"

// [0,13] is either left key of split backspace (e.g. HHKB \| key) or 2U backspace
// [1,13] is either backslash or ISO Enter
// [2,12] is either ANSI Enter or key left of ISO Enter
// [2,13] is right key of split backspace (e.g. HHKB `~ key)
// [3,1] is right key of split left-shift (e.g ISO key)
// [3,13] is right key of split right-shift (e.g. HHKB Fn key)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {

// Default layer
[0] = {
 {KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSLS},
 {KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSPC},
 {KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_GRV},
 {KC_LSFT, KC_NO, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, FN_MO13},
 {KC_LCTL, KC_LGUI, KC_LALT, KC_NO, KC_NO, KC_NO, KC_NO, KC_SPC, KC_NO, KC_NO, KC_RALT, KC_RGUI, FN_MO23, KC_RCTL}
},

// Fn1 Layer
[1] = {
 {KC_GRV, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL },
 {KC_CAPS, KC_TRNS, KC_UP, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_INS, KC_TRNS, KC_PSCR, KC_SLCK, KC_PAUS, KC_TRNS},
 {KC_TRNS, KC_LEFT, KC_DOWN, KC_RGHT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_HOME, KC_PGUP, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLD, KC_VOLU, KC_MUTE, KC_END, KC_PGDN, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS}
},

// Fn2 Layer
[2] = {
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS}
},

// Fn3 Layer (zeal60 Configuration)
[3] = {
 {KC_TRNS, EF_DEC, EF_INC, H1_DEC, H1_INC, H2_DEC, H2_INC, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, BR_DEC, BR_INC, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, S1_DEC, S1_INC, S2_DEC, S2_INC, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS},
 {KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS}
}

};

config.h:

#ifndef CONFIG_USER_H
#define CONFIG_USER_H

#include "../../config.h"

/* enable/disable LEDs based on layout */
#undef USE_SPLIT_BACKSPACE
#define USE_SPLIT_BACKSPACE 1

#undef USE_SPLIT_LEFT_SHIFT
#define USE_SPLIT_LEFT_SHIFT 0

#undef USE_SPLIT_RIGHT_SHIFT
#define USE_SPLIT_RIGHT_SHIFT 1

#undef USE_7U_SPACEBAR
#define USE_7U_SPACEBAR 0

#undef USE_ISO_ENTER
#define USE_ISO_ENTER 0

#endif //CONFIG_USER_H

And as promised, zeal60_keymap_rmac.bat:

@echo off

zeal60 backlight_config_set_values ^
 use_split_backspace=1 ^
 use_split_left_shift=0 ^
 use_split_right_shift=1 ^
 use_7u_spacebar=1 ^
 use_iso_enter=0 ^
 disable_when_usb_suspended=1 ^
 disable_after_timeout=10

zeal60 backlight_config_set_alphas_mods ^
 MOD ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA MOD ^
 MOD ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA MOD ^
 MOD ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA MOD MOD ^
 MOD ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA ALPHA MOD MOD ^
 MOD MOD MOD MOD MOD MOD MOD MOD MOD MOD MOD MOD MOD MOD

zeal60 keymap 0 ^
 KC_ESC KC_1 KC_2 KC_3 KC_4 KC_5 KC_6 KC_7 KC_8 KC_9 KC_0 KC_MINS KC_EQL KC_BSLS ^
 KC_TAB KC_Q KC_W KC_E KC_R KC_T KC_Y KC_U KC_I KC_O KC_P KC_LBRC KC_RBRC KC_BSPC ^
 KC_CAPS KC_A KC_S KC_D KC_F KC_G KC_H KC_J KC_K KC_L KC_SCLN KC_QUOT KC_ENT KC_GRV ^
 KC_LSFT KC_NO KC_Z KC_X KC_C KC_V KC_B KC_N KC_M KC_COMM KC_DOT KC_SLSH KC_RSFT FN_MO13 ^
 KC_LCTL KC_LGUI KC_LALT KC_NO KC_NO KC_NO KC_NO KC_SPC KC_NO KC_NO KC_NO KC_RALT FN_MO23 KC_RCTL

zeal60 keymap 1 ^
 KC_GRV KC_F1 KC_F2 KC_F3 KC_F4 KC_F5 KC_F6 KC_F7 KC_F8 KC_F9 KC_F10 KC_F11 KC_F12 KC_DEL ^
 KC_TRNS KC_TRNS KC_UP KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_UP KC_TRNS KC_TRNS ^
 KC_TRNS KC_LEFT KC_DOWN KC_RGHT KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_LEFT KC_RGHT KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_DOWN KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS

zeal60 keymap 2 ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS

zeal60 keymap 3 ^
 KC_TRNS EF_DEC EF_INC H1_DEC H1_INC H2_DEC H2_INC KC_TRNS KC_TRNS KC_TRNS KC_TRNS BR_DEC BR_INC KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS S1_DEC S1_INC S2_DEC S2_INC KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS TG_NKRO KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS ^
 KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS KC_TRNS

pause

Here’s a link to all three files with the compiled `zeal60_rmac.hex`.

Hope you enjoy this! It took a bit of doing, but was well worth it 🙂

Note that the files in question do a good job of walking you through the mods, making it rather easy. The one caveat was the tilde key being on a different row, as there’s only 13 spots per row, but that’s covered in the comments anyway.

As for the batch file for windows,  you can see that it’s really a combination of the config.h  and keymap.c files, to a certain extent, so copying and pasting the modified bits did the trick.

Zeal60 Zealencio Zealiostotle JSpacer Holtite Custom Keyboard Build

Custom Backlit Mechanical Keyboard - Ryan MacLean
The finished product – a ZealPC Zeal60 with holtite mod, Zealiostotlespacers, Originative Modern Beige Japanese keycaps resting in a Sentraq teal aluminum 60% case with teal plate.

After much research, I decided to assemble a keyboard using hand-picked parts.
The goals of this build were a bit lofty, but luckily I prevailed in the end:

  • Cold-swappable switches in case I change my mind later
  • Almost silent typing that would be safe for work
  • Tactile feeling on the keys when typing
  • The option to use backlighting at night
  • A small footprint so I could bring the keyboard home for the weekend
  • The board should be programmable and support multiple layouts
  • The keyboard should be as flat as possible
  • There should be no “ping” from the case
  • There should be no flex in the PCB when installing switches
  • Keycaps that fit Zealencios properly – Cherry profile thick keycaps

This led me to a few easy decisions: I would go with a ZealPC Zeal60 as it supported SMD LEDs, was programmable via QMK, was 60%. I’d also opt for Zealio 65g switches as they had great tactile response, and were fairly quiet when combined with Zealencios.

What I didn’t see coming was the arrival of the test Aristotle stems (via /r/mechmarket) and a last-minute purchase of JSpacers from The Van Keyboard. These were for two different projects, respectively: Gatistoles and Jailhoused Outemu blues. During those projects I got to wondering if I could mix the results, and sure enough, figured out a way to add JSpacers to Gatistoles. Not satisfied, I ended up trying this same mod in a Zealio housing, and then to seal the deal, put some Zealencios on top. This led to a tactile switch that was very quiet – more so than a typical dome keyboard.

Creating Zealiostotlespacers with Holtites - Ryan MacLean
Parts required for a “Zealiostotlespacer” – The Van Keyboard JSpacers (black rubber, bottom left), Zealios Switches (purple switch, middle), Aristotle switch stems (white, top right), and Zealencios (clear, top left). This creates a tactile, non-wobbly switch that is both satisfying and almost silent.

In order to be able to cold-swap switches as with the TeamWolf Zhuque+ and PC Gaming Master Race Mechanical Keyboard, I’d need to sort out a way to replicate the recepticles each of these boards implement. After doing a lot of research into LED sip sockets that might work, I stumbled across a Linus Tech Tips forum post about something called “holtites” which were used for this purpose. A few Reddit searches later I found out that the TE AMP connectors would fit the bill, and made a spur of the moment DigiKey order.

TE AMP Holtites - Ryan MacLean
TE Connectivity AMP Holtites – these allow you to “cold-swap” switches without desoldering, perfect for testing different switch types
Holtites Installed on ZealPC Zeal60 PCB - Ryan MacLean
Here you can see the Holtites installed in the Zeal60 board – they are flush-inserted using a spudger and a rotating motion to keep them snug.

Now that I had the switches and board picked out, I was left with a tough decision regarding the case and plate. Nothing I could find actually had the proper mounting standoffs for the Zeal60 PCB, most were of the “Poker” variety, which were missing top screw standoffs, as well as one lower-left. It looked like I had to compromise, and seeing as most 60% cases seemed to be incredibly similar, I ended up going with a Sentraq aluminum case and plate based on looks alone. I ended up getting a cyan case and plate, as well as purple versions of the same, intending to try and colour match the Zeal60. It later turned out that I preferred the cyan varieties, which you’ll see in the shots.

Custom keyboard with Zeal60, Zealiostotlespacers, Enjoy PBT Kana Keycaps, Sentraq Teal Plate and Case - Ryan MacLean
A pretty good match – the Sentraq teal aluminum case fit the purple legends with teal kana subscript

The Sentraq case with the Zeal60 is not without problems: both cases prohibited me from screwing in the leftmost PCB screw, and both seemed to have the wrong thread on the top left standoff. Nothing altogether impossible to get around, but disconcerting all the same. Furthermore, the fit and finish of the top plate in the case wasn’t great, causing the right side to be less snug than the left. All of this to say: I’m still on the lookout for a case and plate that fit well together, and have not ruled out making my own.

Sentraq Teal Case and Plate - Ryan MacLean
The Sentraq plate and case are pretty snug, but there’s definitely room for improvement. That being said, this is a 1:1 macro…

As for the keycaps, I searched all over, asked around and tried a few OEM samples and was not satisfied. I ended up purchasing an Originative Co Modern Beige purple set with kana legends which I am happy with in terms of looks, fit and sound, but am not sure they are fit for work. In other words – I’m still looking for a good set of keycaps. I’ve been told GMK might be the best call for the Aristotle stems, and as soon as I have a sample of them, I’ll report back.

Enjoy PBT Keycaps with Kana Legends - Ryan MacLean
Originative Co Modern Beige with purple legends and cyan kana keycaps
Custom keyboard with Zeal60, Zealiostotlespacers, Enjoy PBT Kana Keycaps, Sentraq Teal Plate and Case - Ryan MacLean
The Cherry profile is perfect for the Zealencios, which didn’t work very well with the OEM keycaps I had tried previously.

Finally, regarding the LEDs: though I was not at all interested in RGB LEDs (preferring white when possible), I ended up actually really liking the default rainbow profile (fn1+2). This is a bit colourful for most, but as I have them turned off most of the time, the assumption is that I’ll be the only one seeing them.

Originative Co Modern Beige Keycaps with Kana Legends - Ryan MacLean
The SMD LEDs are fairly bright when set to max!

Gallery of the rest of the shots from this set:

Building Zeal60 Keymaps on OSX

Before starting, this guide requires Homebrew, which you can install from here: https://brew.sh

Open Terminal, and paste in the following. It will take a while to build, but you should be able to get it all done in one fell swoop:

cd
git clone https://github.com/Wilba6582/qmk_firmware.git
cd qmk_firmware
git branch zeal60
cd keyboards/zeal60
git submodule update --init --recursive
brew tap osx-cross/avr
brew install avr-gcc avr-libc dfu-programmer

You can then make your keymap, like so:

  • make keymap=default
  • make keymap=poker2_ansi
  • make keymap=poker2_iso
  • make keymap=hhkb_ansi

You can run them all if you want, but you’ll probably only need one 🙂

Once you have your firmware, you can flash like so (for HHKB ANSI):

sudo dfu-programmer atmega32u4 erase --force
sudo dfu-programmer atmega32u4 flash hhkb_ansi.hex
sudo dfu-programmer atmega32u4 reset