We need to test our software against various versions of Windows: 7, 10, 10 LTSB (“IoT Enterprise”). Thus far we’ve used manually configured physical machines exposed via Jenkins node/agent label. This was easy to get working initially but has been problematic:
- Scalability: static set of machines that can be a bottleneck before releases
- Fail-fast/Debuggable: have to wait for PR to percolate through build pipeline to find out something doesn’t work and then take the node offline to debug
- Reproducability: manual configuration is error-prone, people make changes to the environment, etc.
Vagrant’s documentation for Windows guest VMs is pretty good.
We’re in the process of evaluating Windows 10 Enterprise LTSC (the OS formerly known as Windows 10 “IoT Enterprise”/LTSB) which is comparable to Windows 10 Enterprise version 1809/RS5. We’ve installed it to a Virtual Box VM named
Vagrant uses Windows Remote Management to manage Windows VMs.
Inside the VM, make sure the current network connection is “Private” otherwise the WinRM configuration will fail:
Get-NetConnectionProfile # Get "Name" value from output Set-NetConnectionProfile -Name "NAME FROM ABOVE" -NetworkCategory Private
Problems with the WinRM configuration will manifest later as commands like
vagrant resume hanging with:
default: WinRM transport: negotiate
In our case, we’re on a domain and the connection will eventually rename itself. Once that happens we needed to set the category to “Private” again.
Enable Remote Desktop for
vagrant rdp to work.
In order for
vagrant ssh to work, must install OpenSSH server:
- Windows 10 RS3/1709 and later, it’s an optional component
- For ealier versions including Windows 10 LTSB 2016 (RS1), https://github.com/PowerShell/Win32-OpenSSH/releases
In either case, you also need to:
Add the vagrant public key to
~/.ssh/authorized_keys(see this SO)
- Have sshd start automatically:
sc config sshd start= auto
For this exercise we’re going to manually install software. Once we’ve fashioned an environment to our liking, we want to preserve it for later re-use as development environments, build nodes, etc.
Arguably the most common Windows component is Visual Studio which has a bevy of install choices:
# Save the base image somewhere vagrant package --base win10_ltsc_2019 --output /shared_folder/win10_ltsc_2019.box # Elsewhere... # Remove the box if it already exists vagrant box remove win10_ltsc_2019 # Add the saved base image to list of available "boxes" vagrant box add --name win10_ltsc_2019 /shared_folder/win10_ltsc_2019.box
Keep in mind none of this is fast as it involves 5-15GB VM images (depending on the version of Windows and how much you install). This should be used for relatively “static” configuration; I wouldn’t want to mess with it every day or even every week. For things that change frequently (like your software) there’s “shared folders”.
With our VM image ready, we need a
Vagrantfile to get things going:
Vagrant.configure("2") do |config| config.vm.box = "win10_ltsc_2019" config.vm.guest = :windows config.vm.communicator = "winrm" # 3389 RDP config.vm.network "forwarded_port", guest: 3389, host: 3389 end
==> default: Forwarding ports... default: 3389 (guest) => 3389 (host) (adapter 1) default: 5985 (guest) => 55985 (host) (adapter 1) default: 5986 (guest) => 55986 (host) (adapter 1) default: 22 (guest) => 2222 (host) (adapter 1) ==> default: Mounting shared folders... default: /vagrant => /Users/XXX/nng.NETCore
Note the last line. The project directory on the host (
/Users/XXX/nng.NETCore) is accessible to the VM as a “shared folder”.
3389 is RDP so
vagrant rdp works. I added a (very belated) answer to Stack Overflow about this:
$ vagrant rdp ==> default: Detecting RDP info... default: Address: 127.0.0.1:3389 default: Username: vagrant
This gives us a good start that accomplishes a few of our goals:
- On-demand Windows environments
- Reproducible at both developer’s workstations and for building/testing
But we still need a few improvements before we can fully migrate:
- General automation
- A previous project had PowerShell scripts for installing/running our software that we should either track down or re-write
- Integration into Jenkins pipeline
- Truth be told, the environment is fairly static, so maintaining read-only VM images should suffice for now
- This repo has neat scripts and Vagrantfiles for working with Windows and Docker