Skip to content

Repository files navigation

kitchen-hyperv

Gem Version

A Test Kitchen driver for Microsoft Hyper-V. It creates test instances as differencing-disk virtual machines from a parent VHD, which makes them fast to create and cheap on disk.

This documentation uses Cinc Workstation and the cinc commands throughout. Everything here works identically with Chef Workstation — see Using with Chef.

Contents

Requirements

  • Windows with the Hyper-V role enabled, either locally or on a remote server
  • An administrative shell, since Hyper-V cmdlets require elevation
  • Ruby 3.1 or later (already satisfied if you use Cinc Workstation)
  • A parent VHD or VHDX to create test instances from

Enable Hyper-V if it is not already present, then reboot:

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

Installation

This driver ships as part of Cinc Workstation. If you have Cinc Workstation installed, there is nothing else to install.

To install it into a standalone Ruby:

gem install kitchen-hyperv

Or with Bundler, add it to your Gemfile:

gem "kitchen-hyperv"

...then run bundle install.

Quick Start

This driver needs a parent VHD to clone from. Point parent_vhd_folder at the directory holding it and parent_vhd_name at the file:

---
driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: ubuntu-2204.vhdx
  memory_startup_bytes: 2147483648
  processor_count: 2

provisioner:
  name: cinc_infra

verifier:
  name: cinc_auditor

platforms:
  - name: ubuntu-22.04

suites:
  - name: default
    run_list:
      - recipe[my_cookbook::default]

Then run the full test cycle from an elevated shell:

cinc kitchen test

Or step through it:

cinc kitchen create    # clone the parent VHD and start the VM
cinc kitchen converge  # apply your cookbook
cinc kitchen verify    # run your tests
cinc kitchen destroy   # stop and delete the VM and its differencing disk

How instances are created

The driver never modifies your parent VHD. For each instance it creates a differencing disk backed by parent_vhd_name, attaches it to a new VM, and deletes that disk on destroy. This means a single parent image can back many concurrent test instances.

Configuration

All options below are set under the driver: key in kitchen.yml.

Required

Option Default Description
parent_vhd_folder none Directory containing the parent VHD/VHDX. Required.
parent_vhd_name none File name of the parent VHD/VHDX, including the extension. Required.

Virtual machine

Option Default Description
vm_generation 1 Hyper-V VM generation. Use 2 for UEFI guests.
processor_count 2 Number of virtual processors.
vm_note unset Text set as the VM's Notes field in Hyper-V Manager.
disable_secureboot false Disable Secure Boot. Usually required for generation 2 Linux guests.
enable_guest_services unset Enable the Hyper-V guest services integration component, required for copy_vm_files.
disk_type extension of parent_vhd_name Disk format for created disks, .vhd or .vhdx. Defaults to matching the parent.
resize_vhd unset Size in bytes to grow the instance's differencing disk to after creation.
additional_disks unset Array of extra data disks to create and attach. See Additional disks.

Memory

Option Default Description
memory_startup_bytes 536870912 (512 MB) Memory assigned at boot, in bytes.
dynamic_memory false Enable Hyper-V dynamic memory.
dynamic_memory_min_bytes 536870912 (512 MB) Minimum memory when dynamic_memory is enabled, in bytes.
dynamic_memory_max_bytes 2147483648 (2 GB) Maximum memory when dynamic_memory is enabled, in bytes.

Networking

Option Default Description
vm_switch default switch Name of the Hyper-V virtual switch to attach to. Falls back to the host's default switch.
vm_vlan_id unset VLAN ID to tag the VM's network adapter with.
static_mac_address unset Static MAC address for the network adapter. Hyper-V assigns a dynamic one if unset.
ip_address from DHCP Static IP address to assign to the guest.
subnet "255.255.255.0" Subnet mask used with ip_address.
gateway unset Default gateway used with ip_address.
dns_servers unset Array of DNS servers used with ip_address.

Media

Option Default Description
iso_path unset Path to an ISO to mount as a DVD drive on the instance.
boot_iso_path unset Path to an ISO to attach and boot from.

File copy

Option Default Description
copy_vm_files unset Array of {source, dest} hashes copied into the guest after it becomes reachable. Requires enable_guest_services.

Remote Hyper-V host

By default the driver drives the local Hyper-V host. Set hyperv_server to run against a remote one over PowerShell remoting.

Option Default Description
hyperv_server nil Hostname of a remote Hyper-V server. Uses the local host if unset.
hyperv_username nil Username for the remote server.
hyperv_password nil Password for the remote server.
hyperv_ssl false Connect over HTTPS rather than HTTP.
hyperv_insecure true Skip certificate validation when hyperv_ssl is enabled.
remote_vm_path C:\Users\Public\Documents\Hyper-V Path on the remote server where VM files are stored.

On hyperv_insecure. It defaults to true, which means the driver does not verify the Hyper-V server's TLS certificate when hyperv_ssl is on. That default exists because Hyper-V hosts usually present the self-signed certificate WinRM generates for itself. It also means the connection can be intercepted, so credentials and everything the driver sends are only as private as the network between you and the host. If your host has a certificate from a CA the client trusts, set hyperv_insecure: false. On a trusted lab network the default is normally fine; over anything shared or routed, it is not.

Debugging

Option Default Description
dry_run false Echo the generated PowerShell instead of running it. Useful for debugging the driver.

The driver also implements the standard Test Kitchen diagnostics:

kitchen list --probe   # asks Hyper-V whether each instance's VM still exists
kitchen doctor         # checks for a missing Hyper-V module or parent VHD
kitchen diagnose --all # shows every resolved driver option

kitchen list --probe is read-only: it reports a stopped instance as stopped rather than starting it.

Examples

Generation 2 Linux guest

Generation 2 VMs boot via UEFI, and most Linux images need Secure Boot turned off.

driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: ubuntu-2204.vhdx
  vm_generation: 2
  disable_secureboot: true
  memory_startup_bytes: 2147483648

Dynamic memory

driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: windows-2022.vhdx
  dynamic_memory: true
  dynamic_memory_min_bytes: 1073741824
  dynamic_memory_max_bytes: 4294967296

Static networking on a specific switch

driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: ubuntu-2204.vhdx
  vm_switch: External
  vm_vlan_id: 100
  ip_address: 192.168.1.50
  subnet: 255.255.255.0
  gateway: 192.168.1.1
  dns_servers:
    - 192.168.1.1
    - 8.8.8.8

Additional disks

Each entry requires a name. size_gb defaults to 5, and type defaults to the driver's disk_type. Creation fails if a disk file of that name already exists, so pick names unique to the suite.

driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: ubuntu-2204.vhdx
  additional_disks:
    - name: data
      size_gb: 20
    - name: logs
      size_gb: 10
      type: .vhdx

Copying files into the guest

driver:
  name: hyperv
  parent_vhd_folder: C:\hyper-v\base-images
  parent_vhd_name: windows-2022.vhdx
  enable_guest_services: true
  copy_vm_files:
    - source: C:\licenses\product.lic
      dest: C:\ProgramData\product.lic

Remote Hyper-V server

driver:
  name: hyperv
  hyperv_server: hyperv01.example.com
  hyperv_username: Administrator
  hyperv_password: <%= ENV['HYPERV_PASSWORD'] %>
  hyperv_ssl: true
  hyperv_insecure: false
  remote_vm_path: D:\Hyper-V
  parent_vhd_folder: D:\base-images
  parent_vhd_name: ubuntu-2204.vhdx

Using with Chef

This driver is not tied to Cinc. The examples above use Cinc Workstation and the cinc_infra provisioner, but the driver works exactly the same with Chef Workstation — run kitchen instead of cinc kitchen, and use chef_infra instead of cinc_infra:

provisioner:
  name: chef_infra

verifier:
  name: inspec

No driver configuration changes are needed.

Troubleshooting

First: see what the driver is actually doing

Three commands answer most questions before you start reading error messages:

kitchen doctor default-ubuntu-2204   # is Hyper-V reachable and the parent VHD present?
kitchen diagnose --all               # every driver option as the driver resolved it
kitchen create -l debug              # the generated PowerShell, and the host's replies

Setting dry_run: true on the driver makes it echo each script instead of running it, which is the quickest way to see the exact PowerShell a configuration produces.

The term 'Get-VM' is not recognized or Access denied

The Hyper-V PowerShell module is missing, or your shell is not elevated. Both are prerequisites — the Hyper-V cmdlets refuse to run for a non-administrator.

Get-Module -ListAvailable -Name Hyper-V      # is the module installed?
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

Then reboot and reopen your terminal as Administrator. kitchen doctor reports both conditions.

Failed to find a default VM Switch.

The driver asked the host for a switch and got nothing back. Either no virtual switch exists, or the name in vm_switch does not match one. List what the host has:

Get-VMSwitch | Select-Object Name, SwitchType

Set vm_switch to one of those names exactly — the match is on the name, not the switch type. On a fresh Hyper-V install with no switches at all, create one in Hyper-V Manager (Virtual Switch Manager) or with New-VMSwitch first.

Missing parent_vhd_folder or Missing parent_vhd_name

The driver checks for the parent disk on the local filesystem before it creates anything. Confirm the folder and the file name, including the extension, resolve on the machine running Test Kitchen:

Test-Path 'C:\hyper-v\base-images\ubuntu-2204.vhdx'

Note that parent_vhd_name is the file name only — the directory belongs in parent_vhd_folder. When hyperv_server is set, these paths refer to the remote host's filesystem and the local check is skipped, so a typo there surfaces later as a Hyper-V error instead.

The create hangs, then times out waiting for an IP address

The VM booted but never reported an address. Common causes, in the order worth checking:

  • The switch has no DHCP behind it. An internal or private switch gives out no addresses unless you run a DHCP server on it. Use an external switch, or configure a static address with ip_address, subnet and gateway.
  • A generation 2 Linux guest failed to boot because Secure Boot rejected its bootloader. Set disable_secureboot: true.
  • The guest's integration services are missing or disabled, so Hyper-V cannot read the address out of the guest. Connect to the VM in Hyper-V Manager and check that it reached a login prompt.

Watching the VM console in Hyper-V Manager while kitchen create runs distinguishes "never booted" from "booted but has no address" in a few seconds.

A generation 2 VM will not boot

Generation 2 VMs boot via UEFI with Secure Boot on, and Hyper-V's default Secure Boot template only trusts Microsoft's bootloaders. Most Linux images need:

driver:
  vm_generation: 2
  disable_secureboot: true

Generation 2 also has no IDE and no DVD drive by default; the driver adds one when you set iso_path or boot_iso_path.

Additional disk file already exists

The driver refuses to overwrite a disk file. This normally means a previous run was interrupted before kitchen destroy could clean up. Delete the leftover .vhdx under .kitchen/<instance-name>/, or give the disk a name unique to the suite.

kitchen destroy says the instance does not exist, but the VM is still there

The instance's state file lost the VM id — usually because .kitchen/ was deleted, or the VM was created under a different kitchen.yml. Test Kitchen tracks VMs by id, so it can no longer find it. Remove it by hand:

Get-VM | Where-Object Name -like 'default-*' | Stop-VM -Force -TurnOff -PassThru | Remove-VM -Force

Then delete the leftover differencing disks under .kitchen/.

Errors connecting to a remote hyperv_server

The driver reaches a remote host over WinRM, so the host has to be configured for it and to trust you:

Enable-PSRemoting -Force                      # on the Hyper-V host
Test-WSMan -ComputerName hyperv01.example.com # from the machine running Test Kitchen

If the connection is refused over HTTPS, check that hyperv_ssl: true matches a WinRM HTTPS listener on the host. If it fails certificate validation, either install a certificate the client trusts and keep hyperv_insecure: false, or accept the self-signed certificate with the default hyperv_insecure: true — see the note under Remote Hyper-V host for what that costs you.

An ISO or file copy does nothing

copy_vm_files needs the Hyper-V guest service interface, which is off by default. Set enable_guest_services: true as well. The copy also happens only after the guest is reachable, so it will not run if the create never gets that far.

Contributing

Bug reports and pull requests are welcome on GitHub. See CONTRIBUTING.md for development setup, how to run the tests, and the release process.

License

Licensed under the Apache License, Version 2.0. See LICENSE for details.

About

Hyper-V Driver for Test-Kitchen

Topics

Resources

Code of conduct

Contributing

Stars

26 stars

Watchers

18 watching

Forks

Releases

Packages

Used by

Contributors

Languages