Docs
← Back to the builder

Adding drivers to a Windows unattended install

Three places to inject drivers into a Windows install, and you pick by workflow. For a fresh USB install (Workflow A) it's DriverPaths in the windowsPE pass. For a golden image (Workflow B) it's dism /Add-Driver against the WIM before deployment. For an image refresh (Workflow D) it's pnputil from auditUser. Each has its own signing rules, its own ordering quirks, and one mistake you can only diagnose by reading setupapi.dev.log at 2 am. Cross-ref: sysprep workflows for how driver state persists across refresh cycles.

Which workflow do you need?

The three workflows differ in when the driver is loaded and where the .inf payload lives. Choose based on whether you control the install media, the image, or only the deployed machine.

Driver-injection workflow comparison
WorkflowWhen drivers loadDriver payload livesBest for
A — DriverPathswindowsPE pass, during SetupFolder on install media or network shareStorage controllers needed for Setup to see the disk; one-off USB installs
B — DISM /Add-DriverAlready in the image when Setup runsMounted offline install.wimGolden images deployed to known hardware
D — pnputilAfter Windows is installed and the user has logged onLocal disk or network share, run by FirstLogonCommands or SetupCompleteHardware that does not need a driver to install Windows (GPUs, peripherals)
It is fine to combine them. Typical pattern: Workflow B for the NIC and storage drivers (so the machine boots and reaches the network), then Workflow D from SetupComplete.cmd for the GPU and chipset packages that ship as self-extracting installers rather than raw .inf files.

Workflow A — windowsPE DriverPaths

At Setup time, the windowsPE pass can be told to scan one or more folders for .inf drivers and load them into the WinPE environment and stage them for the running Windows install. This is the only workflow that solves the "Setup can't find any drives" problem caused by a storage controller (RAID, NVMe, vendor SCSI) that WinPE does not know about out of the box.

Folder layout on the install media

Place the driver folders anywhere on the install media (USB stick or ISO), and reference them with absolute paths from the media's drive letter at PE time. A common convention:

USB / ISO root:
  \autounattend.xml
  \sources\install.wim
  \drivers\
      \storage\
          \intel-vmd\
              iaStorVD.inf
              iaStorVD.sys
              iaStorVD.cat
          \lsi-megaraid\
              megasas35.inf
              megasas35.sys
              megasas35.cat
      \network\
          \intel-i225\
              e2f.inf
              e2f.sys
              e2f.cat

Each leaf folder must contain the .inf, the .sys binary, and the .cat security catalog. Do not zip them — Setup scans for .inf files literally.

The XML snippet

The component is Microsoft-Windows-PnpCustomizationsWinPE, in the windowsPE pass. Each driver folder is a <PathAndCredentials> entry with a unique wcm:keyValue:

<settings pass="windowsPE">
  <component name="Microsoft-Windows-PnpCustomizationsWinPE"
             processorArchitecture="amd64"
             publicKeyToken="31bf3856ad364e35"
             language="neutral"
             versionScope="nonSxS"
             xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
    <DriverPaths>
      <PathAndCredentials wcm:keyValue="1" wcm:action="add">
        <Path>X:\drivers\storage\intel-vmd</Path>
      </PathAndCredentials>
      <PathAndCredentials wcm:keyValue="2" wcm:action="add">
        <Path>X:\drivers\storage\lsi-megaraid</Path>
      </PathAndCredentials>
      <PathAndCredentials wcm:keyValue="3" wcm:action="add">
        <Path>X:\drivers\network\intel-i225</Path>
      </PathAndCredentials>
    </DriverPaths>
  </component>
</settings>
The drive letter is the WinPE drive letter, not the future C:. At windowsPE time, your USB key typically appears as X: or D: depending on boot order. If you hard-code C:\drivers\... it will fail because no C: exists yet (or it points at the in-RAM PE volume). Use the media's PE-time letter, or use a UNC path to a network share you can reach from PE.
Boot-critical storage drivers MUST be in DriverPaths. HP and Dell enterprise SKUs with vendor NVMe controllers, Surface Pro storage, and any RAID-mode SATA controller are invisible to a stock WinPE. If the storage driver is not loaded before the install pass, Setup cannot see the target disk and the "Where do you want to install Windows?" screen shows no drives. This is the failure mode Workflow A is specifically designed to fix — it is not optional for unfamiliar hardware. Workflow B (offline DISM) cannot help here: the WIM is on a disk Setup can't see.

Network share variant

You can also load drivers from a SMB share that WinPE can reach, by giving <Credentials> alongside the path:

<PathAndCredentials wcm:keyValue="1" wcm:action="add">
  <Path>\\fileserver\drivers\storage\intel-vmd</Path>
  <Credentials>
    <Domain>CONTOSO</Domain>
    <Username>driver-fetch</Username>
    <Password>...</Password>
  </Credentials>
</PathAndCredentials>

This requires that the WinPE image already has network drivers for the NIC on the target machine. If WinPE cannot bring up the NIC, the share is unreachable and the path fails silently — drivers from later <PathAndCredentials> entries still attempt. Plan accordingly: put the NIC driver itself on local media, and only use the share for bulky third-party packages.

Workflow B — DISM /Add-Driver into an offline image

Workflow B bakes drivers permanently into install.wim (or a captured golden image) before that image ever boots. When Setup runs, the drivers are already in the driver store — DriverPaths in unattend.xml is not needed for that payload. This is the cleanest approach for any image you control and deploy more than once.

Mount the image

:: From an elevated cmd on a Windows machine with the ADK installed.
md C:\mount

:: List indexes inside the WIM (Pro = 6 in standard Win 11 media; check yours):
dism /Get-WimInfo /WimFile:"D:\install.wim"

:: Mount the index you want to modify:
dism /Mount-Image /ImageFile:"D:\install.wim" /Index:6 /MountDir:C:\mount

Add drivers (single .inf, folder, or recursive folder)

:: Single .inf
dism /Image:C:\mount /Add-Driver /Driver:"D:\drivers\intel-vmd\iaStorVD.inf"

:: Whole folder, recursive (typical):
dism /Image:C:\mount /Add-Driver /Driver:"D:\drivers" /Recurse

:: To allow unsigned drivers (lab only, see signing section below):
dism /Image:C:\mount /Add-Driver /Driver:"D:\drivers" /Recurse /ForceUnsigned

List, commit, and unmount

:: Verify the driver is in the offline store
dism /Image:C:\mount /Get-Drivers

:: Commit changes and unmount (this writes the WIM)
dism /Unmount-Image /MountDir:C:\mount /Commit

:: If something went wrong and you want to throw the changes away:
dism /Unmount-Image /MountDir:C:\mount /Discard
Commit takes minutes, not seconds. A WIM commit on a multi-GB Windows image can take 3–10 minutes depending on disk speed. Do not interrupt it — an aborted commit leaves the WIM corrupt and you restore from your backup copy. Always keep a copy of the original install.wim before mounting.

Same workflow on a captured golden image

If you have a captured WIM from dism /Capture-Image (see the sysprep workflows page), the same Mount-Image + Add-Driver + Unmount-Image /Commit sequence works. This is how you patch drivers into a golden image without doing a full Workflow D sysprep-over-sysprep refresh.

Workflow D — pnputil after install

Some drivers do not need to be present at Setup time — Windows boots fine without them, and you only want them installed once the user is at the desktop. GPU drivers, vendor utilities, and any package that ships as a self-extractor with a chipset/audio bundle fall into this bucket. The cleanest tool for raw .inf driver installs at this stage is pnputil.

Stage the drivers somewhere local

Either copy them to the local disk during SetupComplete.cmd, or leave them on a network share you can reach. A local copy is more reliable because it survives a reboot mid-install:

:: Stage drivers locally (in SetupComplete.cmd or FirstLogonCommands)
md C:\Drivers
xcopy /E /Y /Q "\\fileserver\drivers\post-install\*" "C:\Drivers\"

Install with pnputil

:: Add and install every .inf under a folder, recursively
pnputil /add-driver "C:\Drivers\*.inf" /subdirs /install

:: Add only (driver staged in store but not bound to hardware):
pnputil /add-driver "C:\Drivers\nvidia\nvlt.inf"

The /install flag tells pnputil to bind the driver to any matching hardware immediately rather than waiting for the next PnP scan. The /subdirs flag walks subdirectories.

Some vendor packages refuse to be installed via pnputil. NVIDIA's full GeForce package, for example, is an installer (setup.exe) that wraps the .inf + a control panel + telemetry components. For those, run the vendor installer with its silent flag from SetupComplete.cmd instead. See the command reference for how to invoke vendor installers safely.

Calling it from unattend

Wrap the pnputil call in SetupComplete.cmd if you want it to run in SYSTEM context before the first user logon, or in FirstLogonCommands if you specifically need a user profile. For driver installs, SYSTEM is correct — drivers are machine-level. The command reference covers the entry points in detail.

If your image deploys with an OEM product key on Pro/Home, SetupComplete.cmd is silently disabled. Only Windows Enterprise editions and Windows Server execute SetupComplete.cmd; on OEM-keyed Pro/Home consumer hardware the script is skipped without warning. For those images, FirstLogonCommands is a poor substitute for driver work — it runs at MEDIUM integrity and pnputil /add-driver ... /install fires a UAC prompt that nobody is there to click. Better options on OEM-keyed images: (a) bake the drivers into the WIM via Workflow B (DISM), or (b) schedule a SYSTEM-context task via schtasks /create /ru SYSTEM from FirstLogonCommands and let it run after first logon. See the command reference for the full SetupComplete restriction.

Picking signed vs unsigned drivers

Windows refuses to load unsigned kernel-mode drivers on 64-bit editions by default. This is enforced by Driver Signature Enforcement (DSE) and tightened further on systems with Secure Boot. The practical rules:

Win 11 22H2+ with Secure Boot rejects unsigned drivers in Workflow A (DriverPaths) outright. On 22621 / 22631 / 26100 with default Secure Boot, an unsigned driver loaded via Microsoft-Windows-PnpCustomizationsWinPE fails with "INF could not be installed" in setupact.log and the device is left without a driver. Your options are: (a) sign the driver with an in-house code-signing certificate whose root is in Trusted Publishers, (b) toggle bcdedit /set TESTSIGNING ON on the target (lab only — see warning below), or (c) bake the driver in via Workflow B with /ForceUnsigned AND disable Secure Boot on the target. There is no supported way to load an unsigned driver from DriverPaths on a Secure Boot machine.
  • Production deployments: use vendor-signed drivers only. If the .cat file is missing or the signature is broken, the driver will fail to load and Device Manager will show a yellow bang with code 52.
  • Lab / test rigs: you can pass /ForceUnsigned to dism /Add-Driver to bake unsigned drivers into the image, but the running OS still needs DSE disabled (test signing mode) to actually load them at boot.
  • Self-signed for in-house drivers: sign with a code-signing cert from your own CA, distribute the root cert to the Trusted Publishers store via GPO, and Windows will load them. This is the right path for internally-developed PnP drivers.

How to check signature status before deploying

:: From an elevated PowerShell on your build machine:
signtool verify /pa /v "D:\drivers\intel-vmd\iaStorVD.cat"

:: Or with PowerShell's Get-AuthenticodeSignature:
Get-AuthenticodeSignature "D:\drivers\intel-vmd\iaStorVD.cat" | Format-List *

If signtool reports Successfully verified and the signer chains to a CA in the Trusted Root store, the driver will load on a default-configured Windows machine.

Do not ship test-signing mode to production. Disabling DSE (bcdedit /set testsigning on) lets unsigned drivers load but also lets any unsigned kernel code load — including malware that managed to drop a .sys file. Test-signing is fine for a dev box; it is a serious vulnerability on a fleet image.

Driver-architecture mismatch (x64 vs arm64)

The processorArchitecture attribute on each <component> in unattend.xml must match the architecture of the Windows image being installed — not the architecture of the driver. The driver itself must also match the image: a 64-bit Windows install cannot load a 32-bit driver, and an ARM64 Windows install (Windows on ARM, Surface Pro X, Snapdragon X laptops) cannot load an x64 driver.

processorArchitecture values
Target installprocessorArchitectureDriver must be
64-bit x86 (Intel / AMD) Windows 10/11amd64x64 driver
ARM64 Windows 11 (Surface Pro X, Copilot+ PCs)arm64ARM64-native driver
32-bit x86 (legacy Windows 10, very rare)x86x86 driver

Common mistakes

  • Copying a 64-bit unattend block and changing only the <Path> when targeting ARM64 — leave processorArchitecture="amd64" by accident and the entire component is silently dropped at parse time.
  • Bundling x64 drivers into an ARM64 image with dism /Add-Driver. DISM accepts them at add time (no error) but Windows fails to load them on boot. Always validate with the OS architecture of the mounted image: dism /Image:C:\mount /Get-CurrentEdition shows the SKU, and the WIM info shows the architecture.
  • Confusing processorArchitecture="ia64" (Itanium — long-dead) with amd64. There is no scenario in 2026 where ia64 is correct.

Verifying drivers actually loaded

After deploying the image, the only safe verification is to look at the running machine. Two commands and one log file cover it.

pnputil /enum-drivers — what is in the driver store

:: List every third-party driver in the store (run elevated)
pnputil /enum-drivers

:: Sample output:
::   Published Name :     oem12.inf
::   Original Name :      iaStorVD.inf
::   Provider Name :      Intel
::   Class Name :         Storage controllers
::   Driver Version :     19.5.5.1054
::   Signer Name :        Microsoft Windows Hardware Compatibility Publisher

If your .inf name appears in Original Name, the driver is in the store. If it also appears in Device Manager bound to a hardware ID, it is actively in use. If it is in the store but no device shows up, the driver did not match any hardware ID — usually the wrong vendor revision for the silicon on the target.

setupapi.dev.log — why a driver loaded (or did not)

Windows logs every PnP driver install attempt to C:\Windows\INF\setupapi.dev.log. This is the authoritative record of which .inf got picked for each hardware ID and why. When a driver mysteriously fails to bind, this is where the explanation lives.

:: Tail the log while you trigger a driver install
powershell -NoProfile -Command "Get-Content C:\Windows\INF\setupapi.dev.log -Tail 100 -Wait"

:: Or search for a specific hardware ID
powershell -NoProfile -Command ^
  "Select-String -Path C:\Windows\INF\setupapi.dev.log -Pattern 'VEN_8086&DEV_15F3'"

Look for blocks beginning with >>> [Device Install — each one is a single attempt with rank scoring, signature check result, and final outcome. A line like !!! dvi: Driver not Trusted Installed means the driver was rejected by DSE — back to the signing section.

For storage drivers loaded in WinPE specifically

A storage driver loaded by Workflow A only reveals itself if Setup actually got past the disk-selection screen. If the drive list is empty at install time, the driver did not load — check that the <Path> letter in unattend matches the actual WinPE drive letter (press Shift+F10 at the Setup screen and run diskpartlist volume to see what PE has mounted).

Further reading