In one project, we have the need to test an application for the different platforms in which it is available. One of them is Windows, and to test the software, we spawn a Virtual Machine in which the application is installed, tested, etc. The backend for the VMs is OpenStack and KVM, so this time I learned…
How to create Windows 10 or Windows 11 VMs to be used in an OpenStack platform (using the KVM hypervisor)
In my case, I am preparing the Windows 10 image using Virtual Box for MacOS. The steps are the same in case you are using VirtualBox for Linux or virt-manager
for Linux.
First, you need to get a Windows 10 ISO. If it is for personal use, you can download an ISO from Microsoft and later activate it by (e.g.) setting your activation key. In my particular case, I am using a volume license that I am activating against my license server.
Once you have the ISO, download the virtio drivers ISO from this page. The direct link for the stable drivers ISO is this one (built by Fedora).
And now we are ready to start…
Create the VM in VirtualBox
We are creating a simple VM in VirtualBox by hitting “New”. In my case, its name is “Windows 10 for OpenStack”, and I’ll add the Windows 10 ISO from which I am installing the OS.
It is important to mark the “Skip Unattended Installation” because we are installing it manually.

In the next step, we set the amount of memory and processors. I am considering adding a minimum of 4Gb and 2 cores for installing the OS. These values may be different for the template in OpenStack.
In the case of Windows 10, it is not mandatory to use EFI, but it is in the case of Windows 11. In my case, I am using EFI for this post (I do not see any advantage as of today).

In the next step, we are skipping the creation of a Virtual Hard disk. This is because we want to create it using a virtio bus instead of the default SATA one.

Once we have the VM created, we’ll go to the Configuration tab, and there we’ll go to the Storage section. We’ll find something like this image:

In the SATA controller section, we’ll add a new Optical Drive that must point to the Virtio Drivers ISO. We’ll ensure that the port for this ISO is greater than the one for the Windows ISO so that it has less priority for boot.
Once we have the two SATA optical drives, we’ll add a virtio-SCSI bus using the menu in the next image:

And now, we’ll add a new hard drive to the VirtIO bus. We need to ensure that the format is qcow and the size is greater than 50 Gb. (more than 52 GB for the case of Windows 11). The size of this disk is only for installation purposes, and we’ll prepare to make it grow when deployed in the OpenStack platform.

At the end of the settings, the storage section should look like in the next image:

Now we’ll move to the network tab, and we need to set the Type of adapter in the Advanced section to virtio-net so that it matches the driver in KVM.

Finally, we are ready to accept the configuration for our Virtual Machine installation.
The steps for the installation in the case of using virt-manager is equivalent. We only need to make sure that both the hard drive and the network are of type VirtIO, as they are used in KVM.
First boot (start installation)
Now that we have the VM configured, we are booting it for the first time, to install Windows 10…
When the screen shows the message “Press any key to boot from CD or DVD“, please press any key… otherwise it will get to the EFI console (if you get to the EFI console, simply reboot the VM).


Once a key is pressed, the Windows installation will start as usual:




Now we’ll go through some installation screens, and we’ll get to the one where we have to select the drive to install Windows. In that screen, we’ll get no drive, and that is because the Windows installation does not have the VirtIO driver. Thus it cannot detect our drive.
So we must select the Load driver option and browse the appropriate path for the VirtIO driver placed in the second optical drive. In my case, the path is E:\amd64\win10
, but it may differ in your case (depending on the specific Windows version or the i386 or x86_64 architecture).




If you added the disk as virtio, now it should appear, and you should be able to select it as the main disk to install Windows and start the installation as usual.


You should proceed with the installation as usual, except for the case of the network: as we added the network card using the virtio driver, Windows cannot detect it for now. It is not a problem because we’ll activate the network later.
For the case of the user for the VM, I suggest using a generic user (in my case, it is Windows), and please do not leave the password blank if you do not want your VMs to start with the opened session. Openstack will change the password during the creation of the instance.
Preparing the VM for OpenStack
Once finished with the installation, you start configuring the network and the other drivers. Please execute the application virtio-win-guest-tools to install the eventually needed virtio drivers (in particular, this will install the network driver, too).






In my case, the next step is to set the server for the licenses (as I am running a volume license Windows).

The next step is to update Windows using the Windows Update procedure… This procedure may need to reboot your system multiple times.

During the installation, Windows creates multiple partitions in the disk. In particular, it creates a recovery partition at the end of the disk that bothers the usual procedure of OpenStack. When it starts a new VM, the actual size of the disk is usually bigger than the size of the original disk image. If we keep the partition at the end of the disk, we won’t be able to make the system partition grow to spawn across the new disk. So we are deleting that partition:

The sequence of commands is (please adequate the number for the disk and the partition for your specific installation):
diskpart select disk 0 list partition select partition 4 delete partition override
Now, if you check the partitions, you’ll see that you only have 3 partitions, where the main one is the last.
We’ll offer remote desktop to access the Windows VMs that we are creating in OpenStack, so we need to activate Remote Desktop:

At this point, you could install any application that you want to ship in your Windows 10 image.
The last step for preparing the VM image is to install the agent that will integrate OpenStack and Windows. It is an agent that enables setting the password for the main user, running the user’s scripts, etc., as OpenStack does with cloud-init in Linux.
First, we need to enable some permissions to enable cloudinit’s tasks. So we need to open cmd as admin and execute the following command: powershell Set-ExecutionPolicy Unrestricted.
This command will enable running scripts during system startup. If not enabled, the scripts will not be run, and the password for the main user will not be set.

Now we’ll need to navigate to cloudbase.it to download the package cloudbase-init. Then download the appropriate package for your architecture, and start the installation.


The basic concept for this package is that it enables running the scripts from OpenStack and setting the password for one specific user. In my case, I set the user “Windows“, which is installing cloudbase-init. Moreover, I will check the “Run Cloudbase-init service as LocalSystem” option.


Finally, we’ll set the option to “Run sysprep…” and “Shutdown when Sysprep teminates“.
Please ensure that Windows is fully updated because if it has downloaded updates but have not been installed, sysprep will fail. If it fails, please uninstall cloudbase-init and after install it again as described above.
If sysprep still fails, you can check the log file c:\Windows\System32\sysprep\Panther\setuperr.log. The most probable error is the installation of some applications or updates in language files. In case you find anything like “Error SYSPRP Package XXX was installed for a user, but not provisioned for all users. This package will not function properly in the sysprep image this is your case.
You can debug the error yourself, but I advise removing the package using this tutorial.
Moving the image to OpenStack
If anything goes as described in this tutorial, VirtualBox should have shut down your VM at the end of the previous step, and you should be ready to transfer the virtual disk to your OpenStack deployment. In my case, the name for the disk is windows10foropenstack.qcow.
My advice is to change the format from qcow to qcow2. This is because OpenStack uses the command file to get the format of the file that contains the disk and then forces qemu-img to use that format instead of leaving qemu-img to deal with the file. That makes that, if it is needed to convert the file at a further time, such conversion may fail because some versions of qemu-img believe that they cannot deal with qcow format (which is different from qcow).

In case you follow my advice and want to change the format, you can do it with the following command: qemu-img convert -O qcow2 -p windows10foropenstack.qcow windows10foropenstack.qcow2

Now you can create the image for OpenStack. In my case, I am using the command line: openstack image create --public --protected --disk-format qcow2 --file windows10foropenstack.qcow2 "Windows 10"

The problem of multiple CPUs in Windows
Windows 10 has a problem that it does not recognize more than 2 CPUs (at least, my version). And this is a problem in case we want to add more than 2 cores to a flavor because OpenStack does not work with cores; when it creates a VM with (e.g.) 8 vCPUs, OpenStack instructs kvm to add 8 CPUs, not (e.g.) 2 CPU and 4 core for each one.
To solve this problem, we need to define the CPU topology. This is done using metadata and can be set for flavors, disk images, or even specific VMs. The web interface has multiple variables under the “Virtual CPU Topology” section: hw_cpu_cores, hw_cpu_sockets, hw_cpu_threads, etc. You can see more information here.

In my case, I am setting hw_cpu_max_sockets to 2 at the image level so that OpenStack will calculate the other values. Moreover, as my disk is 60Gb, I am setting the minimum size for the disk to that value, just to make sure that Windows can run. The command in the CLI would be similar to this one: openstack image set 93f656a8-529d-41a1-9f92-96983e7d47bb --min-disk 60 --property hw_cpu_max_sockets=2
.
Activating UEFI in OpenStack
Please skip this step if you did not install Windows 10 using UEFI. Otherwise, you’ll need to inform OpenStack that the image needs to activate the UEFI boot in KVM; if you don’t, your image will fail.
KVM supports UEFI boot, but you need to install a package in each node that will virtualize UEFI VMs. In the case of ubuntu, the package is ovmf and you can install it using the common tools: apt update && apt install -y ovmf
. Once installed, you can find some files in folder /usr/share/OVMF
, which are the files that OpenStack expects to find to use UEFI.
Now you need to instruct OpenStack to use UEFI with your image, which is done by setting metadata variable hw_firmware_type to uefi. In the CLI, the command is similar to this one: openstack image set 93f656a8-529d-41a1-9f92-96983e7d47bb --property hw_firmware_type=uefi
.
Running the Windows 10 VM
Once everything is ready, you can finally boot a Windows 10 based VM. If you followed the advice in this tutorial, it should be running, and you could get to the console in the horizon web interface.
Windows will ask you for a password if you try to access the VM. The easier way to retrieve it is by using the web interface, using the “Retrieve password” option placed in the server action button, inserting your private key in plain text, and hitting the button “Decrypt password” (this is done in the browser and your private key will not get the network).


You could also retrieve the password using the command line following this tutorial.