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:
rbalsleyMSFT
2025-06-26 17:45:31 -07:00
parent 40fd739b2c
commit 98c5946efd
9 changed files with 532 additions and 227 deletions
@@ -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
@@ -383,10 +389,9 @@ function Invoke-ParallelProcessing {
$finalStatus = "$ErrorStatusPrefix Invalid Result Format"
$processedCount++ # Count as processed to avoid loop issues
}
# 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
}
# Add the received result (even if format was unexpected, for logging)
if ($null -ne $result) { $resultsCollection.Add($result) }
}
}
else {
# Job completed but had no data