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.
| Workflow | When drivers load | Driver payload lives | Best for |
|---|---|---|---|
| A — DriverPaths | windowsPE pass, during Setup | Folder on install media or network share | Storage controllers needed for Setup to see the disk; one-off USB installs |
| B — DISM /Add-Driver | Already in the image when Setup runs | Mounted offline install.wim | Golden images deployed to known hardware |
| D — pnputil | After Windows is installed and the user has logged on | Local disk or network share, run by FirstLogonCommands or SetupComplete | Hardware that does not need a driver to install Windows (GPUs, peripherals) |
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> 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.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 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.
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.
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:
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
.catfile 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
/ForceUnsignedtodism /Add-Driverto 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 Publishersstore 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.
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.
| Target install | processorArchitecture | Driver must be |
|---|---|---|
| 64-bit x86 (Intel / AMD) Windows 10/11 | amd64 | x64 driver |
| ARM64 Windows 11 (Surface Pro X, Copilot+ PCs) | arm64 | ARM64-native driver |
| 32-bit x86 (legacy Windows 10, very rare) | x86 | x86 driver |
Common mistakes
- Copying a 64-bit unattend block and changing only the
<Path>when targeting ARM64 — leaveprocessorArchitecture="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-CurrentEditionshows the SKU, and the WIM info shows the architecture. - Confusing
processorArchitecture="ia64"(Itanium — long-dead) withamd64. There is no scenario in 2026 whereia64is 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 diskpart → list volume to see what PE
has mounted).
Further reading
- Sysprep workflows A/B/C/D — how
drivers persist (or do not) across an image-refresh cycle, and
PersistAllDeviceInstallsbehaviour. - Running PowerShell, cmd, and VBS from
unattend.xml — how to wrap
pnputiland vendor installers fromSetupComplete.cmdorFirstLogonCommands. - First-run command snippets — worked snippets including driver staging from a network share.
- Windows 11 OOBE & hardware bypass status — relevant when a "missing driver" message is actually a platform check failure, not a real driver problem.
- Microsoft Learn: Add device drivers to Windows during Windows Setup and Add and remove drivers to an offline Windows image.