In the world of Linux containers where deployment takes on the order of seconds, even the best-case scenario for spinning up a new Windows host can seem like an eternity.
Clearly, you don’t want to wait for the entire Windows install process each time you bring up a Windows guest OS. Even automated, this would take 15+ minutes and all it would deliver is a base, non-patched, non-customized system.
Windows Sysprep allows you to build a base Windows template with any patches, customizations, and files that you want in a base system. And then any subsequent guest OS created with that template will inherit all those template basics.
I wrote this article to give developers a peek into how these templates are created so they can influence the base images that their Operations teams generate.
Prerequisite KVM
For this article I’m assuming you are on a Debian based Ubuntu host with KVM installed as described in my previous article.
Download Windows 2019
You can get an evaluation version of Windows 2019 directly from Microsoft.
https://www.microsoft.com/en-us/evalcenter/evaluate-windows-server-2019
After downloading this 5+ Gb file, you should have a local ISO file named something like: 17763.737.190906-2324.rs5_release_svc_refresh_SERVER_EVAL_x64FRE_en-us_1.iso.win2019
Rename it to “Windows2019.iso” to make it easier to reference in the coming steps.
Create a base Windows2019 instance in KVM
Issue the following command to create a Windows 2019 instance in KVM that is using the full path to the Windows 2019 ISO downloaded.
virt-install --name=win2k19base \ --os-type=windows --os-variant=win2k19 \ --cpu host-passthrough \ --ram 4096 --vcpus=4 \ --virt-type=kvm --hvm \ --cdrom /data/Windows2019.iso \ --network network:default,model=e1000 \ --graphics vnc --video qxl \ --disk pool=default,size=80,sparse=true,bus=sata,format=qcow2 \ --boot cdrom,hd --noautoconsole --force
I am intentionally using SATA for the disk instead of SCSI, because the disk was not being recognized during the installation process without this change.
Windows2019 standard installation
Now you should be able to pull up the Virtual machine manager GUI and see the following Windows install screen.
Installation steps
Follow the below path through the windows installation process.
Language=English, Time=English, Keyboard=US; Next press "Install now" OS=Windows Server 2019 Standard Evaluation (Desktop Experience); Next checkbox to Accept license; Next Custom: Install Windows only (advanced) Select Drive 0 with 80G unallocated space; Next ...Installing Windows... ...Windows needs to restart to continue...
The guest VM will then restart itself so the installation process can continue.
When the guest VM comes back up, you will see Windows preparing itself for first time use.
The first prompt you see is to set the Administrator password. Once done, you will be take to the Windows GUI initial lock screen. Press ctrl-alt-delete to login as Administrator. This can be done by selecting “Send Key” > “Ctrl+ Alt+Delete” from the Virtual machine manager menu.
Customize the Windows install
Now that you have a base Windows install, you need to prepare it with the patches, software, and customizations that you want for all your cloned Windows hosts.
Install latest Windows Updates
The first thing you’ll want to do is pull up “Windows Update” and apply all the latest patches. If you do not, then every Windows host you clone will require patching from the moment it is instantiated.
Install Files and software
Then you will want to place all the common utilities/files/software onto the guest OS. This may be the installation of utilities like 7zip or Notepad++, or Java and a Tomcat server so it is ready for application deployments, or a zip file that contains scripts that will be run post-deployment.
Folders, files, and simple utilities are not an issue, but more complex application with dependent services and complex registry settings can be broken after Sysprep. Also, not all applications do well when a machine name is different than what is noted in the registry/config file or domain status changes.
You have to experiment to determine if an application can be installed before Sysprep, or must be executed post-instantiation.
Run Sysprep
Now that we have a Windows 2019 guest OS that serves as the ideal template for our environment, we need to run Sysprep. This process is customized with a configuration file named “unattend.xml”. You can find full documentation of the settings here.
I’ve created an “unattend.xml” for Server 2019 that you can download from my github. You will need to copy this file to the guest OS at c:\windows\system32\sysprep\unattend.xml.
Open the command prompt as Administrator. Then delete any older files in the ‘sysprep\Panther’ folder and run sysprep as shown below:
# console "Run as Administrator" del /q c:\windows\system32\sysprep\panther\*.* c:\windows\system32\sysprep\sysprep.exe /generalize /oobe /shutdown /unattend:c:\windows\system32\sysprep\unattend.xml
This will prepare the guest OS, and then power down the guest. The next time the system is powered on, it will run through the Sysprep process which creates a unique SID and a fresh system.
KVM backing file
One of the tricks we will use to save a considerable amount of disk space and get faster startup times on our Windows clones is to use the concept of a KVM backing file. In other words, we can use the large disk of the Windows OS we just installed as the single base for any number of new Windows guest VMs.
Instead of forcing a fresh copy of the entire 20Gb+ base disk every time you to spin up a Windows Guest OS, a backing disk allows your instance to reuse the parent disk as a source, and then simply persist any differences.
# eject installation CDROM cdrom=$(virsh domblklist win2k19base --details | grep cdrom | awk '{print $3}') virsh change-media win2k19base $cdrom --eject # get full path to base disk virsh dumpxml win2k19base | grep -Po "<source file=\K.*'" | tr -d "'" # go into directory cd $(dirname $fullpath) # check size of base disk (will be 20Gb+) sudo qemu-img info $(basename $fullpath) --force-share # create new child disk, with original as backing sudo qemu-img create -f qcow2 -F qcow2 -b $(basename $fullpath) win2k19-child1.qcow2 # validate size of new child disk (will be less than 1Mb) sudo qemu-img info win2k19-child1.qcow2 --force-share
The new child disk is a minimal size right now, and will grow during its operational lifetime as it diverges from the original base image.
Create a Windows 2019 clone
Now that you have a new child disk, you can use this to create a new Windows 2019 Guest OS.
virt-install --name=win2k19-child1 \ --os-type=windows --os-variant=win2k19 \ --cpu host-passthrough \ --ram 2048 --vcpus=2 \ --virt-type=kvm --hvm \ --network network=default,model=e1000 \ --graphics vnc --video=qxl \ --disk $(dirname $fullpath)/win2k19-child1.qcow2 \ --boot=hd --noautoconsole --force
The Windows logo will show as the new Guest OS goes through the steps detailed in the Sysprep unattend.xml that you prepared in the earlier section.
As you can see, spinning up a new Windows 2019 instance takes less than 5 minutes, which is significantly faster than needing to do a completely fresh install and then get it to a desirable base state of OS patches, applications, and utilities.
Post Creation steps
Now that you have a fresh guest OS, you still need to perform a set of steps that makes it ready for your environment. At the very least you will want to change the name, activate the Windows licensing, possibly add it to a AD domain, enable services, modify the firewall, enable remoting via winrm/powershell, etc.
Hopefully these are tasks you have scripted in Powershell and batch files, and if so you should consider adding them to the <FirstLogonCommands> in unattend.xml so you do not have to touch the system.
Typically virtualization engines provide some kind of hook for passing values such as the intended hostname, so that the computer hostname, network and other critical values can be passed in to guest OS customization scripts. For example vSphere has the Customization Specification Manager, vCloud Director has guest Customizations.
Performance notes on Linked Clones
Take note that using linked clones can have performance ramifications if you plan on these guest OS instances running continually for months or if they are I/O intensive to the parent c:\ drive. Just like snapshots, the differential image being written builds up and has to be taken into account for future operations [1,2,3]. You can help counter this by adding another virtual drive post-instantiation that serves as the data drive for most of the application I/O operations and also put the Windows pagefile.sys on the data drive.
In production deployments linked clones are not used much since storage deduplication is enabled, replication/DR is easier when you can avoid these dependencies, guest OS typically have long lives and so fully independent disks will be more performant, and the initial spin up time is not as significant as these previous factors.
BUT for development environments where quick spinup time leads to faster development cycles and hosts are tore down continually, linked clones have high utility.
REFERENCES
c-nergy.be, images of each step in Windows 2019 installation
Windows 2019 Server evaluation
windowsafg.com, auto answer file generator for Windows 2019
github StefanScherer, unattended.xml files for Packer Windows
github pento, unattended.xml files for Packer
fabianlee.org, KVM backing file and linked clones
infologs, KVM base images and linked clones
NOTES
KVM Snapshot taken before running Sysprep, so multiple iterations of using unattend.xml can be made.
# create snapshot while powered down virsh shutdown|destroy win2k19base virsh snapshot-list win2k19base virsh snapshot-create-as win2k19base -name beforesysprep # revert to snapshot, then power back on virsh snapshot-revert win2k19base --snapshotname beforesysprep virsh start win2k19base
Windows license expiration and renewal
This article uses a trial ISO for Windows 2019. If the licensing watermark as shown in the bottom right of the screen is expired, make sure to run “slmgr.vbs /rearm” and reboot to give it another 180 grace period or your machine will shutdown after about 30 minutes.