mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Feat: Automate driver selection during FFU deployment
Adds a `DriverMapping.json` file to automate driver injection during image deployment. Driver download tasks now generate or update this mapping file with the relative path for each successfully downloaded driver package. The deployment script now uses this file to automatically detect and select the correct drivers for the target hardware, removing the need for manual selection. The manual driver selection prompt is retained as a fallback.
This commit is contained in:
@@ -3976,31 +3976,110 @@ if ($driversJsonPath -and (Test-Path $driversJsonPath) -and ($InstallDrivers -or
|
||||
-ListViewControl $null `
|
||||
-MainThreadLogPath $LogFile
|
||||
|
||||
# Log results from Invoke-ParallelProcessing
|
||||
# After processing, update the driver mapping file
|
||||
$successfullyDownloaded = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
if ($null -ne $parallelResults) {
|
||||
foreach ($result in $parallelResults) {
|
||||
if ($null -ne $result) {
|
||||
# The $result here is the direct output from the Save-*DriversTask
|
||||
# It should be a PSCustomObject with Identifier/Model, Status, Success
|
||||
$identifier = if ($result.PSObject.Properties.Name -contains 'Identifier') { $result.Identifier } elseif ($result.PSObject.Properties.Name -contains 'Model') { $result.Model } else { "UnknownItem" }
|
||||
$status = if ($result.PSObject.Properties.Name -contains 'Status') { $result.Status } else { "UnknownStatus" }
|
||||
$success = if ($result.PSObject.Properties.Name -contains 'Success') { $result.Success } else { $false }
|
||||
# Create a lookup table from the original items to get the 'Make'
|
||||
$makeLookup = @{}
|
||||
$driversToProcess | ForEach-Object { $makeLookup[$_.Model] = $_.Make }
|
||||
|
||||
$logMessage = "Driver task for '$identifier': Status: $status, Success: $success"
|
||||
WriteLog $logMessage
|
||||
if (-not $success) {
|
||||
Write-Warning $logMessage
|
||||
# Filter for objects that could be results, avoiding stray log strings
|
||||
foreach ($result in ($parallelResults | Where-Object { $_ -is [hashtable] })) {
|
||||
if ($null -eq $result) { continue }
|
||||
|
||||
# The result from Invoke-ParallelProcessing is a hashtable.
|
||||
# Access properties using their keys.
|
||||
$modelName = $result['Identifier']
|
||||
$resultCode = $result['ResultCode']
|
||||
$driverPath = $result['DriverPath']
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($modelName)) {
|
||||
WriteLog "Could not determine model name from result object: $($result | ConvertTo-Json -Compress -Depth 3)"
|
||||
continue
|
||||
}
|
||||
|
||||
if ($resultCode -eq 0 -and -not [string]::IsNullOrWhiteSpace($driverPath)) {
|
||||
# The task was successful and returned a driver path.
|
||||
$make = $makeLookup[$modelName]
|
||||
if ($make) {
|
||||
$successfullyDownloaded.Add([PSCustomObject]@{
|
||||
Make = $make
|
||||
Model = $modelName
|
||||
DriverPath = $driverPath
|
||||
})
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: Could not find 'Make' for successful download of model '$modelName'. Skipping from DriverMapping.json."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "A parallel driver task processed by Invoke-ParallelProcessing returned a null result."
|
||||
$logMessage = "Driver download failed or did not return a path for model '$modelName'. Status: $($result['Status'])"
|
||||
WriteLog $logMessage
|
||||
Write-Warning $logMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Invoke-ParallelProcessing returned null or no results."
|
||||
}
|
||||
|
||||
# Update the driver mapping JSON if there are any successful downloads
|
||||
if ($successfullyDownloaded.Count -gt 0) {
|
||||
try {
|
||||
WriteLog "Updating DriverMapping.json with $($successfullyDownloaded.Count) successfully downloaded drivers."
|
||||
Update-DriverMappingJson -DownloadedDrivers $successfullyDownloaded -DriversFolder $DriversFolder
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to update DriverMapping.json: $($_.Exception.Message)"
|
||||
# This is not a fatal error for the build process itself, so just show a warning.
|
||||
Write-Warning "The driver download process completed, but failed to update the DriverMapping.json file. Please check the log for details."
|
||||
}
|
||||
}
|
||||
WriteLog "Finished processing drivers from $driversJsonPath."
|
||||
|
||||
# After processing, update the driver mapping file
|
||||
$successfullyDownloaded = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
if ($null -ne $parallelResults) {
|
||||
# Create a lookup table from the original items to get the 'Make'
|
||||
$makeLookup = @{}
|
||||
$driversToProcess | ForEach-Object { $makeLookup[$_.Model] = $_.Make }
|
||||
|
||||
foreach ($result in $parallelResults) {
|
||||
if ($null -ne $result) {
|
||||
# Collect successful results for driver mapping
|
||||
if ($result.PSObject.Properties['Success'] -and $result.Success -and $result.PSObject.Properties['DriverPath'] -and -not [string]::IsNullOrWhiteSpace($result.DriverPath)) {
|
||||
$modelName = if ($result.PSObject.Properties.Name -contains 'Identifier') { $result.Identifier } else { $result.Model }
|
||||
|
||||
# Find the 'Make' from the original list
|
||||
$make = $makeLookup[$modelName]
|
||||
|
||||
if ($make) {
|
||||
$successfullyDownloaded.Add([PSCustomObject]@{
|
||||
Make = $make
|
||||
Model = $modelName
|
||||
DriverPath = $result.DriverPath
|
||||
})
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: Could not find 'Make' for successful download of model '$modelName'. Skipping from DriverMapping.json."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Update the driver mapping JSON if there are any successful downloads
|
||||
if ($successfullyDownloaded.Count -gt 0) {
|
||||
try {
|
||||
WriteLog "Updating DriverMapping.json with $($successfullyDownloaded.Count) successfully downloaded drivers."
|
||||
Update-DriverMappingJson -DownloadedDrivers $successfullyDownloaded -DriversFolder $DriversFolder
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Failed to update DriverMapping.json: $($_.Exception.Message)"
|
||||
# This is not a fatal error for the build process itself, so just show a warning.
|
||||
Write-Warning "The driver download process completed, but failed to update the DriverMapping.json file. Please check the log for details."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Existing single-model driver download logic
|
||||
|
||||
@@ -80,8 +80,95 @@ function Compress-DriverFolderToWim {
|
||||
}
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# SECTION: Driver Mapping Function
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
function Update-DriverMappingJson {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[array]$DownloadedDrivers, # Array of PSCustomObjects with Make, Model, DriverPath
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DriversFolder # Base drivers folder (e.g., C:\FFUDevelopment\Drivers)
|
||||
)
|
||||
|
||||
$mappingFilePath = Join-Path -Path $DriversFolder -ChildPath "DriverMapping.json"
|
||||
WriteLog "Updating driver mapping file at: $mappingFilePath"
|
||||
|
||||
# Load existing mapping file or create a new list
|
||||
$mappingList = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
if (Test-Path -Path $mappingFilePath -PathType Leaf) {
|
||||
try {
|
||||
$existingJson = Get-Content -Path $mappingFilePath -Raw | ConvertFrom-Json
|
||||
# Ensure it's a collection before adding to the list
|
||||
if ($existingJson -is [array]) {
|
||||
$mappingList.AddRange($existingJson)
|
||||
}
|
||||
else {
|
||||
$mappingList.Add($existingJson)
|
||||
}
|
||||
WriteLog "Loaded $($mappingList.Count) existing entries from $mappingFilePath"
|
||||
}
|
||||
catch {
|
||||
WriteLog "Warning: Could not read or parse existing DriverMapping.json. A new file will be created. Error: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
$updatedCount = 0
|
||||
$addedCount = 0
|
||||
|
||||
foreach ($driver in $DownloadedDrivers) {
|
||||
# Skip if any required property is missing or null
|
||||
if (-not $driver.PSObject.Properties['Make'] -or -not $driver.PSObject.Properties['Model'] -or -not $driver.PSObject.Properties['DriverPath'] -or [string]::IsNullOrWhiteSpace($driver.DriverPath)) {
|
||||
WriteLog "Skipping driver entry due to missing or empty Make, Model, or DriverPath. Details: $(($driver | ConvertTo-Json -Compress -Depth 3))"
|
||||
continue
|
||||
}
|
||||
|
||||
# Find existing entry
|
||||
$existingEntry = $mappingList | Where-Object { $_.Manufacturer -eq $driver.Make -and $_.Model -eq $driver.Model } | Select-Object -First 1
|
||||
|
||||
if ($null -ne $existingEntry) {
|
||||
# Update existing entry if the path is different
|
||||
if ($existingEntry.DriverPath -ne $driver.DriverPath) {
|
||||
WriteLog "Updating driver path for '$($driver.Make) - $($driver.Model)' from '$($existingEntry.DriverPath)' to '$($driver.DriverPath)'."
|
||||
$existingEntry.DriverPath = $driver.DriverPath
|
||||
$updatedCount++
|
||||
}
|
||||
}
|
||||
else {
|
||||
# Add new entry
|
||||
$newEntry = [PSCustomObject]@{
|
||||
Manufacturer = $driver.Make
|
||||
Model = $driver.Model
|
||||
DriverPath = $driver.DriverPath
|
||||
}
|
||||
$mappingList.Add($newEntry)
|
||||
WriteLog "Adding new mapping for '$($driver.Make) - $($driver.Model)' with path '$($driver.DriverPath)'."
|
||||
$addedCount++
|
||||
}
|
||||
}
|
||||
|
||||
if ($updatedCount -gt 0 -or $addedCount -gt 0) {
|
||||
try {
|
||||
# Sort the list for consistency before saving
|
||||
$sortedList = $mappingList | Sort-Object -Property Manufacturer, Model
|
||||
$sortedList | ConvertTo-Json -Depth 5 | Set-Content -Path $mappingFilePath -Encoding UTF8
|
||||
WriteLog "Successfully saved DriverMapping.json with $addedCount new entries and $updatedCount updated entries."
|
||||
}
|
||||
catch {
|
||||
WriteLog "Error saving updated DriverMapping.json: $($_.Exception.Message)"
|
||||
throw "Failed to save driver mapping file."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "No changes needed for DriverMapping.json."
|
||||
}
|
||||
}
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# SECTION: Module Export
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
Export-ModuleMember -Function Compress-DriverFolderToWim
|
||||
Export-ModuleMember -Function Compress-DriverFolderToWim, Update-DriverMappingJson
|
||||
@@ -276,11 +276,17 @@ function Invoke-ParallelProcessing {
|
||||
$localProgressQueue.Enqueue(@{ Identifier = $resultIdentifier; Status = $resultStatus })
|
||||
}
|
||||
|
||||
$driverPathValue = $null
|
||||
if ($null -ne $taskResult -and $taskResult.PSObject.Properties.Name -contains 'DriverPath') {
|
||||
$driverPathValue = $taskResult.DriverPath
|
||||
}
|
||||
|
||||
# Return a consistent hashtable structure (final result)
|
||||
return @{
|
||||
Identifier = $resultIdentifier
|
||||
Status = $resultStatus # Return the final status
|
||||
ResultCode = $resultCode
|
||||
DriverPath = $driverPathValue
|
||||
}
|
||||
|
||||
} -ThrottleLimit 5 -AsJob
|
||||
@@ -385,7 +391,6 @@ function Invoke-ParallelProcessing {
|
||||
}
|
||||
# Add the received result (even if format was unexpected, for logging)
|
||||
if ($null -ne $result) { $resultsCollection.Add($result) }
|
||||
break # Only process first result from this job
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -176,6 +176,7 @@ function Save-DellDriversTask {
|
||||
|
||||
$makeDriversPath = Join-Path -Path $DriversFolder -ChildPath $Make
|
||||
$modelPath = Join-Path -Path $makeDriversPath -ChildPath $modelName
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath $modelName # Relative path for the driver folder
|
||||
|
||||
try {
|
||||
# Define paths for Dell catalog. The catalog is assumed to be prepared by the calling function.
|
||||
@@ -190,7 +191,7 @@ function Save-DellDriversTask {
|
||||
$status = "Already downloaded"
|
||||
WriteLog "Drivers for '$modelName' already exist in '$modelPath'."
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $true }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $true; DriverPath = $driverRelativePath }
|
||||
}
|
||||
else {
|
||||
WriteLog "Driver folder '$modelPath' for '$modelName' exists but is empty/small. Re-downloading."
|
||||
@@ -635,6 +636,7 @@ function Save-DellDriversTask {
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
$wimFileName = "$($modelName).wim"
|
||||
$destinationWimPath = Join-Path -Path $makeDriversPath -ChildPath $wimFileName
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath $wimFileName # Update relative path to the WIM file
|
||||
WriteLog "Compressing '$modelPath' to '$destinationWimPath'..."
|
||||
try {
|
||||
$compressResult = Compress-DriverFolderToWim -SourceFolderPath $modelPath -DestinationWimPath $destinationWimPath -WimName $modelName -WimDescription $modelName -ErrorAction Stop
|
||||
@@ -667,14 +669,14 @@ function Save-DellDriversTask {
|
||||
# Enqueue the error status before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
# Ensure return object is created even on error
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success; DriverPath = $null }
|
||||
}
|
||||
|
||||
# Enqueue the final status (success or error) before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
|
||||
# Return the final status
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success; DriverPath = $driverRelativePath }
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
@@ -115,6 +115,7 @@ function Save-HPDriversTask {
|
||||
$hpDriversBaseFolder = Join-Path -Path $DriversFolder -ChildPath $make # Changed variable name for clarity
|
||||
$platformListXml = Join-Path -Path $hpDriversBaseFolder -ChildPath "PlatformList.xml"
|
||||
$modelSpecificFolder = Join-Path -Path $hpDriversBaseFolder -ChildPath ($modelName -replace '[\\/:"*?<>|]', '_') # Sanitize model name for folder path
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath ($modelName -replace '[\\/:"*?<>|]', '_') # Relative path for the driver folder
|
||||
$finalStatus = "" # Initialize final status
|
||||
$successState = $true # Assume success unless an operation fails
|
||||
|
||||
@@ -130,7 +131,7 @@ function Save-HPDriversTask {
|
||||
$errMsg = "Failed to create base HP driver folder '$hpDriversBaseFolder': $($_.Exception.Message)"
|
||||
WriteLog $errMsg
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status "Error: Create HP dir failed" }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = "Error: Create HP dir failed"; Success = $false }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = "Error: Create HP dir failed"; Success = $false; DriverPath = $null }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,10 +168,14 @@ function Save-HPDriversTask {
|
||||
$finalStatus = "Already downloaded"
|
||||
}
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $finalStatus }
|
||||
if ($CompressToWim) {
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath "$($identifier).wim"
|
||||
}
|
||||
return [PSCustomObject]@{
|
||||
Identifier = $identifier
|
||||
Status = $finalStatus
|
||||
Success = $successState
|
||||
DriverPath = $driverRelativePath
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,6 +381,7 @@ function Save-HPDriversTask {
|
||||
Compress-DriverFolderToWim -SourceFolderPath $modelSpecificFolder -DestinationWimPath $wimFilePath -WimName $identifier -WimDescription "Drivers for $identifier" -ErrorAction Stop
|
||||
WriteLog "Compression successful for '$identifier'."
|
||||
$finalStatus = "Completed & Compressed"
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath "$($identifier).wim" # Update relative path to the WIM
|
||||
}
|
||||
catch {
|
||||
WriteLog "Error during compression for '$identifier': $($_.Exception.Message)"
|
||||
@@ -389,6 +395,7 @@ function Save-HPDriversTask {
|
||||
WriteLog $errorMessage
|
||||
$finalStatus = "Error: $($_.Exception.Message.Split([Environment]::NewLine)[0])"
|
||||
$successState = $false
|
||||
$driverRelativePath = $null # Ensure path is null on error
|
||||
if (Test-Path -Path $modelSpecificFolder -PathType Container) {
|
||||
WriteLog "Attempting to remove partially created folder $modelSpecificFolder due to error."
|
||||
Remove-Item -Path $modelSpecificFolder -Recurse -Force -ErrorAction SilentlyContinue
|
||||
@@ -396,7 +403,7 @@ function Save-HPDriversTask {
|
||||
}
|
||||
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $finalStatus }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $finalStatus; Success = $successState }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $finalStatus; Success = $successState; DriverPath = $driverRelativePath }
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
@@ -90,6 +90,7 @@ function Save-LenovoDriversTask {
|
||||
$makeDriversPath = Join-Path -Path $DriversFolder -ChildPath $Make
|
||||
# Use the identifier (which contains the model name and machine type) and sanitize it for the path
|
||||
$modelPath = Join-Path -Path $makeDriversPath -ChildPath ($identifier -replace '[\\/:"*?<>|]', '_')
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath ($identifier -replace '[\\/:"*?<>|]', '_') # Relative path for the driver folder
|
||||
$tempDownloadPath = Join-Path -Path $makeDriversPath -ChildPath "_TEMP_$($machineType)_$($PID)" # Temp folder for catalog/package XMLs
|
||||
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status "Checking..." }
|
||||
@@ -102,7 +103,7 @@ function Save-LenovoDriversTask {
|
||||
$status = "Already downloaded"
|
||||
WriteLog "Drivers for '$identifier' already exist in '$modelPath'."
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $true }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $true; DriverPath = $driverRelativePath }
|
||||
}
|
||||
else {
|
||||
WriteLog "Driver folder '$modelPath' for '$identifier' exists but is empty/small. Re-downloading."
|
||||
@@ -380,6 +381,7 @@ function Save-LenovoDriversTask {
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
||||
$wimFileName = "$($identifier).wim" # Use sanitized identifier for filename
|
||||
$destinationWimPath = Join-Path -Path $makeDriversPath -ChildPath $wimFileName
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath $wimFileName # Update relative path to the WIM file
|
||||
WriteLog "Compressing '$modelPath' to '$destinationWimPath'..."
|
||||
try {
|
||||
$compressResult = Compress-DriverFolderToWim -SourceFolderPath $modelPath -DestinationWimPath $destinationWimPath -WimName $identifier -WimDescription $identifier -ErrorAction Stop
|
||||
@@ -412,7 +414,7 @@ function Save-LenovoDriversTask {
|
||||
# Enqueue the error status before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
||||
# Ensure return object is created even on error
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success; DriverPath = $null }
|
||||
}
|
||||
finally {
|
||||
# Clean up the main catalog XML and temp folder
|
||||
@@ -424,7 +426,7 @@ function Save-LenovoDriversTask {
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
||||
|
||||
# Return the final status
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success; DriverPath = $driverRelativePath }
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
@@ -94,6 +94,7 @@ function Save-MicrosoftDriversTask {
|
||||
$modelName = $DriverItemData.Model
|
||||
$modelLink = $DriverItemData.Link
|
||||
$make = $DriverItemData.Make
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath $modelName # Relative path for the driver folder
|
||||
$status = "Getting download link..." # Initial local status
|
||||
$success = $false
|
||||
|
||||
@@ -112,7 +113,7 @@ function Save-MicrosoftDriversTask {
|
||||
# Enqueue this status before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
# Return success immediately
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $true }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $true; DriverPath = $driverRelativePath }
|
||||
}
|
||||
else {
|
||||
# Status is not set to error here, just log and continue
|
||||
@@ -352,6 +353,7 @@ function Save-MicrosoftDriversTask {
|
||||
$wimFileName = "$($modelName).wim"
|
||||
# Corrected WIM path: WIM file should be next to the model folder, not inside it.
|
||||
$destinationWimPath = Join-Path -Path $makeDriversPath -ChildPath $wimFileName
|
||||
$driverRelativePath = Join-Path -Path $make -ChildPath $wimFileName # Update relative path to the WIM file
|
||||
WriteLog "Compressing '$modelPath' to '$destinationWimPath'..."
|
||||
try {
|
||||
# Use the function from the imported common module
|
||||
@@ -398,14 +400,14 @@ function Save-MicrosoftDriversTask {
|
||||
# Enqueue the error status before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
# Ensure return object is created even on error
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success; DriverPath = $null }
|
||||
}
|
||||
|
||||
# Enqueue the final status (success or error) before returning
|
||||
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $modelName -Status $status }
|
||||
|
||||
# Return the final status (this is still used by Receive-Job for final confirmation)
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success }
|
||||
return [PSCustomObject]@{ Model = $modelName; Status = $status; Success = $success; DriverPath = $driverRelativePath }
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function *
|
||||
@@ -624,7 +624,7 @@ function Invoke-DownloadSelectedDrivers {
|
||||
CompressToWim = $compressDrivers
|
||||
}
|
||||
|
||||
Invoke-ParallelProcessing -ItemsToProcess $selectedDrivers `
|
||||
$parallelResults = Invoke-ParallelProcessing -ItemsToProcess $selectedDrivers `
|
||||
-ListViewControl $State.Controls.lstDriverModels `
|
||||
-IdentifierProperty 'Model' `
|
||||
-StatusProperty 'DownloadStatus' `
|
||||
@@ -636,14 +636,62 @@ function Invoke-DownloadSelectedDrivers {
|
||||
-MainThreadLogPath $State.LogFilePath
|
||||
|
||||
$overallSuccess = $true
|
||||
# Check if any item has an error status after processing
|
||||
# We iterate over $State.Controls.lstDriverModels.Items because their DownloadStatus property was updated by Invoke-ParallelProcessing
|
||||
foreach ($item in ($State.Controls.lstDriverModels.Items | Where-Object { $_.IsSelected })) {
|
||||
# Check only originally selected items
|
||||
if ($item.DownloadStatus -like 'Error:*') {
|
||||
$successfullyDownloaded = [System.Collections.Generic.List[PSCustomObject]]::new()
|
||||
|
||||
# Check the results from the parallel processing tasks
|
||||
if ($null -ne $parallelResults) {
|
||||
# Create a lookup from the original selected drivers to get the 'Make' property,
|
||||
# as the result object might only have 'Identifier' or 'Model'.
|
||||
$makeLookup = @{}
|
||||
$selectedDrivers | ForEach-Object { $makeLookup[$_.Model] = $_.Make }
|
||||
|
||||
# Filter for objects that could be results, avoiding stray log strings
|
||||
foreach ($result in ($parallelResults | Where-Object { $_ -is [hashtable] })) {
|
||||
if ($null -eq $result) { continue }
|
||||
|
||||
# The result from Invoke-ParallelProcessing is a hashtable.
|
||||
# Access properties using their keys.
|
||||
$modelName = $result['Identifier']
|
||||
$resultCode = $result['ResultCode']
|
||||
$driverPath = $result['DriverPath']
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($modelName)) {
|
||||
WriteLog "Could not determine model name from result object: $($result | ConvertTo-Json -Compress -Depth 3)"
|
||||
$overallSuccess = $false
|
||||
WriteLog "Error detected for model $($item.Model) (Make: $($item.Make)): $($item.DownloadStatus)"
|
||||
# No break here, log all errors
|
||||
continue
|
||||
}
|
||||
|
||||
if ($resultCode -ne 0) {
|
||||
$overallSuccess = $false
|
||||
WriteLog "Error detected for model $modelName."
|
||||
}
|
||||
elseif (-not [string]::IsNullOrWhiteSpace($driverPath)) {
|
||||
# The task was successful and returned a driver path.
|
||||
$make = $makeLookup[$modelName]
|
||||
if ($make) {
|
||||
$successfullyDownloaded.Add([PSCustomObject]@{
|
||||
Make = $make
|
||||
Model = $modelName
|
||||
DriverPath = $driverPath
|
||||
})
|
||||
}
|
||||
else {
|
||||
WriteLog "Warning: Could not find 'Make' for successful download of model '$modelName'. Skipping from DriverMapping.json."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Update the driver mapping JSON if there are any successful downloads
|
||||
if ($successfullyDownloaded.Count -gt 0) {
|
||||
try {
|
||||
WriteLog "Updating DriverMapping.json with $($successfullyDownloaded.Count) successfully downloaded drivers."
|
||||
Update-DriverMappingJson -DownloadedDrivers $successfullyDownloaded -DriversFolder $localDriversFolder
|
||||
}
|
||||
catch {
|
||||
WriteLog "Failed to update DriverMapping.json: $($_.Exception.Message)"
|
||||
# This is not a fatal error for the download process itself, so just show a warning.
|
||||
[System.Windows.MessageBox]::Show("The driver download process completed, but failed to update the DriverMapping.json file. Please check the log for details.", "Driver Mapping Error", "OK", "Warning")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,19 +111,22 @@ function Invoke-Process {
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
@@ -296,7 +299,8 @@ elseif($Unattend -and $UnattendComputerName){
|
||||
[string]$computername = $SCName.ComputerName
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Writelog 'No matching serial number found in SerialComputerNames.csv. Setting random computer name to complete setup.'
|
||||
[string]$computername = ("FFU-" + ( -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 11 | ForEach-Object { [char]$_ })))
|
||||
$computername = Set-Computername($computername)
|
||||
@@ -412,9 +416,70 @@ else {
|
||||
$DriversPath = $USBDrive + "Drivers"
|
||||
$DriverSourcePath = $null
|
||||
$DriverSourceType = $null # Will be 'WIM' or 'Folder'
|
||||
$driverMappingPath = Join-Path -Path $DriversPath -ChildPath "DriverMapping.json"
|
||||
|
||||
If (Test-Path -Path $DriversPath)
|
||||
{
|
||||
# --- Automatic Driver Detection using DriverMapping.json ---
|
||||
if (Test-Path -Path $driverMappingPath -PathType Leaf) {
|
||||
WriteLog "DriverMapping.json found at $driverMappingPath. Attempting automatic driver selection."
|
||||
try {
|
||||
# Get system information
|
||||
$systemManufacturer = (Get-CimInstance -Class Win32_ComputerSystem).Manufacturer
|
||||
# Lenovo uses a different property for the model name
|
||||
$systemModel = if ($systemManufacturer -like '*LENOVO*') {
|
||||
(Get-CimInstance -Class Win32_ComputerSystemProduct).Version
|
||||
}
|
||||
else {
|
||||
(Get-CimInstance -Class Win32_ComputerSystem).Model
|
||||
}
|
||||
WriteLog "Detected System: Manufacturer='$systemManufacturer', Model='$systemModel'"
|
||||
|
||||
# Load and parse the mapping file
|
||||
$driverMappings = Get-Content -Path $driverMappingPath -Raw | ConvertFrom-Json
|
||||
|
||||
# Find a matching rule
|
||||
$matchedRule = $null
|
||||
foreach ($rule in $driverMappings) {
|
||||
# Use -like for wildcard matching
|
||||
if ($systemManufacturer -like "$($rule.Manufacturer)*" -and $systemModel -like "$($rule.Model)*") {
|
||||
$matchedRule = $rule
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -ne $matchedRule) {
|
||||
WriteLog "Automatic match found: Manufacturer='$($matchedRule.Manufacturer)', Model='$($matchedRule.Model)'"
|
||||
$potentialDriverPath = Join-Path -Path $DriversPath -ChildPath $matchedRule.DriverPath
|
||||
|
||||
if (Test-Path -Path $potentialDriverPath) {
|
||||
$DriverSourcePath = $potentialDriverPath
|
||||
# Determine if it's a WIM or a Folder
|
||||
if ($DriverSourcePath -like '*.wim') {
|
||||
$DriverSourceType = 'WIM'
|
||||
}
|
||||
else {
|
||||
$DriverSourceType = 'Folder'
|
||||
}
|
||||
WriteLog "Automatically selected driver source. Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
}
|
||||
else {
|
||||
WriteLog "Matched driver path '$potentialDriverPath' not found. Falling back to manual selection."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "No matching driver rule found in DriverMapping.json for this system. Falling back to manual selection."
|
||||
}
|
||||
}
|
||||
catch {
|
||||
WriteLog "An error occurred during automatic driver detection: $($_.Exception.Message). Falling back to manual selection."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "DriverMapping.json not found. Proceeding with manual driver selection."
|
||||
}
|
||||
|
||||
# --- Manual Driver Selection (Fallback) ---
|
||||
if ($null -eq $DriverSourcePath) {
|
||||
If (Test-Path -Path $DriversPath) {
|
||||
WriteLog "Searching for driver WIMs and folders in $DriversPath"
|
||||
|
||||
# Get all WIM files
|
||||
@@ -446,7 +511,8 @@ If (Test-Path -Path $DriversPath)
|
||||
$DriverSourcePath = $DriverSources[0].Path
|
||||
$DriverSourceType = $DriverSources[0].Type
|
||||
WriteLog "Single driver source found. Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
# Multiple sources found, prompt user
|
||||
WriteLog "Multiple driver sources found. Prompting for selection."
|
||||
$displayArray = @()
|
||||
@@ -464,7 +530,8 @@ If (Test-Path -Path $DriversPath)
|
||||
$var = $true
|
||||
[int]$DriverSelected = Read-Host 'Enter the number of the driver source to install'
|
||||
$DriverSelected = $DriverSelected - 1
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid number.'
|
||||
$var = $false
|
||||
}
|
||||
@@ -474,12 +541,15 @@ If (Test-Path -Path $DriversPath)
|
||||
$DriverSourceType = $DriverSources[$DriverSelected].Type
|
||||
WriteLog "User selected Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
WriteLog "No driver WIMs or folders found in Drivers directory."
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
WriteLog "Drivers folder not found at $DriversPath. Skipping driver installation."
|
||||
}
|
||||
}
|
||||
#Partition drive
|
||||
Writelog 'Clean Disk'
|
||||
try {
|
||||
@@ -539,8 +609,7 @@ Get-Disk | Where-Object Number -eq $DiskID | Get-Partition | Where-Object Partit
|
||||
|
||||
#Copy modified WinRE if folder exists, else copy inbox WinRE
|
||||
$WinRE = $USBDrive + "WinRE\winre.wim"
|
||||
If (Test-Path -Path $WinRE)
|
||||
{
|
||||
If (Test-Path -Path $WinRE) {
|
||||
WriteLog 'Copying modified WinRE to Recovery directory'
|
||||
Get-Disk | Where-Object Number -eq $DiskID | Get-Partition | Where-Object Type -eq Recovery | Set-Partition -NewDriveLetter R
|
||||
Invoke-Process xcopy.exe "/h $WinRE R:\Recovery\WindowsRE\ /Y"
|
||||
@@ -625,25 +694,29 @@ if ($null -ne $DriverSourcePath) {
|
||||
Invoke-Process dism.exe "/image:W:\ /Add-Driver /Driver:""$TempDriverDir"" /Recurse"
|
||||
WriteLog "Driver injection from WIM succeeded."
|
||||
|
||||
} catch {
|
||||
}
|
||||
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 $_
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (Test-Path -Path $TempDriverDir) {
|
||||
WriteLog "Cleaning up temporary driver directory: $TempDriverDir"
|
||||
Remove-Item -Path $TempDriverDir -Recurse -Force
|
||||
WriteLog "Cleanup successful."
|
||||
}
|
||||
}
|
||||
} elseif ($DriverSourceType -eq 'Folder') {
|
||||
}
|
||||
elseif ($DriverSourceType -eq 'Folder') {
|
||||
WriteLog "Injecting drivers from folder: $DriverSourcePath"
|
||||
Write-Warning 'Copying Drivers - dism will pop a window with no progress. This can take a few minutes to complete. This is done so drivers are logged to the scriptlog.txt file. Please be patient.'
|
||||
Invoke-Process dism.exe "/image:W:\ /Add-Driver /Driver:""$DriverSourcePath"" /Recurse"
|
||||
WriteLog "Driver injection from folder succeeded."
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
WriteLog "No drivers to install."
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user