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
|
installs Windows unattended from the ISO in `install-iso/` (it does everything
|
||||||
itself: answer file in `windows11/seed/`, QEMU hardware in `windows11/qemu/`; this
|
itself: answer file in `windows11/seed/`, QEMU hardware in `windows11/qemu/`; this
|
||||||
directory has no dependency on any other repository). The answer file boots
|
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
|
`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
|
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
|
image: nothing but Windows and the VirtIO drivers, with no account, no SSH, no settings,
|
||||||
other hardware-specific drivers and no machine identity. Each VM is configured
|
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
|
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
|
CD-ROM during its specialize and OOBE passes (a VM launcher can mount such a
|
||||||
seed ISO when it creates a VM disk).
|
seed ISO when it creates a VM disk).
|
||||||
|
|||||||
+14
-3
@@ -6,11 +6,12 @@
|
|||||||
# made once, and this script needs nothing outside this directory:
|
# made once, and this script needs nothing outside this directory:
|
||||||
# 1. an answer ISO is built from windows11/seed/Autounattend.xml;
|
# 1. an answer ISO is built from windows11/seed/Autounattend.xml;
|
||||||
# 2. QEMU installs Windows unattended from the ISO into a scratch directory.
|
# 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;
|
# /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.
|
# 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
|
# The image holds nothing but Windows and the VirtIO drivers, like a cloud image:
|
||||||
# other hardware-specific drivers (the installation hardware, windows11/qemu,
|
# no other hardware-specific drivers (the installation hardware, windows11/qemu,
|
||||||
# only uses devices with inbox Windows drivers), no machine identity, no user
|
# 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 answer file holds no credentials either: audit mode needs no
|
||||||
# account; the built-in Administrator is disabled), no SSH, no settings. Every
|
# 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)
|
VM_IMAGE_ARCH amd64 or arm64 (default: the host's)
|
||||||
WIN11_VM_INSTALL_ISO Windows 11 ISO (default: the path that
|
WIN11_VM_INSTALL_ISO Windows 11 ISO (default: the path that
|
||||||
"download-images.sh --print-path windows11 --iso" prints)
|
"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_DISK_SIZE Size of the system disk (default: 64G)
|
||||||
WIN11_VM_UEFI_CODE OVMF read-only firmware (auto-detected if unset)
|
WIN11_VM_UEFI_CODE OVMF read-only firmware (auto-detected if unset)
|
||||||
WIN11_VM_UEFI_VARS OVMF variable-store template (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
|
||||||
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)}"
|
output="${output:-$("$downloader" --print-path windows11)}"
|
||||||
if [[ -e "$output" && $force -eq 0 ]]; then
|
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."
|
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"
|
mkdir -p "$seed_dir"
|
||||||
sed "s/processorArchitecture=\"amd64\"/processorArchitecture=\"${arch}\"/" \
|
sed "s/processorArchitecture=\"amd64\"/processorArchitecture=\"${arch}\"/" \
|
||||||
"${win_dir}/seed/Autounattend.xml" >"${seed_dir}/Autounattend.xml"
|
"${win_dir}/seed/Autounattend.xml" >"${seed_dir}/Autounattend.xml"
|
||||||
|
cp -- "${win_dir}/seed/Install-VirtIO-Drivers.ps1" "${seed_dir}/"
|
||||||
seed_iso="${work}/seed.iso"
|
seed_iso="${work}/seed.iso"
|
||||||
case "$iso_tool" in
|
case "$iso_tool" in
|
||||||
xorriso) xorriso -as mkisofs -quiet -o "$seed_iso" -V ANSWER -J -r "$seed_dir" ;;
|
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
|
attach_media install_media "$install_iso" 2 ide.1
|
||||||
fi
|
fi
|
||||||
attach_media answer_media "$seed_iso" 3 ide.2
|
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
|
# 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
|
# to boot from CD or DVD...") and gives up after a few seconds. Nobody is there
|
||||||
|
|||||||
@@ -131,6 +131,11 @@
|
|||||||
</RunSynchronousCommand>
|
</RunSynchronousCommand>
|
||||||
<RunSynchronousCommand wcm:action="add">
|
<RunSynchronousCommand wcm:action="add">
|
||||||
<Order>4</Order>
|
<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>
|
<Description>Generalize the image and power off</Description>
|
||||||
<Path>C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /quiet</Path>
|
<Path>C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /shutdown /quiet</Path>
|
||||||
</RunSynchronousCommand>
|
</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