mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Improve driver injection error handling and resilience
Enhances the driver installation process to be more resilient by allowing non-critical driver injection failures to not halt the entire deployment. Key improvements include: - Adds `IgnoreExitCode` and `PassThruExitCode` parameters to the process invocation function, enabling callers to handle non-zero exit codes without throwing exceptions. - Modifies driver injection logic (both WIM and folder-based) to capture exit codes and log warnings instead of failing the deployment when drivers fail to inject. - Automatically collects and preserves diagnostic logs (dism.log and setupapi.offline.log) to the USB drive when driver installation encounters issues, aiding post-deployment troubleshooting. - Wraps cleanup operations in try-catch blocks to ensure temporary resources are released even if unmounting or deletion fails. - Fixes code formatting inconsistencies and indentation throughout the script for improved readability. This approach prioritizes deployment completion while preserving critical diagnostic information when driver-related issues occur.
This commit is contained in:
@@ -23,7 +23,7 @@ function Get-HardDrive() {
|
||||
if ($manufacturer -eq 'Microsoft Corporation' -and $model -eq 'Virtual Machine') {
|
||||
WriteLog 'Running in a Hyper-V VM. Getting virtual disk on Index 0 and SCSILogicalUnit 0'
|
||||
$diskDriveCandidates = @(Get-CimInstance -Class 'Win32_DiskDrive' | Where-Object { $_.MediaType -eq 'Fixed hard disk media' `
|
||||
-and $_.Model -eq 'Microsoft Virtual Disk'
|
||||
-and $_.Model -eq 'Microsoft Virtual Disk' `
|
||||
-and $_.Index -eq 0 `
|
||||
-and $_.SCSILogicalUnit -eq 0
|
||||
})
|
||||
@@ -74,7 +74,13 @@ function Invoke-Process {
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
[string]$ArgumentList,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$IgnoreExitCode,
|
||||
|
||||
[Parameter()]
|
||||
[switch]$PassThruExitCode
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
@@ -96,19 +102,39 @@ function Invoke-Process {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
# Non-terminating mode: capture output to Scriptlog and continue
|
||||
if ($IgnoreExitCode) {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
if ([string]::IsNullOrEmpty($cmdError) -eq $false) {
|
||||
WriteLog $cmdError
|
||||
}
|
||||
if ($PassThruExitCode) {
|
||||
return $cmd.ExitCode
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
throw "Process failed. ExitCode = $($cmd.ExitCode)."
|
||||
}
|
||||
else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
|
||||
if ($PassThruExitCode) {
|
||||
return $cmd.ExitCode
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
@@ -1577,65 +1603,182 @@ if ($null -ne $DriverSourcePath) {
|
||||
Write-Host "Installing drivers from WIM: $DriverSourcePath"
|
||||
$TempDriverDir = "W:\TempDrivers"
|
||||
try {
|
||||
# Create working folder for WIM-based drivers
|
||||
WriteLog "Creating temporary directory for drivers at $TempDriverDir"
|
||||
New-Item -Path $TempDriverDir -ItemType Directory -Force | Out-Null
|
||||
|
||||
# Mount the driver WIM read-only so DISM can recurse the extracted INF tree
|
||||
WriteLog "Mounting WIM contents to $TempDriverDir"
|
||||
Write-Host "Mounting WIM contents to $TempDriverDir"
|
||||
# For some reason can't use /mount-image with invoke-process, so using dism.exe directly
|
||||
dism.exe /Mount-Image /ImageFile:$DriverSourcePath /Index:1 /MountDir:$TempDriverDir /ReadOnly /optimize
|
||||
$mountExitCode = $LASTEXITCODE
|
||||
if ($mountExitCode -ne 0) {
|
||||
throw "DISM WIM mount failed. LastExitCode = $mountExitCode."
|
||||
}
|
||||
WriteLog "WIM mount successful."
|
||||
|
||||
# Inject drivers into the offline Windows image; failures here should not stop deployment
|
||||
WriteLog "Injecting drivers from $TempDriverDir"
|
||||
Write-Host "Injecting drivers from $TempDriverDir"
|
||||
Write-Host "This may take a while, please be patient."
|
||||
Invoke-Process dism.exe "/image:W:\ /Add-Driver /Driver:""$TempDriverDir"" /Recurse"
|
||||
WriteLog "Driver injection from WIM succeeded."
|
||||
Write-Host "Driver injection from WIM succeeded."
|
||||
$driverInjectExitCode = Invoke-Process -FilePath dism.exe -ArgumentList "/image:W:\ /Add-Driver /Driver:""$TempDriverDir"" /Recurse" -IgnoreExitCode -PassThruExitCode
|
||||
if ($driverInjectExitCode -ne 0) {
|
||||
$warningMessage = "Warning: One or more drivers failed to inject from WIM. ExitCode = $driverInjectExitCode. Continuing deployment."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
|
||||
# Copy setupapi.offline.log to the USB drive when driver injection fails
|
||||
|
||||
$setupApiLogPath = 'W:\Windows\INF\setupapi.offline.log'
|
||||
if (Test-Path -Path $setupApiLogPath) {
|
||||
try {
|
||||
Invoke-Process xcopy.exe """$setupApiLogPath"" ""$USBDrive"" /Y"
|
||||
}
|
||||
catch {
|
||||
WriteLog "An error occurred during WIM driver installation: $_"
|
||||
# Copy DISM log to USBDrive for debugging
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
throw $_
|
||||
WriteLog "Warning: Failed to copy setupapi.offline.log to $USBDrive. "
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: setupapi.offline.log not found at $setupApiLogPath"
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Driver injection from WIM succeeded."
|
||||
Write-Host "Driver injection from WIM succeeded."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$warningMessage = "Warning: An error occurred during WIM driver installation. Continuing deployment."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
|
||||
# Copy troubleshooting logs to the USB drive when driver installation fails
|
||||
try {
|
||||
Invoke-Process cmd.exe "/c copy /Y ""X:\Windows\logs\dism\dism.log"" ""$($USBDrive)dism_driverinject.log"""
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to copy dism.log to $USBDrive."
|
||||
}
|
||||
|
||||
$setupApiLogPath = 'W:\Windows\INF\setupapi.offline.log'
|
||||
if (Test-Path -Path $setupApiLogPath) {
|
||||
try {
|
||||
Invoke-Process xcopy.exe """$setupApiLogPath"" ""$USBDrive"" /Y"
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to copy setupapi.offline.log to $USBDrive."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: setupapi.offline.log not found at $setupApiLogPath"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -Path $TempDriverDir) {
|
||||
# Always attempt to unmount and clean up; unmount failures should not stop deployment
|
||||
WriteLog "Unmounting WIM from $TempDriverDir"
|
||||
Write-Host "Unmounting WIM from $TempDriverDir"
|
||||
try {
|
||||
Invoke-Process dism.exe "/Unmount-Image /MountDir:""$TempDriverDir"" /Discard"
|
||||
WriteLog "Unmount successful."
|
||||
Write-Host "Unmount successful."
|
||||
}
|
||||
catch {
|
||||
$warningMessage = "Warning: Failed to unmount WIM from $TempDriverDir. Continuing cleanup."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
WriteLog "Cleaning up temporary driver directory: $TempDriverDir"
|
||||
Write-Host "Cleaning up temporary driver directory: $TempDriverDir"
|
||||
try {
|
||||
Remove-Item -Path $TempDriverDir -Recurse -Force
|
||||
WriteLog "Cleanup successful."
|
||||
Write-Host "Cleanup successful."
|
||||
}
|
||||
catch {
|
||||
$warningMessage = "Warning: Failed to clean up temporary driver directory: $TempDriverDir."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($DriverSourceType -eq 'Folder') {
|
||||
$substMapping = $null
|
||||
try {
|
||||
# Use SUBST to shorten long paths for DISM /Add-Driver
|
||||
$substMapping = New-DriverSubstMapping -SourcePath $DriverSourcePath
|
||||
$shortDriverPath = $substMapping.DrivePath
|
||||
WriteLog "Injecting drivers from folder via SUBST. Source: $DriverSourcePath, Mapped: $($substMapping.DriveName)"
|
||||
Write-Host "Injecting drivers from folder: $shortDriverPath"
|
||||
Write-Host "This may take a while, please be patient."
|
||||
Invoke-Process dism.exe "/image:W:\ /Add-Driver /Driver:$shortDriverPath /Recurse"
|
||||
|
||||
# Inject drivers into the offline Windows image; failures here should not stop deployment
|
||||
$driverInjectExitCode = Invoke-Process -FilePath dism.exe -ArgumentList "/image:W:\ /Add-Driver /Driver:$shortDriverPath /Recurse" -IgnoreExitCode -PassThruExitCode
|
||||
if ($driverInjectExitCode -ne 0) {
|
||||
$warningMessage = "Warning: One or more drivers failed to inject from folder. ExitCode = $driverInjectExitCode. Continuing deployment."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
|
||||
# Copy setupapi.offline.log to the USB drive when driver injection fails
|
||||
$setupApiLogPath = 'W:\Windows\INF\setupapi.offline.log'
|
||||
if (Test-Path -Path $setupApiLogPath) {
|
||||
try {
|
||||
Invoke-Process xcopy.exe """$setupApiLogPath"" ""$USBDrive"" /Y"
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to copy setupapi.offline.log to $USBDrive. "
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: setupapi.offline.log not found at $setupApiLogPath"
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Driver injection from folder succeeded."
|
||||
Write-Host "Driver injection from folder succeeded."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
WriteLog "An error occurred during folder driver installation: $_"
|
||||
$warningMessage = "Warning: An error occurred during folder driver installation. Continuing deployment."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
|
||||
# Copy troubleshooting logs to the USB drive when driver installation fails
|
||||
try {
|
||||
Invoke-Process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
throw $_
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to copy dism.log to $USBDrive."
|
||||
}
|
||||
|
||||
$setupApiLogPath = 'W:\Windows\INF\setupapi.offline.log'
|
||||
if (Test-Path -Path $setupApiLogPath) {
|
||||
try {
|
||||
Invoke-Process xcopy.exe """$setupApiLogPath"" ""$USBDrive"" /Y"
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to copy setupapi.offline.log to $USBDrive."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: setupapi.offline.log not found at $setupApiLogPath"
|
||||
}
|
||||
}
|
||||
finally {
|
||||
# Always attempt to remove SUBST mapping; failures here should not stop deployment
|
||||
if ($null -ne $substMapping) {
|
||||
try {
|
||||
Remove-DriverSubstMapping -DriveLetter $substMapping.DriveLetter
|
||||
}
|
||||
catch {
|
||||
$warningMessage = "Warning: Failed to remove SUBST mapping $($substMapping.DriveLetter). Continuing deployment."
|
||||
WriteLog $warningMessage
|
||||
Write-Host $warningMessage -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user