I’ve been working with Kitchen to test Chef cookbooks for Windows Server again recently and needed a new base VHD image to test against. This time however I decided to use Hyper-V directly on a Windows 10 host instead of Vagrant as the Kitchen driver. My assumption was the Hyper-V driver has gotten better over the last few months and it negates the need for both Vagrant and VirtualBox, thus reducing the number technologies involved with testing and potential failures.
Prerequisites
The basic required installs include the following:
- Hyper-V on Windows 10
- ChefDK
- Additional Ruby Gems
- winrm
- kitchen-hyperv
- kitchen-winrm
Here are the versions that I’m working with
Microsoft Windows [Version 10.0.14393]
Chef Development Kit Version: 1.2.22
chef-client version: 12.18.31
delivery version: master (0b746cafed65a9ea1a79de3cc546e7922de9187c)
berks version: 2017-03-05T07:27:13.998968 13076] 2017-03-05T07:27:13.998968 13076] 2017-03-05T07:27:13.999451 13076] 2017-03-05T07:27:13.999451 13076] 2017-03-05T07:27:14.078969 13076] 2017-03-05T07:27:14.078969 13076] 5.6.0
kitchen version: 1.15.0*** LOCAL GEMS ***
kitchen-dokken (2.1.2, 1.1.0)
kitchen-ec2 (1.3.2, 1.2.0)
kitchen-hyperv (0.3.0)
kitchen-inspec (0.17.0)
kitchen-pester (0.7.1)
kitchen-vagrant (1.0.2, 1.0.0)
test-kitchen (1.15.0)
winrm (2.1.2)
winrm-elevated (1.1.0)
winrm-fs (1.0.1)
Find a Base VHD
Kitchen takes a base image and extends it in a temporary image for testing. No changes will be made to the base image by Kitchen. You can create the base VHD from scratch but that can take a lot of time and effort. Instead I found two sources for VHDs that can help you get started faster:
Microsoft VHDs
Microsoft provides VHDs of select Windows operating systems for evaluation or testing purposes. Here a few:
- https://www.microsoft.com/en-us/download/details.aspx?id=11093
- https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2016
- https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/
Vagrant Boxes
In the past I’ve use Vagrant as the driver for Kitchen. Vagrant provides a registry of publicly available Vagrant boxes in many different flavors including Hyper-V. You can download these boxes and grab the VHD inside to use as your base VHD.
https://atlas.hashicorp.com/boxes/search?provider=hyperv&vagrantcloud=1
In my recent testing I used the VHD from the following Vagrant box:
https://atlas.hashicorp.com/dwickern/boxes/win-2008r2-datacenter
Hyper-V Integration components
Depending on where you get your VHD from it may or may not already have the Hyper-V Integration Components. These components enable more features and tighter integration when running the VHD via Hyper-V. Some of these features are required for Kitchen to work with Hyper-V properly. The following link is the latest set of ICs for VHDs hosted in Windows 10/2016:
The ICs are installed via a patch (.cab file). Use the pkgmgr command to install the package:
pkgmgr /ip /m:<path><file name>.cab /quiet
I’ve installed this several times and a reboot is not always required. On some VMs I don’t actually get any feedback from the pkgmgr call that anything happened but they installed correctly.
VHD Setup
Assuming you have a VHD to start with, these are the basic steps to prepare a VHD to base your Kitchen tests off of:
1 – Create a base Hyper-V virtual machine
You will need to manually create a VM in Hyper-V to setup your base VHD for Kitchen. Choose your VHD from Microsoft or Vagrant as the existing virtual hard disk for your VM:
2 – Setup appropriate networking
In most cases I find I need an internet connection for testing Chef cookbooks so I almost always use an external connection.
If using an internal network you will need to specify the IP address on the virtual Ethernet adapter associated with the virtual switch to ensure you can connect to it. UPDATE – See Steve’s comment at the end of the post about setting this via Kitchen.
3 – Open the VM
Next you will need to open the VM and connect to it verify there aren’t any pending user prompts that need to be addressed or pending reboots required. You may also need to install updates or service patches to bring the OS to the state you wish to test against.
4 – Configure WinRM
Next we need to configure WinRM so Kitchen can communicate with the VM by calling the “winrm quickconfig” command from PowerShell window:
5 – Close the VM
Lastly shutdown the VM from within the OS so the image is in a good state for Kitchen.
And that’s it. You should now be able to reference your VHD from within your Kitchen.yml file and start running tests.
driver: name: hyperv parent_vhd_folder: D:\VirtualMachines parent_vhd_name: packer-virtualbox-iso-1458688988.vhd vm_switch: DefaultExternalSwitch # ip_address: 1.1.1.10 username: vagrant password: vagrant memory_startup_bytes: 1073741824 transport: name: winrm username: vagrant password: vagrant elevated: true elevated_username: vagrant elevated_password: vagrant
If Kitchen create or converge fails you may want to remote into the VM and to troubleshoot the error. If you find issues, you will probably want to destroy the VM created by Kitchen and make the changes to the base instead. This way all future Kitchen runs will have the fix.
Outstanding Networking Issues
I’ve noticed the VM created by Kitchen receives a 169.254.*.* IP from time to time for various reasons. The 169 IP is a default IP configuration when no other IP address is set or can be obtained from the network adapter/switch. This causes Kitchen Create to fail because Kitchen cannot connect to the VM with WinRM.
The first cause I found was when the vm_switch attribute in your kitchen.yml differs from the network adapter used in your base image. After some digging I noticed the VM has a pending reboot request when I connected to the VM manually. My guess here is a network adapter change requires a reboot of the VM. After the reboot the proper IP is set and everything works fine. I workaround this by setting the network switch on my base VM to match the switch I will use in Kitchen.
The next cause is when you are using an external network switch but the external network is not connected on the host machine. I saw this several times when I had selected my wireless network adapter as the connection for an external switch. At the time I was docked and my host machine was using the hardwired network connection and was not connected to the wireless network. The simple fix was to connect to a wireless network.
The last and most frustrating cause seems to be an issue in the latest Windows 10 updates which sporadically causes virtual switch to become corrupted and won’t provide IPs to VMs after reboot. When this occurs you must remove the switch from the VM, delete the virtual switch, recreate it and reset on the VM. More details about this issue can be found here –
Feedback
Please let me know if you found this blog post helpful or you have further questions in the comments below. You can also share the post on Twitter or other preferred social media outlet.
Happy Testing!
Hey Robb,
Great post. My one comment would be that on an internal switch, you can use the networking settings for the kitchen-hyperv driver to set the guest network config (as long as the integration components support it). This is really handy if you make the internal switch a NAT (sort of like what Docker for Windows does).
Steve
Thanks Steve!
Regarding issues with the 169.. addresses and resetting the switch. Here is a script I use to unshare ICS, uninstall and reinstall the switch and then reshare ICS:
function Disable-NicICS {
$cfg = New-Object -ComObject HNetCfg.HNetShare.1
$all = $cfg.EnumEveryConnection
foreach($conn in $all){
$shareCfg=$cfg.INetSharingConfigurationForINetConnection($conn)
$props=$cfg.NetConnectionProps($conn)
$props
$shareCfg.DisableSharing()
}
}
function Enable-NicICS($SwitchName) {
$cfg = New-Object -ComObject HNetCfg.HNetShare.1
$conn = $cfg.EnumEveryConnection | Where-Object { $cfg.NetConnectionProps($_).Name -eq “Wi-Fi” }
$shareCfg=$cfg.INetSharingConfigurationForINetConnection($conn)
$shareCfg.EnableSharing(0)
$conn = $cfg.EnumEveryConnection | Where-Object { $cfg.NetConnectionProps($_).Name -eq “vEthernet ($SwitchName)” }
$shareCfg=$cfg.INetSharingConfigurationForINetConnection($conn)
$shareCfg.EnableSharing(1)
}
function Reset-VMSwitch($Name) {
Disable-NicICS
Remove-VMSwitch -Name $Name -Force
New-VMSwitch -Name $Name -SwitchType Internal
Enable-NicICS $Name
}
Awesome, thanks for sharing that Matt!