1 Comment

Using Vagrant to Deploy to a Remote Hyper-V Host

As a general rule, I try to minimize the number of programs installed and configured on the host OS of my laptop. I use the host mostly for email, office, and web browsing. It’s much faster to create isolated development environments inside of virtual machines. If a project requires some third-party applications or modifying system-wide settings, I don’t need to worry about it corrupting my host OS or affecting other projects.

Since my host OS is Windows, I’m using Hyper-V for managing vms. Vagrant is the tool my current team is using to automate provisioning our development environment. It integrates with Chef to install and configure all of the necessary packages and dependencies in a new vm.

Overall, the Vagrant/Hyper-V combo has worked well, but I had two annoyances that I was interested in mitigating:

  1. Hyper-V and Wifi frequently stop working, causing me to lose internet access on my host, guest, or both.
  2. Vagrant/Chef are Ruby-based tools, and they have a relatively large footprint on a Windows OS.

Internet Access

I’ll start with #1 since it’s simpler to address. First, I tried creating an external virtual Hyper-V switch bound to my wifi adapter, but this usually results in neither my host nor vm having internet connectivity. For a while I used an internal switch and ICS to provide access within my vms, but I frequently lost connectivity and needed to reset my wifi adapter or reboot my computer to get things back in working order.

A few months ago, I tried installing the VMware network adapter as recommended by this blog post, and everything has worked flawlessly.

Large Footprint

Problem #2 was more difficult to mitigate. Vagrant and the ChefDK both want to install to the root of the C drive and be included in the system path. They sometimes leak network shares, and they require about 2GB of disk space. There’s also a bug in the current 1.7.2 Vagrant release that prevents it from handling memory and cpu settings for Hyper-V machines.

This led me to see if I could install Vagrant and chef within a vm, but still have it deploy the provisioned machines to the Hyper-V manager on my host OS. I took a few shortcuts, but the edits I needed to make this work are here on github. Here’s the high-level approach:

  1. Enable powershell remoting on the host machine.
    • I created a new local user account on the host that was a member of the Hyper-V and Remote Management groups.
    • Set up the host firewall to only allow remote powershell connections from the guest vm with Vagrant installed.
  2. Add a remote config section to the Vagrantfile that includes information to connect to the host.
  3. Modify the Vagrant action scripts to run powershell commands on the remote host instead of the local machine.

There’s still some pain points that I bypassed since I’m new to ruby/powershell and didn’t need the functionality in my specific environment:

  1. Retain ability to target a local Hyper-V host. I just converted the existing Vagrant Hyper-V provider to only target a remote host.
  2. Store the remote powershell credentials securely, and only prompt for them once during the provisioning process.
  3. Generalize the remoting code if possible to not repeat it in each powershell script file.
  4. Efficiently copy the base vhdx image to the right host directory. With an SSD and virtual network connection, I’d like throughput to be ~100s MB/s instead of only ~10s MB/s.

It’s great that Vagrant has a flexible plugin system allowing providers to target various remote hosts. The same approach should also work for extending the other Vagrant providers to target a remote host via either powershell remoting or ssh.