Cody Bunch Some Random IT Guy - OpenStack, DevOps, Cloud, Things

VMworld Day -1


This, this was the easy day. The day where one finishes driving in (Yes, driving).

Going to the Hoover Dam, and well, having an easy dinner with friends you only see a few times a year.

Damn Dam

And last but not least, sending the final emails around the vCoffee exchange:


vCoffee Exchange

If you missed the emails, here are the details:

  • Drop off: Monday, vBrownBag area in the VMTN communities space.
  • Pickup: Wednesday, vBrownBag area in the VMTN communities space.
  • How: Once dropped off, I’ll mix them up Secret Santa style before Wednesday.
  • Also: There may or may not be a surprise to go along with it.

Reverse SSH for Lab Access

There are times, like when you’re writing a book with Kevin Jackson, when you need access to your homelab while traveling. Sure you could set up a VPN, and there are plenty of decent guides for that, but, there are times when SSH is just simpler, and just sort of works.

For this to work, without opening ports on your lab firewall, we will need a server hosted somewhere else. Doesn’t have to be large, in fact a (t2.micro) instance will work just fine.

Build the cloud server

Log into your cloud provider of choise, and choose an Ubuntu flavored instance. Update, install SSH, and lock it down sone. The script linked below will do a lot of that work for you.

Note: Now that you have a JumpBox setup, you will want to lock it down some, so that not just anyone can get in. This script, not included here to keep the post short, enables a number of protections against spoofing. Disables all access inboud except for SSH, then installs and configures Aide, Fail2Ban, psad, and logwatch which will email you a daily report.

Build the JumpBox

In your lab, you will now need to configure the JumpBox to “call home”. That is, to SSH out to the cloud server, and setup up a tunnel in the reverse direction. For this we use the following script:

Once you’ve created said script, set it to executable:

chmod +x ./

Finally, we configure it to run every minute, to bring the ssh session back up should it fail. To do that, add the following line to your crontab:

*/1 * * * * ~/ >> tunnel.log 2>&1


Now that all the parts are in place, test a connection from your cloud server to the JumpBox:

From your console:

ssh user@cloudserver.address

From the cloud server:

ssh -l pirate -p 2222 localhost

And there you go.

Docker on Raspberry PI for Fan Control

My home office gets a bit stuffy in the afternoon / early evenings. That I have west facing windows, this is not unexpected. Now, one could have solved this by adding shades, or an additional AC vent, or you know, not working past a given time.

… but that is no fun. So, I over engineered a solution using Docker, Python/Flask, and a Raspberry PI. Overkill? Maybe. Entertaining? Yup.


To keep things readable, I’ve broken the build out into a few components:

  • Physical prep
  • Setup the Raspberry PI
  • Docker
  • Flask / Python
  • Test & Run

Physical Preparation

As we’re doing something “IRL”, there are some parts required, and some hookups needed. The pictures in this section are borrowed from the Adafruit page for the Powerswitch Tail 2.

Parts list

You will need the following parts for this project:


First up, connect the Powerswitc Tail to the fan. Then use jumper wires to connect it to the breadboard like this:

Powerswitch Tail to Breadboard

If that is hard to read, you are connecting the +in on the Powerswitch Tail to a numbered GPIO pin. You are then connecting the -in to a GND pin. Once you have done this, you can plug the Powerswitch Tail into 110V power.

Setup the Raspberry PI

Install HypriotOS and ensure you can login. To do this, I use the flash utility:

./flash --hostname node-00 --ssid lolwifi --password lolinternets \

Once that finishes, plug the SD card into the rPI, power it on, and ensure it’s up to date:

sshpass -p hypriot /usr/local/bin/ssh-copy-id pirate@node-01.local

ssh pirate@node01.local \
    "sudo apt-get update && \
    sudo apt-get install -y git wget curl vim mosh byobu && \
    sudo apt-get dist-upgrade -y"


Next up, we need to create a Dockerfile for the container that will control the fan. Here is one to start from:

  • Line 1 - Tells Docker we want to start with the Arm version of Alpine Linux
  • Line 3 - Installs python, pip, build utilities, and curl so we can test.
  • Line 4 - Uses pip to install python modules needed to control the fan
  • Line 5 - Copies our fan control program into our Docker container. We break this script down next.
  • Line 7 - Exposes port 80 so we can control the fan remotely

Flask / Python

Phew, that was a lot of work, no? Why don’t you turn on the fan and sip a cold drink? Oh… we still haven’t told python how to control the fan just yet. Place the following python into a file called

As you’ll see in both the script and the breakdown that follows, we use the RPi GPIO library to interface with the Powerswitch Tail to turn the fan on and off. We also use Flask to provide a simple REST interface to control the fan remotely.

Here’s the script, followed by a breakdown of what it is doing:

  • Line 1: Imports the GPIO library
  • Lines 2&3: Import the parts of Flask we are going to use.
  • Lines 5-12: Initialize the GPIO pin we are going to use
  • Lines 14-16: Create a new Flask app
  • Lines 18-20: Define the /status endpoint.
    • This allows us to request fan status. 0 = off; 1 = on
  • Lines 22-29: Defines the power control endpoint /power/[on off]
    • Line 25 turns the fan on
    • Line 27 turns the fan off
    • Line 29 returns the new status of the fan
  • Lines 31&32: Tells python to use Flask to serve our app on all interfaces on poer 80


Phew, all that hard work I broke a sweat. Let’s actually turn the fan on!

To do that, once all your files are saved, you should have a directory structure that looks like this on the Raspberry PI:

$ tree
├── Dockerfile

Now, let’s have Docker build our image:

$ pwd

$ docker build -t fan-control .

Next, run the image:

$ docker run -d -p 80:80 --restart=always --privileged fan-control python /

What this does, is tells Docker to run our container in the background, to keep it running, and to allow it access to the hardware (Needed for GPIO control). Finally, it tells Docker to start our fan controller in the container.

Controlling the fan

Now, you can control the fan. From the RPi you can do this as follows:

# Get status
$ docker exec keen_neumann curl -s http://localhost/status

# Turn it on
$ docker exec keen_neumann curl -s http://localhost/power/on

You can also do this remotely using the same curl commands, changing localhost to the IP or hostname of the RPi:

$ curl -s http://node-01.local/power/off

$ curl -s http://node-01.local/status


The 8-Minute AeroPress

Today we take a little bit of a detour into coffee land to talk about my preferred method for making coffee. Specifically, how I make an 8-Minute AeroPress.

Why 8 minutes? The trick here, is that we use the timing to get our water to within an ideal temperature range for steeping and brewing with the AeroPress. AeroPress recommends, and I have found, that along with a longer steeping time, the lower temperature produces an amazing cup of coffee.


Of course there is some gear, and disclaimer of disclaimer, these are all links.

Note: You don’t need these specific things. They’re the ones I’ve found to work best for me.

You’ll notice I left the actual coffee off that list. This is because that is super highly subjective. My current goto is Cottonwood from Brown Coffee Co.. Great coffee, great consistency, and roasted in a way I aspire to get to in my own roasting.


First things first, we need to get the grind right. For the Porlex Mini linked above, this is 7 “clicks”. Clicks? Yes, clicks:

Porlex Mini

On the top half of the grinder, turn the knob 6 “clicks” counterclockwise.

For other grinders you are looking for a grind a bit finer than what you’d use for French Press. This is because we’re going to be steeping for a bit longer than is typical, and want to get the extraction just so.


  1. Fill the kettle, set it to boil
  2. Set the funnel into your mug
  3. Grind 1.25 “scoops” or about 16.5 or 17g of coffee
  4. Set up for an “Inverted AeroPress”:

Inverted AeroPress

Image CC licensed

  1. Dump the grounds into the AeroPress.

At about this point, the kettle should be boiling and click itself off. Here is where the first 4 minutes of the 8 comes from. Set a timer for 4 minutes to let the water cool.

  1. Add water to the first marker (should be number 3 if inverted). About 50g
  2. Using the back of the scoop, stir gently
  3. Place the filter in the cover, place the cover filter side up, over the end of the AeroPress, and wet the filter (just wet it, we add the rest of the water later).
  4. Wait another 4 minutes (you see, 8 minutes)
  5. Add the rest of the water. I shoot to fill to about 1cm from the edge, or about 250g total in water.
  6. Screw filter on
  7. Flip into funnel and press

OSX Update All The Things

I have a little alias, yes I do. I have a little alias, how about you?

Specifically, this alias keeps my box up to date, and is derived from @mathis’s excellent dotfiles:

alias update='sudo softwareupdate -i -a; \
    brew update; \
    brew upgrade; \
    brew cleanup; \
    brew cask outdated | xargs brew cask reinstall; \
    npm install npm -g; \
    npm update -g; \
    sudo gem update --system; \
    sudo gem update; \
    sudo gem cleanup; \
    vagrant plugin update; \
    sudo purge'

Hope that helps someone other than me.

Upgrading UniFi Controller on UBNT Cloud Key by Hand

Today I needed to update the version of the UniFi Controller that runs on my UBNT Cloud Key. To do this, I basically followed the directions here.

Upgrading UniFi Controller on UBNT Cloud Key

To do the upgrade:

  • Log directly into the CloudKey over ssh
  • wget the new firmware
  • dpkg -i new_firmware.deb
  • Hop over to the web portal for any additional steps

Here is what that looks like from the cli:

 ___ ___      .__________.__
|   |   |____ |__\_  ____/__|
|   |   /    \|  ||  __) |  |   (c) 2013-2016
|   |  |   |  \  ||  \   |  |   Ubiquiti Networks, Inc.
|______|___|  /__||__/   |__|

     Welcome to UniFi CloudKey!

root@UniFi-CloudKey:~# cd /tmp
root@UniFi-CloudKey:/tmp# wget
--2017-06-12 20:37:51--
Resolving (,,
Connecting to (||:443... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: [following]
converted '' (ANSI_X3.4-1968) -> '' (UTF-8)
--2017-06-12 20:37:52--
Resolving (
Connecting to (||:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 145135070 (138M) [application/x-debian-package]
Saving to: 'unifi_sysvinit_all.deb'

unifi_sysvinit_all.deb 100%[===========================>] 138.41M   932KB/s   in 2m 47s

2017-06-12 20:40:39 (851 KB/s) - 'unifi_sysvinit_all.deb' saved [145135070/145135070]

root@UniFi-CloudKey:/tmp# dpkg -i unifi_sysvinit_all.deb
(Reading database ... 15695 files and directories currently installed.)
Preparing to unpack unifi_sysvinit_all.deb ...
Unpacking unifi ...
Setting up unifi ...
Processing triggers for systemd ...

Once that wraps up, you can head over to your web portal. If it’s anything like mine, you’ll see a DB migration in process:

Database Migration

After that is done, you can log in.

Wrapping up the upgrade

If you have other UBNT gear on your network, it too may need an upgrade. For this I tend to work from the outside in. In this case that means first upgrading the AP’s, then the switch:

Rolling AP Upgrade

Rolling updates can be found under the APS tab of the UniFi interface:

Find the rolling updates

With that complete, upgrade the rest as needed.

Updated Vagrant Boxes for OpenStack Cookbook 3rd Edition

A reader reached out a day or so ago to point out that the Vagrant images used in the 3rd edition of the OpenStack Cookbook had gone missing.

Why? Well, long story short, I accidentally the images.

That said, I buit new ones, which live on Atlas here.

Changes in this release:

  • Updated to ubuntu 14.04.5
  • Updated NIC drivers
    • Virtualbox: Virtio
    • VMware: vmxnet3
  • Changed from preseed to kickstart

The images are still rather large. However, as we are in the process of writing the 4th edition, I’ll likely leave them that size.

Fixing Sublime (subl) CLI in tmux

This fix is borrowed from here, with some minor adjustments.

brew install reattach-to-user-namespace
echo "set-option -g default-command "reattach-to-user-namespace -l ${SHELL}" \
    | tee -a ~/.byobu/.tmux.conf
byoby kill-server

Then restart your sessions as normal.

Why Windows Update? (re)Build it on the fly.

This sound familiar? You use a *nix based OS as a daily driver. Be that OSX or some flavor of Linux. You’ve got say, 95% of what you need to do covered, but every now and again (like pulling up an old Java app, or running a specific version of IE), you keep a Windows VM around.

Ok you think, this not so bad. Until you find, it’s been forever since you last opened said Windows VM, and now you need updates and such, and then the app updates, and suddenly you have spent more time caring and feeding Windows than doing work.

At least, I find myself here too often. I hated it. So like any good lazy-admin. I fixed it. I no longer keep a Windows VM around all the time. Instead I build it fresh, each and every time I need it, pulling in all the relevant security patches, tools, and so forth. What follows then, is the actual ‘how’, behind it.

But first, the short version, because this will be a huge post:

brew update
brew cask install packer
brew cask install vagrant
brew cask install vmware-fusion
vagrant plugin install vagrant-vmware-fusion
vagrant plugin license vagrant-vmware-fusion ~/Downloads/license.lic
git clone ~/projects/templates
cd ~/projects/templates
make vmware/eval-win7x64-enterprise-ssh.json
vagrant box add \
    -name Win10x64 \

Before You Start

Before you head down this road, you will need a few tools:

  • Packer
  • Vagrant
  • Virtualbox or VMware Fusion/Workstation (examples here are Fusion, but should work either way)
  • The ISO files. That is, if you are doing non-eval versions

Getting Started

We’ll work with the assumption that you have your tools installed and, if needed licensed. The tl;dr of that process for OSX is:

brew update
brew cask install packer
brew cask install vagrant
brew cask install vmware-fusion
brew cask install virtualbox
vagrant plugin install vagrant-vmware-fusion
vagrant plugin license vagrant-vmware-fusion ~/Downloads/license.lic

Once you have the tools, at a high level, the process is:

  • Download (or write) packer definitions
  • Download (or write) post-install scripts
  • Run the things

The Packer Environment

The good folks behind boxcutter have produced quite a robust set of packer environment files which provide an excellent foundation from which to start. They cover most recent windows versions from win7 => current desktop wise and 2008R2 => current server wise. I would advise forking that repo to provide a good jumping off point.

Now, let’s customize one for Windows 10:

git clone ~/projects/templates
cd ~/projects/templates

In here you will want to edit either win10x64-enterprise-ssh.json or eval-win10x64-enterprise-ssh.json respectively.

Note: The difference here is between licensed install media or not. Our example follows the eval version.

Note 2: I’m using ssh over winrm here because that works well for me on OSX. The winrm versions of these files are similar enough, however.

You are looking for this section of the file:

"provisioners": [
    "environment_vars": [
    "execute_command": " cmd /c C:/Windows/Temp/script.bat",
    "remote_path": "/tmp/script.bat",
    "scripts": [
    "type": "shell"
    "inline": [
      "rm -f /tmp/script.bat"
    "type": "shell"

In that section, between the bat files and the inline script, we inject our custom powershell:

"provisioners": [
    "type": "file",
    "source": "script/custom.ps1",
    "destination": "C:/Windows/Temp/custom.ps1"
    "environment_vars": [
    "execute_command": " cmd /c C:/Windows/Temp/script.bat",
    "remote_path": "/tmp/script.bat",
    "scripts": [
    "type": "shell"
    "inline": [
      "rm -f /tmp/script.bat"
    "type": "shell"

The Install Script

As you saw in the prior section, we added three things:

  • A file provisioner to copy in our PowerShell script
  • The script script/custom_launcher.bat
  • The script script/custom.ps1

The file provisioner tells packer to copy our custom PowerShell script into the VM.

The next thing we did, was right before script/clean.bat, we told packer to run custom_launcher.bat. custom_launcher.bat is a helper script that launches PowerShell to run custom.ps1, and is a simple one-liner:

Powershell.exe -executionpolicy Unrestricted -Command "& c:\Windows\Temp\custom.ps1"

Note: We use the helper script approach because the PowerShell provisioner assumes the use of WinRM, which didn’t work out of the box on OSX Sierra. If you are using Windows to run packer, you can use the PowerShell provisioner instead.

Finally, the contents of custom.ps1 are here:

If you’re paying attention, the above script is just a consolidated version of the work from here.

Build the VM

Now that you have all the bits together, it is time to build the VM. From the command line, run the following command:

$ pwd

$ make vmware/eval-win10x64-enterprise-ssh

After a long while and a LOT of output, you will have a shiny Win10 VM vagrant box with Java installed, so you can well, access that one iLO board on that one box that you can’t turn down, because ‘reasons’.

Note: To enable debug logging, you can run the make command like this: PACKER_LOG=1 make vmware/eval-win10x64-enterprise-ssh

Use the VM

I had thought to leave this step as an exercise for the end user. Why? Because, while packer will give you a Vagrant box by default, you can also output this directly into vSphere for use that way. What we’ll do here, for those unfamiliar with Vagrant, is build a minimal Vagrant file and fire up the VM:

vagrant box add \
    -name Win10x64 \

mkdir -p /projects/Win10-x64

cd /projects/Win10-x64

vagrant init -m Win10x64

vagrant up --provider=vmware_fusion

Cleanup & Rebuilding

Ok, so you logged into whatever thing you needed the Win10VM for, did a bunch of work, and now want the free space back:

cd /projects/Win10-x64

vagrant destroy -f

vagrant box remove Win10x64

cd /projects/packer-templates/windows

make clean

To rebuild, rerun the make command, which should pull in all the updates and what not that you need.

On Computerized Note Taking

I have blogged on and off about note taking, their importance, some random techniques and such. After a year or two of trying /LOTS/ of different tools, techniques, and the like, I thought I’d share the current method that has stuck for me.

This post is going to be rather long, so: tl;dr - Rakefile with templates, markdown formatted, auto-saved to an personal gogs server.

This post has four parts:

  • How I got here
  • The types of notes I take
  • My current process
  • The Tools

How I got here

The long and short of this story, is I missed the days of putting .LOG into Windows notepad and having it timestamp on each file open. Couple that with what is formerly Google Desktop search, and you had all of your notes, right there, indexed and with easy access.

This is not a knock on Evernote and other tools of the like. I tried just about every one of them. Along with easy searching, version control, and all the other good stuff, I gain a few extra benefits: easy transition to blog posts (commit to a different repo), easy export as static html or PDF for others consumption (using pandoc).

The Types of Notes

When taking notes on the computer, there are three basic types I take:

  • Case-Notes
  • Call-Notes
  • Draft Posts


These are analogous to project notes and are the most generic form of notes I take. The template for these looks like this:

Customer:    Orangutan Roasters
Project:     Burundi Roast
Author:      Your Name
Date:        2017-04-17
# Orangutan-Roasters

Some background here
# Burundi Roast

Some background here
# Notes:

The information at the top is metadata, which wile searchable, does not get exported to PDF. It then includes two sections to provide background information about the task, customer, project, you name it. This is useful for context setting. Finally, you have a heading for freeform notes. Here I add timestamps as I go, so I have a running log of what I am doing and some context to return to.


Any time my phone rings, I take notes. This way I have a reference point of who I called, when, what about, etc. All the stuff you’d get from a recorded call, but you know, searchable. The template for that looks like this:

Subject:     Orangutan Roasters at Farmers Market
Customer:    Orangutan Roasters
Project:     Cottage Food Sales
Author:      Not Me
Date:        2017-04-17
# Background

The context of the call goes here.

# Call details:
Organiser:   Not Me
Bridge:      1-800-867-5309,,112233#

On the call:

# Unformatted notes:

Start taking notes here

Much like the more generic case-notes, the bits at the top are metadata that do not get exported, but are useful for finding this again later. Additionally, when using rake to create the template file, you can supply the call bridge for easy copy-paste later.

The Tools

There are a few tools used here:

  • Rake / Rakefile - Provides the template used for the different note / blog types
  • Sublime Text - My editor of choice
  • The Git and GitAutoCommit modules for Sublime - Version history
  • pandoc module for Sublime - Easy export to Word, PDF, or HTML


As you will see reflected in the Rakefile that follows, I keep my notes in a bit of a tree structure:

$ tree
├── Rakefile
├── call-notes
└── case-notes

The drafts for blog posts live in their own tree. As long as you specify the path in the Rakefile, you can store your notes anywhere.

The Rakefile

The rakefile I use to create notes currently has three sections, one for each type of note I take most regularly: Blogs (:post), Project (:note), and Call (:call)

To create a new note, from the command line one runs rake notetype parameter="thing". For example, if I wanted to open a new file for a coffee roast I would use a command like this:

rake note customer="Orangutan Roasters" project="Buruni Roast"

Which creates a file named similar to and which contains the following template text:

Customer:    Orangutan Roasters
Project:     Burundi Roast
Author:      Your Name
Date:        2017-04-17
# Orangutan-Roasters

Some background here
# Burundi Roast

Some background here
# Notes:

Now that you have the template file in place, you can open in an editor of your choice, and markdown being super close to plain text, you’re off and going.

Sublime Text

My editor of choice. Yes yes, I know I can do these things in vimacs or whatever and to each their own.

To install Sublime on OSX:

brew install Caskroom/cask/sublime-text3

This in turn links the subl command to /usr/local/bin/subl. This allows you to open notes as follows:

subl case-notes/

Sublime Modules

As discussed above I use the Git, GitAutoCommit and pandoc modules. These can each be installed using their specific instructions. I’ve linked those here:

Once you have those, these minor config tweeks will save you some heartache. For Pandoc, in your user-settings (Click sublime, Preferences, Package Settings, Pandoc, Settings - User), paste the following in:

      "default": {

           "pandoc-path": "/usr/local/bin/pandoc",

        "transformations": {

          "HTML 5": {
            "scope": {
              "text.html.markdown": "markdown"
            "syntax_file": "Packages/HTML/HTML.tmLanguage",
            "pandoc-arguments": [
              "-t", "html",
              "--filter", "/usr/local/bin/pandoc-citeproc",

          "PDF": {
            "scope": {
              "text.html": "html",
              "text.html.markdown": "markdown"
            "pandoc-arguments": [
              "-t", "pdf",


          "Microsoft Word": {
            "scope": {
              "text.html": "html",
              "text.html.markdown": "markdown"
            "pandoc-arguments": [
              "-t", "docx",
              "--filter", "/usr/local/bin/pandoc-citeproc"

        "pandoc-format-file": ["docx", "epub", "pdf", "odt", "html"]



To export using Pandoc, ‘command + shift + p’, then type ‘pandoc’, press enter and select how you want to export it.

The Process

If you are still with me, thanks for sticking around. Now that all the scaffolding is in place, to create a new note, from the root of the notes folder:

$ rake call subject="Orangutan Roasters at Farmers Market" customer="Orangutan Roasters" project="Cottage Food Sales" bridge="1-800-867-5309,,112233#" owner="Not Me"

$ subl call-notes/

And you’re off.


Well, this ended up being much longer than I had expected, however, at the end of it all, you have notes that are versioned and backed up to git, and are searchable with grep or any tool of your choice.