Private/steps-osd/Step-OSDCloudWinPEOemDriversExport.ps1
|
function Step-OSDCloudWinPEOemDriversExport { <# .SYNOPSIS Exports connected OEM WinPE drivers to the OSDCloud staging folder. .DESCRIPTION Runs in WinPE only. Enumerates connected devices with pnputil, filters to OEM drivers, and exports supported device classes to a structured folder layout for later OSDCloud driver injection steps. .EXAMPLE Step-OSDCloudWinPEOemDriversExport Exports matching OEM drivers from the current WinPE session to the staging path. .LINK https://github.com/OSDeploy/OSD/tree/master/docs .NOTES Author: David Segura - Recast Software 2026-07-15 - Added comment-based help and improved pnputil validation and export logging #> [CmdletBinding()] param () #================================================= Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)]" #================================================= if ($env:SystemDrive -ne 'X:') { # Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Skip. Not running in WinPE (X:)" return } #================================================= # Output Path $OutputPath = 'C:\Windows\Temp\osdcloud-drivers-winpe' if (-not (Test-Path -Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null } $LogPath = 'C:\Windows\Temp\osdcloud-logs' if (-not (Test-Path -Path $LogPath)) { New-Item -ItemType Directory -Path $LogPath -Force | Out-Null } #================================================= # Build the list of devices using pnputil.exe, as the /format xml switch is not supported in older versions of WinPE. $output = & pnputil.exe /enum-devices /connected if ($LASTEXITCODE -ne 0) { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] pnputil /enum-devices failed with exit code $LASTEXITCODE" Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" return } $devices = @() $currentDevice = @{} foreach ($line in $output) { $line = $line.Trim() if ([string]::IsNullOrWhiteSpace($line)) { # Blank line means end of current device if ($currentDevice.Count -gt 0) { $devices += [PSCustomObject]$currentDevice $currentDevice = @{} } } elseif ($line -like "*:*") { # Parse key-value pair $key, $value = $line -split ':\s*', 2 $key = $key.Trim() -replace '\s+', '' # Remove spaces from key $value = $value.Trim() $currentDevice[$key] = $value } } # Add last device if exists if ($currentDevice.Count -gt 0) { $devices += [PSCustomObject]$currentDevice } $PnputilDevices = $devices | Where-Object { $_.DriverName -match 'oem' } | Sort-Object DriverName -Unique | Sort-Object ClassName # Classes to Export $ExportClass = @( '1394', 'DiskDrive', 'HDC', 'HIDClass', 'Keyboard', 'Mouse', 'MTD', 'Multifunction', 'Net', 'NvmeDisk', 'SCSIAdapter', 'Securitydevices', 'System', 'Volume', 'USB', 'USBDevice' ) #================================================= # Export OEM Drivers $ExportedCount = 0 $SkippedClassCount = 0 $FailedCount = 0 if ($PnputilDevices) { foreach ($OemDriver in $PnputilDevices) { #================================================= # Normalize Manufacturer Name $ManufacturerName = $OemDriver.ManufacturerName -as [string] if ([string]::IsNullOrWhiteSpace($ManufacturerName)) { $ManufacturerName = 'Unknown' } $ManufacturerName = $ManufacturerName.Trim() if ($ManufacturerName -match 'Dell' -or $OemDriver.Description -match 'Dell') { $ManufacturerName = 'Dell' } if ($ManufacturerName -match 'HP' -or $OemDriver.Description -match 'HP') { $ManufacturerName = 'HP' } if ($ManufacturerName -match 'Intel' -or $OemDriver.Description -match 'Intel' -or $OemDriver.InstanceID -match 'VEN_8086') { $ManufacturerName = 'Intel' } if ($ManufacturerName -match 'Logitech' -or $OemDriver.Description -match 'Logitech' -or $OemDriver.InstanceID -match 'VID_046D') { $ManufacturerName = 'Logitech' } if ($ManufacturerName -match 'Qualcomm|Snapdragon' -or $OemDriver.Description -match 'Qualcomm|Snapdragon' -or $OemDriver.InstanceID -match 'QCOM') { $ManufacturerName = 'Qualcomm' } if ($ManufacturerName -match 'Realtek' -or $OemDriver.Description -match 'Realtek' -or $OemDriver.InstanceID -match 'VEN_10EC') { $ManufacturerName = 'Realtek' } #================================================= # Normalize Foldername $FolderName = $OemDriver.DeviceDescription -replace '[\\/:*?"<>|#]', '' $FolderName = $FolderName -replace [regex]::Escape($ManufacturerName), '' $FolderName = $FolderName -replace '\(standard system devices\)', '' $FolderName = [regex]::Replace($FolderName, '\s*\(.*?\)\s*', ' ') $FolderName = [regex]::Replace($FolderName, '\s+', ' ') $FolderName = $FolderName.Trim() if ([string]::IsNullOrWhiteSpace($FolderName)) { $FolderName = $OemDriver.DriverName } #================================================= # Export WinPE Drivers if ($ExportClass -notcontains $OemDriver.ClassName) { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($OemDriver.ClassName)] $ManufacturerName $($OemDriver.DeviceDescription)" $SkippedClassCount++ continue } Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format s)] [$($OemDriver.ClassName)] $ManufacturerName $($OemDriver.DeviceDescription)" $ExportPath = Join-Path -Path (Join-Path -Path $OutputPath -ChildPath $OemDriver.ClassName) -ChildPath "$ManufacturerName $FolderName" if (-not (Test-Path -Path $ExportPath)) { New-Item -ItemType Directory -Path $ExportPath -Force | Out-Null } $null = & pnputil.exe /export-driver $OemDriver.DriverName $ExportPath if ($LASTEXITCODE -eq 0) { $ExportedCount++ } else { Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Failed to export $($OemDriver.DriverName) to $ExportPath (exit code $LASTEXITCODE)" $FailedCount++ } #================================================= } $PnputilDevices | Out-File -FilePath (Join-Path -Path $OutputPath -ChildPath 'pnputil.txt') -Encoding utf8 Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Export summary: Exported=$ExportedCount SkippedClass=$SkippedClassCount Failed=$FailedCount" } else { Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] No connected OEM drivers found for export." } #================================================= # If $OutputPath does not contain any files, remove it to avoid confusion. if (-not (Get-ChildItem -Path $OutputPath -Recurse -File -ErrorAction SilentlyContinue)) { Remove-Item -Path $OutputPath -Recurse -Force -ErrorAction SilentlyContinue } #================================================= Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" #================================================= } |