Install the VirtIO drivers into the base image in audit mode
Add Install-VirtIO-Drivers.ps1, run from Autounattend.xml before sysprep, and attach the virtio-win ISO (downloaded if missing) during the build. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
91ae8cb147
commit
00695cdddc
4 files changed
+61
-6
No files matched your search
@@ -118,11 +118,12 @@ does not run Windows Setup again. `build-windows-base.sh` makes that base image
|
||||
installs Windows unattended from the ISO in `install-iso/` (it does everything
|
||||
itself: answer file in `windows11/seed/`, QEMU hardware in `windows11/qemu/`; this
|
||||
directory has no dependency on any other repository). The answer file boots
|
||||
into audit mode (no account and no credentials are needed) and runs
|
||||
into audit mode (no account and no credentials are needed), adds the VirtIO drivers
|
||||
from the virtio-win ISO (downloaded if missing) to the driver store and runs
|
||||
`sysprep /generalize` there as its last step; the script waits for the guest to power
|
||||
off and stores the disk read-only. The image is clean and generic, like a cloud
|
||||
image: nothing but Windows, with no account, no SSH, no settings, no VirtIO or
|
||||
other hardware-specific drivers and no machine identity. Each VM is configured
|
||||
image: nothing but Windows and the VirtIO drivers, with no account, no SSH, no settings,
|
||||
no other hardware-specific drivers and no machine identity. Each VM is configured
|
||||
on its first boot: Windows reads `Unattend.xml` from the root of an attached
|
||||
CD-ROM during its specialize and OOBE passes (a VM launcher can mount such a
|
||||
seed ISO when it creates a VM disk).
|
||||
|
||||
+14
-3
@@ -6,11 +6,12 @@
|
||||
# made once, and this script needs nothing outside this directory:
|
||||
# 1. an answer ISO is built from windows11/seed/Autounattend.xml;
|
||||
# 2. QEMU installs Windows unattended from the ISO into a scratch directory.
|
||||
# The answer file then boots into audit mode, where it runs sysprep
|
||||
# The answer file then boots into audit mode, where it adds the VirtIO
|
||||
# drivers from the virtio-win ISO to the driver store and runs sysprep
|
||||
# /generalize, which removes the machine identity and shuts the guest down;
|
||||
# 3. the shut-down disk is converted into the output file and made read-only.
|
||||
# The image holds nothing but Windows itself, like a cloud image: no VirtIO or
|
||||
# other hardware-specific drivers (the installation hardware, windows11/qemu,
|
||||
# The image holds nothing but Windows and the VirtIO drivers, like a cloud image:
|
||||
# no other hardware-specific drivers (the installation hardware, windows11/qemu,
|
||||
# only uses devices with inbox Windows drivers), no machine identity, no user
|
||||
# account (the answer file holds no credentials either: audit mode needs no
|
||||
# account; the built-in Administrator is disabled), no SSH, no settings. Every
|
||||
@@ -58,6 +59,8 @@ Environment variables:
|
||||
VM_IMAGE_ARCH amd64 or arm64 (default: the host's)
|
||||
WIN11_VM_INSTALL_ISO Windows 11 ISO (default: the path that
|
||||
"download-images.sh --print-path windows11 --iso" prints)
|
||||
WIN11_VM_TOOLS_ISO virtio-win ISO (default: the path that
|
||||
"download-images.sh --print-path virtio-win" prints; downloaded if missing)
|
||||
WIN11_VM_DISK_SIZE Size of the system disk (default: 64G)
|
||||
WIN11_VM_UEFI_CODE OVMF read-only firmware (auto-detected if unset)
|
||||
WIN11_VM_UEFI_VARS OVMF variable-store template (auto-detected if unset)
|
||||
@@ -130,6 +133,11 @@ if [[ -z "${WIN11_VM_INSTALL_ISO:-}" ]] && ! "$downloader" --print-path windows1
|
||||
fi
|
||||
fi
|
||||
|
||||
# The VirtIO drivers for the image come from the virtio-win ISO.
|
||||
virtio_iso="${WIN11_VM_TOOLS_ISO:-$("$downloader" --print-path virtio-win)}"
|
||||
[[ -s "$virtio_iso" ]] || "$downloader" virtio-win || exit 1
|
||||
[[ -s "$virtio_iso" ]] || die "the virtio-win ISO was not found: ${virtio_iso}"
|
||||
|
||||
output="${output:-$("$downloader" --print-path windows11)}"
|
||||
if [[ -e "$output" && $force -eq 0 ]]; then
|
||||
die "${output} exists (it may be the backing file of existing VM disks); use --force or another OUTPUT."
|
||||
@@ -221,6 +229,7 @@ seed_dir="${work}/seed"
|
||||
mkdir -p "$seed_dir"
|
||||
sed "s/processorArchitecture=\"amd64\"/processorArchitecture=\"${arch}\"/" \
|
||||
"${win_dir}/seed/Autounattend.xml" >"${seed_dir}/Autounattend.xml"
|
||||
cp -- "${win_dir}/seed/Install-VirtIO-Drivers.ps1" "${seed_dir}/"
|
||||
seed_iso="${work}/seed.iso"
|
||||
case "$iso_tool" in
|
||||
xorriso) xorriso -as mkisofs -quiet -o "$seed_iso" -V ANSWER -J -r "$seed_dir" ;;
|
||||
@@ -298,6 +307,8 @@ else
|
||||
attach_media install_media "$install_iso" 2 ide.1
|
||||
fi
|
||||
attach_media answer_media "$seed_iso" 3 ide.2
|
||||
# The VirtIO drivers are installed into the image from this ISO in audit mode.
|
||||
attach_media virtio_media "$virtio_iso" 4 ide.3
|
||||
|
||||
# The Windows ISO boots through bootmgr, which waits for a key ("Press any key
|
||||
# to boot from CD or DVD...") and gives up after a few seconds. Nobody is there
|
||||
|
||||
@@ -131,6 +131,11 @@
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>4</Order>
|
||||
<Description>Add the VirtIO drivers from the virtio-win ISO to the driver store</Description>
|
||||
<Path>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& ('{0}:\Install-VirtIO-Drivers.ps1' -f (Get-Volume -FileSystemLabel ANSWER).DriveLetter); exit $LASTEXITCODE"</Path>
|
||||
</RunSynchronousCommand>
|
||||
<RunSynchronousCommand wcm:action="add">
|
||||
<Order>5</Order>
|
||||
<Description>Generalize the image and power off</Description>
|
||||
<Path>C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /quiet</Path>
|
||||
</RunSynchronousCommand>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# Runs in audit mode, before sysprep: adds the VirtIO drivers from the virtio-win
|
||||
# ISO to the driver store. The devices are not present during the installation
|
||||
# (it uses ramfb and e1000e), so pnputil only stages the packages; they survive
|
||||
# /generalize, and Windows binds them on the first start with virtio devices.
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$arch = if ([Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'ARM64' } else { 'amd64' }
|
||||
$roots = (Get-PSDrive -PSProvider FileSystem).Root
|
||||
$drivers = @(
|
||||
@{ Folder = 'NetKVM'; Inf = 'netkvm.inf'; Required = $true },
|
||||
@{ Folder = 'viogpudo'; Inf = 'viogpudo.inf'; Required = $true },
|
||||
@{ Folder = 'vioserial'; Inf = 'vioser.inf' },
|
||||
@{ Folder = 'Balloon'; Inf = 'balloon.inf' },
|
||||
@{ Folder = 'viorng'; Inf = 'viorng.inf' },
|
||||
@{ Folder = 'vioinput'; Inf = 'vioinput.inf' }
|
||||
)
|
||||
|
||||
$installed = 0
|
||||
foreach ($driver in $drivers) {
|
||||
$inf = $roots |
|
||||
ForEach-Object { Join-Path $_ "$($driver.Folder)\w11\$arch\$($driver.Inf)" } |
|
||||
Where-Object { Test-Path -LiteralPath $_ } |
|
||||
Select-Object -First 1
|
||||
if (-not $inf) {
|
||||
if ($driver.Required) {
|
||||
throw "The $arch $($driver.Folder) driver was not found on the attached media."
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
& pnputil.exe /add-driver $inf /install | Out-Host
|
||||
# 3010 asks for a restart; 259 means that no device uses the driver.
|
||||
if ($LASTEXITCODE -notin 0, 259, 3010) {
|
||||
throw "pnputil failed to install $inf (exit code $LASTEXITCODE)."
|
||||
}
|
||||
$installed++
|
||||
}
|
||||
Write-Host "Added $installed VirtIO $arch driver packages."
|
||||
Reference in new issue
Block a user