private/steps/5-drivers/step-Add-WindowsDriver-DriverFolder.ps1
|
<#
.SYNOPSIS Applies offline drivers from a local driver folder to the Windows image. .DESCRIPTION Validates the configured driver folder paths and, when present, uses Add-WindowsDriver to inject all drivers recursively into the offline Windows installation at C:. If no driver folders are configured or no paths exist, the step exits without error. When selected removable media changes drive letters, the step re-resolves each saved driver folder by scanning current file system drives for the same relative path. .PARAMETER DriverFolderPath Path(s) to folders that contain driver INF files and subfolders. Absolute paths are re-resolved by their OSDCloud\Drivers relative suffix when the original drive letter is no longer valid. .EXAMPLE step-Add-WindowsDriver-DriverFolder -DriverFolderPath 'D:\DriverPack' Injects drivers from D:\DriverPack into the offline Windows image. .NOTES Internal workflow step used by OSDCloud deployment tasks. #> function step-Add-WindowsDriver-DriverFolder { [CmdletBinding()] param ( [System.String[]] $DriverFolderPath ) #================================================= $startMessage = "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Start" Write-Debug -Message $startMessage; Write-Verbose -Message $startMessage #================================================= $Error.Clear() $logPath = 'C:\Windows\Temp\osdcloud-logs' $offlinePath = 'C:\' $dismLogPath = Join-Path -Path $logPath -ChildPath 'dism-add-windowsdriver-driverfolder.log' $volumeMetadataCache = @{} Write-Verbose "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] DriverFolderPath count: $(@($DriverFolderPath).Count); LogPath: $logPath; OfflinePath: $offlinePath" function Get-DriveRootFromPath { param( [Parameter()] [string]$Path ) if ([string]::IsNullOrWhiteSpace($Path)) { return $null } $driveRootMatch = [System.Text.RegularExpressions.Regex]::Match($Path, '^[A-Z]:\\') if ($driveRootMatch.Success) { return $driveRootMatch.Value } return $null } function Get-DriverFolderRelativePath { param( [Parameter()] [string]$Path ) if ([string]::IsNullOrWhiteSpace($Path)) { return $null } $relativePathMatch = [System.Text.RegularExpressions.Regex]::Match($Path, '(?i)(OSDCloud\\Drivers(?:\\.*)?)$') if ($relativePathMatch.Success) { return $relativePathMatch.Groups[1].Value } return $null } function Get-DriveVolumeMetadata { param( [Parameter()] [string]$Path ) $driveRoot = Get-DriveRootFromPath -Path $Path if ([string]::IsNullOrWhiteSpace($driveRoot)) { return [PSCustomObject]@{ DriveRoot = $null VolumeLabel = $null VolumeUniqueId = $null } } if ($volumeMetadataCache.ContainsKey($driveRoot)) { return $volumeMetadataCache[$driveRoot] } $volume = $null try { $volume = Get-Volume -DriveLetter $driveRoot.Substring(0, 1) -ErrorAction Stop } catch { $volume = $null } $volumeMetadata = [PSCustomObject]@{ DriveRoot = $driveRoot VolumeLabel = if ($volume) { [string]$volume.FileSystemLabel } else { $null } VolumeUniqueId = if ($volume) { [string]$volume.UniqueId } else { $null } } $volumeMetadataCache[$driveRoot] = $volumeMetadata return $volumeMetadata } function Resolve-DriverFolderPath { param( [Parameter()] $Selection, [Parameter()] [string]$FallbackPath ) $originalPath = $FallbackPath if ($Selection -and -not [string]::IsNullOrWhiteSpace([string]$Selection.Path)) { $originalPath = [string]$Selection.Path } if (-not [string]::IsNullOrWhiteSpace($originalPath) -and (Test-Path -LiteralPath $originalPath -PathType Container)) { return $originalPath } $relativePath = $null if ($Selection -and -not [string]::IsNullOrWhiteSpace([string]$Selection.RelativePath)) { $relativePath = [string]$Selection.RelativePath } if ([string]::IsNullOrWhiteSpace($relativePath)) { $relativePath = Get-DriverFolderRelativePath -Path $originalPath } if ([string]::IsNullOrWhiteSpace($relativePath)) { Write-Warning "[$(Get-Date -format s)] Unable to determine a relative driver folder path for $originalPath" return $null } $candidatePaths = @( Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Root -match '^[A-Z]:\\$' } | ForEach-Object { $candidatePath = Join-Path -Path $_.Root -ChildPath $relativePath if (Test-Path -LiteralPath $candidatePath -PathType Container) { $candidatePath } } | Sort-Object -Unique ) if (-not $candidatePaths -or $candidatePaths.Count -eq 0) { Write-Verbose "[$(Get-Date -format s)] No current drives contain relative driver folder path $relativePath" Write-Warning "[$(Get-Date -format s)] DriverFolderPath was not found: $originalPath" return $null } if ($candidatePaths.Count -eq 1) { Write-Verbose "[$(Get-Date -format s)] Resolved driver folder candidate: $($candidatePaths[0])" return $candidatePaths[0] } $preferredPaths = @() $storedVolumeUniqueId = if ($Selection) { [string]$Selection.VolumeUniqueId } else { $null } $storedVolumeLabel = if ($Selection) { [string]$Selection.VolumeLabel } else { $null } if (-not [string]::IsNullOrWhiteSpace($storedVolumeUniqueId)) { $preferredPaths = @($candidatePaths | Where-Object { [string](Get-DriveVolumeMetadata -Path $_).VolumeUniqueId -eq $storedVolumeUniqueId }) } if ($preferredPaths.Count -eq 0 -and -not [string]::IsNullOrWhiteSpace($storedVolumeLabel)) { $preferredPaths = @($candidatePaths | Where-Object { [string](Get-DriveVolumeMetadata -Path $_).VolumeLabel -eq $storedVolumeLabel }) } if ($preferredPaths.Count -eq 1) { return $preferredPaths[0] } Write-Warning "[$(Get-Date -format s)] Multiple matching driver folders were found for $originalPath. Skipping this folder to avoid selecting the wrong drive." return $null } $validDriverFolderPaths = @($DriverFolderPath | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) } | Select-Object -Unique) if (-not $validDriverFolderPaths -or $validDriverFolderPaths.Count -eq 0) { Write-Verbose "[$(Get-Date -format s)] DriverFolderPath is not set. Skipping driver injection." return } $selectionLookup = @{} $driverFolderSelections = @($validDriverFolderPaths | ForEach-Object { $driverPath = [string]$_ if ($selectionLookup.ContainsKey($driverPath)) { $selectionLookup[$driverPath] } else { [PSCustomObject]@{ Path = $driverPath RelativePath = Get-DriverFolderRelativePath -Path $driverPath DriveRoot = Get-DriveRootFromPath -Path $driverPath VolumeLabel = $null VolumeUniqueId = $null } } }) $resolvedDriverFolderPaths = @() foreach ($selection in $driverFolderSelections) { $originalPath = [string]$selection.Path $resolvedPath = Resolve-DriverFolderPath -Selection $selection -FallbackPath $originalPath if ([string]::IsNullOrWhiteSpace($resolvedPath)) { continue } if ($resolvedDriverFolderPaths -notcontains $resolvedPath) { if ($resolvedPath -ne $originalPath) { Write-Verbose "[$(Get-Date -format s)] Resolved driver folder path from $originalPath to $resolvedPath" } $resolvedDriverFolderPaths += $resolvedPath } } if (-not $resolvedDriverFolderPaths -or $resolvedDriverFolderPaths.Count -eq 0) { Write-Verbose "[$(Get-Date -format s)] No valid driver folders were resolved. Skipping driver injection." return } if (-not (Test-Path -LiteralPath $logPath)) { Write-Verbose "[$(Get-Date -format s)] Creating driver folder DISM log path: $logPath" New-Item -ItemType Directory -Path $logPath -Force | Out-Null } foreach ($driverPath in $resolvedDriverFolderPaths) { Write-Verbose "[$(Get-Date -format s)] Applying drivers from $driverPath" try { Add-WindowsDriver -Path $offlinePath -Driver $driverPath -Recurse -ForceUnsigned ` -LogPath $dismLogPath ` -ErrorAction Stop | Out-Null } catch { Write-Warning "[$(Get-Date -format s)] Add-WindowsDriver failed for $driverPath. $($_.Exception.Message)" } } #================================================= $endMessage = "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] End" Write-Verbose -Message $endMessage; Write-Debug -Message $endMessage #================================================= } |