Files
gsadmin d6c5799f7e Remove the Storage module dependency and add the SPICE guest tools installation
- The ISO is now mounted, resolved to a volume, and dismounted through the
  storage CIM provider directly instead of through the "Storage" module, which
  is not present within every Windows PE boot image. The "MSFT_DiskImage" class
  is projected as cmdlets using the new "Toolkit\Libraries\Storage\DiskImage.cdxml"
  definition, so only the storage CIM provider itself is required
- The volume is resolved through the "MSFT_DiskImageToVolume" association rather
  than through "Get-Volume", and the drive letter is normalized because the
  provider exposes it as a character
- Removed the "Storage" module requirement from the requires statement
- Added the "-InstallSpiceGuestTools" parameter, which downloads and installs the
  SPICE guest tools. The installer is not digitally signed and exposes no version
  resource, therefore the driver signing certificates are read from the driver
  catalogs contained within the installer itself, imported into the trusted
  publishers store, and the expanded content is then removed. The installer is
  executed using its own normal installation method, which results in a
  completely silent installation with no driver prompts
- Added the new "Import-TrustedPublisherCertificate" toolkit function, which
  reads the signing certificate of each signed file within one or more
  directories and imports the distinct certificates into a configurable
  certificate store
- The archive utility required to expand the installer ships within
  "Toolkit\Tools\X64" and is located by searching the architecture specific
  directories, the architecture neutral directory, and then the local download
  directory. It is only downloaded when no existing copy is present, and the
  downloaded package is expanded using an administrative installation so that
  the product itself is never installed
- Anything downloaded at runtime is written beneath "%WinDir%\Temp\<ScriptBaseName>"
  so that a read only or UNC script location is never written to
- Both the guest agent and the SPICE guest tools are gated to the full operating
  system and are skipped automatically within Windows PE, so a single command
  line is safe for both deployment passes
- Updated the README with the Windows PE and full operating system command lines,
  the gating behavior, and the SPICE guest tools details

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:28:06 -04:00

12 KiB

Invoke-VirtIODrivers

Downloads the latest VirtIO ISO if necessary, copies the ISO locally if necessary, mounts the ISO automatically, dynamically detects the operating system, and installs the correct drivers for Proxmox virtual machines into the Windows driver store using DISM or PNPUTIL. The QEMU guest agent can optionally be installed directly from the mounted ISO when running within the full operating system.

This script can be run from a WindowsPE boot image after the operating system has been deployed to the fixed disk and the target volume will be located automatically by locating a valid installation of Windows.

This script can also be directly used within the full operating system to install the drivers after the fact or before a hypervisor migration. (VMWare to Proxmox).

Structure

The script follows the standardized script template and toolkit flow. The Toolkit\Toolkit.ps1 script is dot sourced during initialization and provides the logging, error handling, environment detection, and function/module/library loading infrastructure. The reusable helper functions live within Toolkit\Functions, the registry parsing libraries live within Toolkit\Libraries\Registry, the disk image cmdlet definition lives within Toolkit\Libraries\Storage, and the archive utility lives within Toolkit\Tools.

The ISO is mounted through the storage CIM provider directly rather than through the Storage PowerShell module, because that module is not present within every WindowsPE boot image. The MSFT_DiskImage class is projected as cmdlets using the Toolkit\Libraries\Storage\DiskImage.cdxml definition, so only the storage CIM provider itself is required.

The script is compatible with both Windows PowerShell 5.1 (including the WindowsPE PowerShell optional component) and PowerShell 7.

Usage

WindowsPE (boot image) — DeployR boot images only contain PowerShell 7, so use pwsh.exe:

pwsh.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File ".\Invoke-VirtIODrivers.ps1" -Install -InstallGuestAgent -InstallSpiceGuestTools

Full operating system:

powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File ".\Invoke-VirtIODrivers.ps1" -Install -InstallGuestAgent -InstallSpiceGuestTools

The same switches are safe to use in both places. Everything is gated in code, so nothing breaks:

  • Within WindowsPE, -InstallGuestAgent and -InstallSpiceGuestTools are skipped automatically (both require the full operating system) and the skip is logged. Only the driver injection runs.
  • Within the full operating system, all three run.
  • The guest agent is skipped when the installed version is already current, and the SPICE guest tools are skipped when already installed.

That means you can use one command line for both passes and let the script decide what applies.

Parameter Description
-Install Download the VirtIO driver ISO (if required) and install the relevant drivers for the detected operating system.
-InstallGuestAgent Install the QEMU guest agent from the mounted ISO. Recommended on every run: the installation is skipped automatically within WindowsPE (where the Windows Installer service is unavailable) and when the installed version is already current.
-InstallSpiceGuestTools Download and install the SPICE guest tools. Skipped automatically within WindowsPE and when already installed. The driver signing certificates are imported into the trusted publishers store beforehand so the installation is completely silent.
-DownloadURL Override the VirtIO ISO download URL. Defaults to the latest stable VirtIO ISO.
-DownloadDestinationDirectory Override the ISO download destination. Defaults to Content\ISOs beside the script. Any ISO already present there is used as-is regardless of its file name (the latest one wins), and the download is skipped entirely.
-SpiceGuestToolsDownloadURL Override the SPICE guest tools download URL.
-SpiceGuestToolsDestinationDirectory Override the SPICE guest tools download destination.
-ArchiveUtilityDownloadURL Override the archive utility download URL, used only when no local copy is present.
-LogDirectory Override the log directory. Sensible defaults are used for WindowsPE, task sequence, and full operating system scenarios.

The ISO download automatically detects and uses the system default proxy with default credentials.

Anything downloaded at runtime is written beneath %WinDir%\Temp\Invoke-VirtIODrivers, so running the script from a read-only or UNC location is not a problem.

SPICE guest tools

The SPICE guest tools installer is not digitally signed and exposes no version resource, so the driver signing certificates are read from the driver catalogs contained within the installer itself. The installer is expanded using the archive utility in Toolkit\Tools, every distinct signing certificate is imported into the local machine trusted publishers store, the expanded content is removed, and the installer is then executed using its own normal installation method. The result is a completely silent installation with no driver prompts.

The archive utility is located in Toolkit\Tools\<ProcessorArchitecture> (for example X64), then Toolkit\Tools\All, and finally the local download directory. A copy ships with the toolkit, so no download is required; if one is ever needed, the package is expanded using an administrative installation and the product itself is never installed.

OS deployment scenarios (DeployR, MDT, SCCM)

During OS deployment the script needs to run twice:

  1. Boot image (WindowsPE) — after the operating system image has been applied, run with -Install -InstallGuestAgent -InstallSpiceGuestTools to inject the VirtIO drivers into the offline operating system using DISM, so the deployed OS can boot on VirtIO virtual hardware on first startup (the guest agent and SPICE guest tools portions are skipped automatically within WindowsPE).
  2. Full operating system — run the same command again to register the drivers with pnputil, install the QEMU guest agent, and install the SPICE guest tools.

OS detection is handled automatically in both passes, so the command line is the same aside from the interpreter. DeployR boot images only contain PowerShell 7, so the script must be launched with pwsh.exe there:

pwsh.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File ".\Invoke-VirtIODrivers.ps1" -Install -InstallGuestAgent -InstallSpiceGuestTools

Note: the boot image itself must already contain the VirtIO drivers (added ahead of time using your preferred boot image servicing method), otherwise WindowsPE cannot see VirtIO SCSI disks or network adapters. Alternatively, the virtual machine can use non-VirtIO virtual hardware (for example SATA disks and an emulated E1000 network adapter), which works without servicing the boot image but carries a performance penalty.

Virtual machine templates and hypervisor migration

Template creation: run the script within the full operating system with -Install -InstallGuestAgent before converting the virtual machine into a template, so every clone comes up VirtIO-ready with the QEMU guest agent already installed.

Hypervisor migration (VMware ESXi to Proxmox): run the script within the full operating system with -Install -InstallGuestAgent before the migration. The VirtIO devices do not exist yet at that point, so pnputil simply stages the driver packages into the Windows driver store. On first boot under the new hypervisor, Plug and Play matches the staged drivers automatically and the VM boots on VirtIO SCSI disk controllers and virtual network adapters without a recovery pass. No more blue screens!

This exact flow was used in a production migration from VMware ESXi to Proxmox: the script was run within the full operating system of each VM before migration, and after cutover, over 30 Windows VMs came up without issue.

Re-running the script is always safe: the Windows driver store handles already-present driver packages (pnputil and DISM skip or version-rank existing packages rather than creating conflicts), so drivers that are already installed are never a problem.

Note: Invoke-VirtIODrivers.exe is just there as a powershell bootstrapper. If you double click the executable, it simply executes the Powershell script with the same name automatically and shows the execution window. Nothing more.

Offline registry hive detection

Within WindowsPE, the deployed operating system details are read directly from the offline SOFTWARE registry hive of the deployed volume without loading or mounting the hive, using the Eric Zimmerman Registry library (version 2026.5.0, netstandard2.0), which parses the raw REGF hive file format. The library and its dependency closure are stored within Toolkit\Libraries\Registry and are loaded into memory in dependency order (no file locks): byte loaded within Windows PowerShell, and stream loaded into a dedicated assembly load context within PowerShell 7.

Snag_612d0a7 Snag_612df8b Snag_62f90f1

This is sample output of the Windows Driver Store using Powershell after the drivers have been installed.

Command: Get-WindowsDriver -Online | Where-Object {($_.ProviderName -imatch '(.*Red.*Hat.*)')} | Select-Object -Property @('OriginalFileName', 'ClassName', 'BootCritical', 'ProviderName', 'Version', 'ClassGUID')

BootCritical ClassGuid ClassName OriginalFileName ProviderName Version
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\balloon.inf_amd64_eaf9fe5ccc46cea0\balloon.inf Red Hat, Inc. 100.94.104.24800
False {4D36E972-E325-11CE-BFC1-08002BE10318} Net C:\Windows\System32\DriverStore\FileRepository\netkvm.inf_amd64_108667f5ebeb0ad0\netkvm.inf Red Hat, Inc. 100.94.104.24800
False {4D36E975-E325-11CE-BFC1-08002BE10318} NetTrans C:\Windows\System32\DriverStore\FileRepository\vioprot.inf_amd64_5abf6da903f19370\vioprot.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\viofs.inf_amd64_9d8003dbf4948969\viofs.inf Red Hat, Inc. 100.94.104.24800
False {4D36E968-E325-11CE-BFC1-08002BE10318} Display C:\Windows\System32\DriverStore\FileRepository\viogpudo.inf_amd64_d108681ae5f48232\viogpudo.inf Red Hat, Inc. 100.94.104.24800
True {745A17A0-74D3-11D0-B6FE-00A0C90F57DA} HIDClass C:\Windows\System32\DriverStore\FileRepository\vioinput.inf_amd64_32a7b4d6e1632c93\vioinput.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\viorng.inf_amd64_dfa2dff76d3c06c9\viorng.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\vioser.inf_amd64_650b7c25b9f9e8bc\vioser.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97B-E325-11CE-BFC1-08002BE10318} SCSIAdapter C:\Windows\System32\DriverStore\FileRepository\vioscsi.inf_amd64_9717e9d0dbb31583\vioscsi.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97B-E325-11CE-BFC1-08002BE10318} SCSIAdapter C:\Windows\System32\DriverStore\FileRepository\viostor.inf_amd64_3e677331b798639a\viostor.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\fwcfg.inf_amd64_c9590a85c7935d96\fwcfg.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\pvpanic.inf_amd64_5041c2d4340b58fb\pvpanic.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\pvpanic-pci.inf_amd64_d9c6c27cd62af2f6\pvpanic-pci.inf Red Hat, Inc. 100.94.104.24800
True {4D36E97D-E325-11CE-BFC1-08002BE10318} System C:\Windows\System32\DriverStore\FileRepository\smbus.inf_amd64_5f03787cbdf7a56d\smbus.inf Red Hat, Inc. 100.0.0.0