Translations: "Spanish" |

Using Windows Disposable VMs for test and research

Share on:



To continue the idea of my previous posts, I want to share a project I have been improving in the last months; I can't imagine the number of times I had to deploy a Windows VM while testing or researching. And something I identified as challenges when doing that are:

  • Baseline configuration
  • Portability between private and public clouds.
  • Licensing

Baseline Configuration

And this is the initial state I would like to have for a fresh VM instance of Windows, no matter the version or edition. After a clean installation, we usually install some essential tools, like Chrome or Firefox as a web browser (Sorry IE/Edge), install 7zip and Notepad++, and then make necessary configuration changes to the environment.


In today's world, we have to deal with different environments like private or public clouds. So I want to have images ready to be used in any environment. More importantly, I would like to embrace the idea of Infrastructure as Code (IaC) and configuration management instead of depending on multiple images or snapshots.


These days are pretty usable and comfortable to work with evaluation licenses for Windows, which activate after the first boot (as soon it has an internet connection). Depending on the edition, we have from 90 to 180 days of evaluation with all the features and updates available. After the evaluation time finish, the main issue is that you have the ugly message of "Windows License is expired," and with the behavior that it powers off from time to time.

To overcome this, I propose to use configuration management solutions like Ansible to take care of the configuration state of the environment we want to achieve. Another benefit of using the evaluation license is to have a clear conscience and not use pirated serial numbers or any other "activation cracking method." The only critical remark is that to comply with the EULA (End User License Agreement), this instance is not mean to be used in production environments; that's why I always mention test/research.

These three definitions made me think about the use of disposable Windows instances as a cool way to handle the deployment of research environments.

The manual way

The traditional way to achieve this is to install each version of Windows we want to have as a "golden image." With the fresh install, run the following tasks:

  1. Install hypervisor tools (if we are using VMware, for example, and special drivers like vmxnet3 are needed to have network connectivity, we need first to install VMware tools)
  2. Enable WinRM for remote execution of configuration tasks (I plan to use Ansible for this).
  3. Disable password expiration
  4. Disable UAC
  5. Disable Hibernate
  6. Allow RDP in the Windows Firewall
  7. Disable Windows Telemetry (big brother?)
  8. Disable Windows Updates

A pause here, before proceeding to install extra applications, I use Chocolatey as packet manager (just like APT or YUM in Linux, but for Windows)

  1. Install Chocolatey
  2. Installing this using chocolatey: Chrome, 7Zip, Notepad++, Process Explorer

Before packing the virtual machine into a template, I like to clean up and run some space usage optimization tasks to reduce the file size of the exported image's. Some of those tasks are:

  1. Remove cache files from updates.
  2. Cleaning SxS, more info about this here
  3. Remove temporary files.
  4. Defragment the disk.
  5. Zero-fill unused space on a hard drive; this trick makes the exported image smaller, the average size of the resulting image is around 5 GB, and without this I seen images around 9 GB of size.

And the final step is to execute the Sysprep tool. You may already hear about that but just in case, as a general reminder:

"Sysprep (System Preparation) prepares a Windows installation (Windows client and Windows Server) for imaging, allowing you to capture a customized installation. Sysprep removes PC-specific information from a Windows installation, "generalizing" the installation so it can be installed on different PCs."...


Sounds easy right?, you need to manually install Windows in a VM, run some personalizations, install some tools, clean up some space, and then use Sysprep. For each version of Windows you need.

The cool way (DevOps Style)

Introducing Packer

I would like to quote from their website:

..."Packer automates the creation of any type of machine image."...

..."Packer images allow you to launch completely provisioned and configured machines in seconds, rather than several minutes or hours. This benefits not only production, but development as well, since development virtual machines can also be launched in seconds, without waiting for a typically much longer provisioning time." ...

More details here:

The intention of this post is not a tutorial of how to learn to use Packer, for this there are several resources like the official tutorial: among others in Internet.

Now I would like to focus on what the definitions I made and how to use it.

I published the code of this project on GitHub:

This is a Packer project to generate Windows Evaluation images to use on test/dev/hack/IR_labs and the idea is to have these images as a target for configuration manager solutions like Ansible to configure, for this I have another project I been working that I plan to share after this post.

The current coverage for hypervisors/clouds is the following:act

Hypervisor / CloudStatus
VMWare vSphereCompleted
Amazon Web ServiceCompleted
Google Cloud PlatformCompleted

Just as a reminder, the QEMU image is compatible with environments like GNS3 or EVE-NG as both uses QEMU under the hood.

And the coverage for Windows versions is the following:

Windows 10 Enterprise1803Completed
Windows 10 Enterprise1903Completed
Windows 10 Enterprise1909Completed
Windows 2016 Datacenter14393Completed
Windows 2019 Datacenter17763Completed

The code has been tested on this environment:

  • OS: Ubuntu 18.04.5 LTS
  • Packer: 1.6.2
  • GOVC: 0.23.0
  • GSUtil: 4.53
  • VMWare vSphere: 7.0.1
  • QEMU: 2.11

Note: when building on VMWare vSphere, packer expects that the VM can receive an IP via DHCP.

Template structure

To avoid creating a definition for each edition and build, I used a nested variables approach, which is like a modular way to work with Packer. A base template includes two builders, one for VMware and the other for QEMU; it also consists of a standard set of provisioners and a set of post-processors to convert the resulting image into a format compatible with the adobe specified hypervisors/clouds. I'm trying to track the sources I used to build this, but there are just too many, and also, some of them were obsolete, and I had to make significant changes.

The main template file is:


To make the template work, it needs a set of variables, the first ones we need to pass is what ISO file is going to be used to build the windows instance. The files that includes this information are the following:


The idea is to make this template future-proof, and the structure of the variables can be adapted to cover another version of windows:

2    "name": "windows-10-1803",
3    "iso_url": "",
4    "iso_checksum_type": "sha1",
5    "iso_checksum": "a4ea45ec1282e85fc84af49acf7a8d649c31ac5c"

Just replace the content of the variables as needed.

Now, depending on the hypervisor to use there are a set of common variables included in the following files:


Again, this can be adapted to match other resources to allocate CPU, RAM, and disk sizes, and to specify the content of the floppy drive to use during the unattended installation.

2"disk_size": "81920",
3"memory": "8192",
4"cpus": "8",
5"floppy_files_list" : "floppy/drivers/virtio/*,floppy/ConfigureRemotingForAnsible.ps1,autounattend/windows_2019/qemu/autounattend.xml"

As you can see inside the files depending on the version and the hypervisor there is a different autounattend.xml file, these autounattend.xml files includes among other settings the credentials to set during the installation.

The list of files included are:


The credentials can be changed if needed in the following section of those autounattend.xml files:

 2    <AdministratorPassword>
 3        <Value>40Net123#</Value>
 4        <PlainText>true</PlainText>
 5    </AdministratorPassword>
 6    <LocalAccounts>
 7        <LocalAccount wcm:action="add">
 8            <Password>
 9            <Value>40Net123#</Value>
10            <PlainText>true</PlainText>
11            </Password>
12            <Group>administrators</Group>
13            <DisplayName>Lab Admin</DisplayName>
14            <Name>labadmin</Name>
15            <Description>Lab Admin</Description>
16        </LocalAccount>
17    </LocalAccounts>
2    <Password>
3        <Value>40Net123#</Value>
4        <PlainText>true</PlainText>
5    </Password>
6    <Enabled>true</Enabled>
7    <Username>labadmin</Username>

The variable files that includes the credentials to match what is defined in the autounattend.xml file, is:

  • packer-creds.json
2    "packer_user": "labadmin",
3    "packer_pass": "40Net123#"

Depending of the environment to use for build the image or to where to export the resulting template there is a group of credentials that we need to defined, the templates are defined as:

  • vsphere-creds-template.json
 2    "vsphere-server": "",
 3    "vsphere-user": "",
 4    "vsphere-password": "",
 5    "vsphere-datacenter": "",
 6    "vsphere-cluster": "" ,
 7    "vsphere-network": "" ,
 8    "vsphere-datastore": "",
 9    "vsphere-folder": ""
  • aws-creds-template.json
2    "aws_secret_key": "",
3    "aws_access_key": ""

The next set of variables, are related to accommodate the output format to each hypervisor/cloud, those files are:

  • kvm-vars.json
2    "compress": "true",
3    "disk_format": "qcow2"
  • gcp-vars.json
2    "compress": "false",
3    "disk_format": "raw",
4    "gcs_bucket" : "packer_images"
  • aws-vars.json
2    "aws_region": "us-east-1",
3    "aws_s3_bucket_name": "packer-windows-images",
4    "enable_aws_tool" : "true"

Mixing all together

There is a set of parameters that are passed during the execution, those are:

  • -only: This helps to specify which builder we want to use, and it also enables some specific provisioners and post-processors.
  • -except: This helps to ignore some specific provisioners and post-processors to accommodate the target environment.
  • -var tag=: This allows us to append a custom name to the image to create.
  • -var vsphere-maketemplate=true/false: This only applies when building an image for vSphere or AWS, and specify if we want to convert the resulting image into a VM Template.

After understand the set of variables needed, we can proceed to the execution of Packer, so it can take care of the whole building process

  • Windows 10 Enterprise Build 1903 on QEMU (using a local environment):
1packer build -only=qemu -var-file=windows-10-1903-iso.json -var-file=windows-10-qemu-vars.json -var-file=packer-creds.json -var-file=kvm-vars.json -except=gcp -var tag=golden windows-base.json

This is an output example I recorded as reference:

Now lets use a vSphere environment, and for this we are going to change some of the variables files as shown in the following example:

  • Windows 10 Enterprise Build 1903 on VMWare vSphere:
1packer build -only=vsphere-iso -except=aws,pre-ovf-aws,export-ovf -var-file=windows-10-1903-iso.json -var-file=windows-10-vsphere-vars.json -var-file=packer-creds.json -var-file=vsphere-creds.json  -var tag=golden -var vsphere-maketemplate=true windows-base.json

This by default is going to export the resulting template as OVF, if we want to avoid that, we need add the following parameter to the "-except" section: "export-ovf". The resulting line it would be:

1packer build -only=vsphere-iso -except=aws,pre-ovf-aws -var-file=windows-10-1903-iso.json -var-file=windows-10-vsphere-vars.json -var-file=packer-creds.json -var-file=vsphere-creds.json  -var tag=golden -var vsphere-maketemplate=true windows-base.json

Now, if we want to upload the resulting image built with QEMU to GCP, we neet to first install and configure gsutil, you can follow this guide:, then we need to create a Cloud Storage Bucket and then we can use the command: gsutil config to set the configuration. The Packer template already have the commands needed to upload and register the image to make it ready to launch new instances on Google Cloud using the resulting image.

  • Windows 10 Enterprise Build 1903 on QEMU (and upload it to Google Cloud Platform):
1packer build -only=qemu -var-file=windows-10-1903-iso.json -var-file=windows-10-qemu-vars.json -var-file=packer-creds.json -var-file=gcp-vars.json -except=fix-output-qcow2 -var tag=take15 windows-base.json

Then, if we want to build a Windows 2019 image, we can use the following command where you will notice that only a couple of variables changed, the same logic explained with the other commands applies:

  • Windows 2019 Server Datacenter on QEMU (using a local environment):
1packer build -only=qemu -var-file=windows-2019-iso.json -var-file=windows-2019-dc-qemu-vars.json -var-file=packer-creds.json -var-file=kvm-vars.json -except=gcp -var tag=golden windows-base.json

For AWS there are a few previous steps needed:

  1. Configure the AWS command-line utility, you can follow this guide
  2. Create an S3 bucket, you can follow this guide
  3. There is a folder named "aws", inside you will find two files, one of them is: "role-policy-tpl.json", you can make a local copy of that file, then locate and change the text: REPLACETHISWITHYOURBUCKET
  4. Execute the following commands (replace the path and the role-policy file name as needed):
1aws iam create-role --role-name vmimport --assume-role-policy-document file:///path/to/trust-policy.json
2aws iam put-role-policy --role-name vmimport --policy-name vmimport --policy-document file:///path/to/role-policy-newfile.json
  1. Make a copy of the file aws-creds-template.json to aws-creds.json and define your own AWS Keys, then execute:
1packer build -only=vsphere-iso -var-file=windows-10-1903-iso.json -var-file=windows-10-vsphere-vars.json -var-file=packer-creds.json -var-file=vsphere-creds.json -var-file=aws-creds.json -var-file=aws-vars.json  -var vsphere-maketemplate=false -var tag=golden windows-base.json

This will build an AWS ready AMI and will upload it using the included Packer plugin into your AWS account, after the process finish you will be able to deploy the image from your custom AMI images into EC2.

That's all for now, and this is the base that I use for later run Ansible Playbooks to fully configure an Active Directory environment, for example, among other things like installing vulnerable applications and services while investigating.

comments powered by Disqus