#Windows from an ISO
Windows builds are the hardest case. There are four moving pieces:
- Autounattend.xml on a virtual floppy — Windows Setup looks for
Autounattend.xmlon attached floppies during install and runs an unattended setup if found. - Virtio drivers on the same floppy — Windows Setup needs
vioscsi(SCSI) andnetkvm(network) to see the disk and the network. They're loaded via the unattend's<DriverPaths>section. - An OpenSSH bootstrap script — runs on first logon to install OpenSSH so the build can SSH in afterward.
- A boot command — types a few Enters to dismiss "Press any key to boot from CD".
sequenceDiagram
participant Aileron
participant VM as Windows VM
Aileron->>VM: power on (boot from ISO)
Aileron->>VM: bootCommand <enter>
VM->>VM: Setup reads Autounattend.xml from floppy
VM->>VM: loads vioscsi and netkvm drivers from floppy
VM->>VM: partitions disk and installs OS
VM->>VM: reboots
VM->>VM: first logon as autologon user
VM->>VM: runs configure-openssh.ps1
VM->>Aileron: SSH ready
Aileron->>VM: runs provisioners
#The Autounattend.xml essentials
The Autounattend XML is large but standardized. Key sections you'll customize:
<settings pass="windowsPE"><DriverPaths>— tells Setup where to find virtio drivers (point at the floppy:%configsetroot%\).<DiskConfiguration>and<ImageInstall>— partition layout and which Windows edition to install.<UserData>— accept EULA, optional product key.
<settings pass="oobeSystem"><AutoLogon>— automatic first-boot login as a known user.<UserAccounts>— local admin accounts.<FirstLogonCommands>— runs the OpenSSH installer.
<settings pass="specialize">— computer name, timezone.
A complete working sample lives in this repo at Autounattend.xml. Copy it, then search for YOUR and replace every occurrence before using the file. The placeholders are:
YOURCOMPUTERNAME— the VM's hostname.YOURUSERNAME— the registered owner string shown inwinverand similar metadata.YOURUSER— the local administrator account name created during setup.YOURPASSWORD— the local administrator account password (also used for autologon).
Tailor the rest to match your Windows version (10, 11, Server 2022, etc.).
#Files you need
files:
# Unattend that drives Setup. Paste the full sample from
# /docs/building/recipes/Autounattend and replace each YOUR* placeholder.
- name: Autounattend.xml
inline: |
<?xml version="1.0" encoding="utf-8"?>
<unattend ...>
...
</unattend>
# Bootstrap script: runs on first logon, installs OpenSSH
- name: configure-openssh.ps1
inline: |
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
choco install openssh --pre -y
. "C:\Program Files\OpenSSH-Win64\install-sshd.ps1"
New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH
Set-Service SSHD -StartupType Automatic
Start-Service SSHD
# Virtio network driver (netkvm)
- name: netkvm.inf
url: https://example.com/virtio/netkvm.inf
- name: netkvm.sys
url: https://example.com/virtio/netkvm.sys
- name: netkvm.cat
url: https://example.com/virtio/netkvm.cat
# Virtio SCSI driver (vioscsi)
- name: vioscsi.inf
url: https://example.com/virtio/vioscsi.inf
- name: vioscsi.sys
url: https://example.com/virtio/vioscsi.sys
- name: vioscsi.cat
url: https://example.com/virtio/vioscsi.cat
You need the .inf, .sys, and .cat for each driver you want loaded during install. The Fedora project publishes signed virtio-win drivers; you can download a virtio-win ISO and host the files yourself.
#The VM block
vms:
- name: builder
source:
blank: true
resources:
cpu: 4
memory: '8Gi' # Windows Setup is RAM-hungry
disks:
- name: rootdisk
size: '25Gi'
bus: scsi # matches vioscsi driver
efiFirmware:
secureBoot: false # Windows 11 needs UEFI
isos:
- url: 'https://example.com/Windows11.iso'
checksum: 'f260e50bf9...'
floppy:
files:
- name: Autounattend.xml
- name: configure-openssh.ps1
- name: netkvm.inf
- name: netkvm.sys
- name: netkvm.cat
- name: vioscsi.inf
- name: vioscsi.sys
- name: vioscsi.cat
communicator:
shell: powershell
sshUsername: skills # match the local admin in Autounattend.xml
sshPassword: skills
sshTimeout: '8h' # Windows Setup is slow
bootCommand:
- '<enter>' # dismiss "Press any key to boot from CD"
#Suggested provisioner order
flowchart TB
A[install-updates<br/>type: windows-update] --> B[install-guest-tools<br/>type: shell - choco installs etc]
B --> C[reboot]
C --> D[upload certs and binaries<br/>type: file]
D --> E[configure-system<br/>type: shell - registry, services]
E --> F[reboot]
F --> G[final-cleanup<br/>type: shell - sdelete, optimize-volume]
G --> H[reboot-final]
provisioners:
- name: install-updates
type: windows-update
windowsUpdate:
searchCriteria: 'BrowseOnly=0 and IsInstalled=0'
filters:
- "exclude:$_.Title -like '*Preview*'"
- 'include:$true'
- name: install-guest-tools
type: shell
shell:
inline: |
choco install sdelete -y --ignore-checksums
choco install autohotkey.portable -y
- type: reboot
- name: configure-system
type: shell
shell:
inline: |
Rename-Computer "MYHOST"
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force
powercfg -h off
- type: reboot
- name: compact
type: shell
shell:
inline: |
Optimize-Volume -DriveLetter C -ReTrim -Verbose
#Windows-specific gotchas
- Always start with NIC
model: e1000(the default). Windows Setup doesn't have virtio at first boot. Oncenetkvmis installed and a reboot has happened, you can switch the model. - Use
bus: scsifor the disk, paired with thevioscsidriver.virtiois faster but the install-time driver loading is fiddlier. efiFirmware.secureBoot: falseis required for most automated installs. Windows 11 strictly requires UEFI but Secure Boot can be enabled later.- File destinations use
C:\\...(double backslash in YAML strings). sshTimeoutshould be measured in hours for fresh ISO installs. Windows Setup + first updates can take 1–2 hours before SSH is reachable.ssh_authorized_keys: []doesn't apply — Windows uses a different key injection path. The build handles this for you as long asconfigure-openssh.ps1is wired into Autounattend's<FirstLogonCommands>.