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:
@@ -1,10 +1,10 @@
|
||||
function Get-USBDrive(){
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS'}).DriveLetter
|
||||
if ($null -eq $USBDriveLetter){
|
||||
function Get-USBDrive() {
|
||||
$USBDriveLetter = (Get-Volume | Where-Object { $_.DriveType -eq 'Removable' -and $_.FileSystemType -eq 'NTFS' }).DriveLetter
|
||||
if ($null -eq $USBDriveLetter) {
|
||||
#Must be using a fixed USB drive - difficult to grab drive letter from win32_diskdrive. Assume user followed instructions and used Deploy as the friendly name for partition
|
||||
$USBDriveLetter = (Get-Volume | Where-Object {$_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy'}).DriveLetter
|
||||
$USBDriveLetter = (Get-Volume | Where-Object { $_.DriveType -eq 'Fixed' -and $_.FileSystemType -eq 'NTFS' -and $_.FileSystemLabel -eq 'Deploy' }).DriveLetter
|
||||
#If we didn't get the drive letter, stop the script.
|
||||
if ($null -eq $USBDriveLetter){
|
||||
if ($null -eq $USBDriveLetter) {
|
||||
WriteLog 'Cannot find USB drive letter - most likely using a fixed USB drive. Name the 2nd partition with the FFU files as Deploy so the script can grab the drive letter. Exiting'
|
||||
Exit
|
||||
}
|
||||
@@ -14,46 +14,46 @@ function Get-USBDrive(){
|
||||
return $USBDriveLetter
|
||||
}
|
||||
|
||||
function Get-HardDrive(){
|
||||
function Get-HardDrive() {
|
||||
$SystemInfo = Get-WmiObject -Class 'Win32_ComputerSystem'
|
||||
$Manufacturer = $SystemInfo.Manufacturer
|
||||
$Model = $SystemInfo.Model
|
||||
WriteLog "Device Manufacturer: $Manufacturer"
|
||||
WriteLog "Device Model: $Model"
|
||||
WriteLog 'Getting Hard Drive info'
|
||||
if ($Manufacturer -eq 'Microsoft Corporation' -and $Model -eq 'Virtual Machine'){
|
||||
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'
|
||||
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' `
|
||||
-and $_.Model -eq 'Microsoft Virtual Disk' `
|
||||
-and $_.Index -eq 0 `
|
||||
-and $_.SCSILogicalUnit -eq 0
|
||||
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object { $_.MediaType -eq 'Fixed hard disk media' `
|
||||
-and $_.Model -eq 'Microsoft Virtual Disk' `
|
||||
-and $_.Index -eq 0 `
|
||||
-and $_.SCSILogicalUnit -eq 0
|
||||
}
|
||||
}
|
||||
else{
|
||||
else {
|
||||
WriteLog 'Not running in a VM. Getting physical disk drive'
|
||||
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object {$_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk'}
|
||||
$DiskDrive = Get-WmiObject -Class 'Win32_DiskDrive' | Where-Object { $_.MediaType -eq 'Fixed hard disk media' -and $_.Model -ne 'Microsoft Virtual Disk' }
|
||||
}
|
||||
$DeviceID = $DiskDrive.DeviceID
|
||||
$BytesPerSector = $Diskdrive.BytesPerSector
|
||||
|
||||
# Create a custom object to return both values
|
||||
$result = New-Object PSObject -Property @{
|
||||
DeviceID = $DeviceID
|
||||
DeviceID = $DeviceID
|
||||
BytesPerSector = $BytesPerSector
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
function WriteLog($LogText){
|
||||
function WriteLog($LogText) {
|
||||
Add-Content -path $LogFile -value "$((Get-Date).ToString()) $LogText"
|
||||
}
|
||||
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile,$DiskID){
|
||||
function Set-DiskpartAnswerFiles($DiskpartFile, $DiskID) {
|
||||
(Get-Content $DiskpartFile).Replace('disk 0', "disk $DiskID") | Set-Content -Path $DiskpartFile
|
||||
}
|
||||
|
||||
function Set-Computername($computername){
|
||||
function Set-Computername($computername) {
|
||||
[xml]$xml = Get-Content $UnattendFile
|
||||
$components = $xml.unattend.settings.component
|
||||
$found = $false
|
||||
@@ -73,60 +73,63 @@ function Set-Computername($computername){
|
||||
}
|
||||
|
||||
function Invoke-Process {
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param
|
||||
(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$FilePath,
|
||||
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
[Parameter()]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$ArgumentList
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
try {
|
||||
$stdOutTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
$stdErrTempFile = "$env:TEMP\$((New-Guid).Guid)"
|
||||
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
$startProcessParams = @{
|
||||
FilePath = $FilePath
|
||||
ArgumentList = $ArgumentList
|
||||
RedirectStandardError = $stdErrTempFile
|
||||
RedirectStandardOutput = $stdOutTempFile
|
||||
Wait = $true;
|
||||
PassThru = $true;
|
||||
NoNewWindow = $false;
|
||||
}
|
||||
if ($PSCmdlet.ShouldProcess("Process [$($FilePath)]", "Run with args: [$($ArgumentList)]")) {
|
||||
$cmd = Start-Process @startProcessParams
|
||||
$cmdOutput = Get-Content -Path $stdOutTempFile -Raw
|
||||
$cmdError = Get-Content -Path $stdErrTempFile -Raw
|
||||
if ($cmd.ExitCode -ne 0) {
|
||||
if ($cmdError) {
|
||||
throw $cmdError.Trim()
|
||||
}
|
||||
if ($cmdOutput) {
|
||||
throw $cmdOutput.Trim()
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ([string]::IsNullOrEmpty($cmdOutput) -eq $false) {
|
||||
WriteLog $cmdOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
#$PSCmdlet.ThrowTerminatingError($_)
|
||||
WriteLog $_
|
||||
Write-Host 'Script failed - check scriptlog.txt on the USB drive for more info'
|
||||
throw $_
|
||||
throw $_
|
||||
|
||||
} finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
}
|
||||
finally {
|
||||
Remove-Item -Path $stdOutTempFile, $stdErrTempFile -Force -ErrorAction Ignore
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -142,7 +145,7 @@ WriteLog "Script version: $version"
|
||||
#Find PhysicalDrive
|
||||
# $PhysicalDeviceID = Get-HardDrive
|
||||
$hardDrive = Get-HardDrive
|
||||
if($null -eq $hardDrive){
|
||||
if ($null -eq $hardDrive) {
|
||||
WriteLog 'No hard drive found. Exiting'
|
||||
WriteLog 'Try adding storage drivers to the PE boot image (you can re-create your FFU and USB drive and add the PE drivers to the PEDrivers folder and add -CopyPEDrivers $true to the command line, or manually add them via DISM)'
|
||||
Exit
|
||||
@@ -153,7 +156,7 @@ WriteLog "Physical BytesPerSector is $BytesPerSector"
|
||||
WriteLog "Physical DeviceID is $PhysicalDeviceID"
|
||||
|
||||
#Parse DiskID Number
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1,1)
|
||||
$DiskID = $PhysicalDeviceID.substring($PhysicalDeviceID.length - 1, 1)
|
||||
WriteLog "DiskID is $DiskID"
|
||||
|
||||
#Find FFU Files
|
||||
@@ -165,8 +168,8 @@ If ($FFUCount -gt 1) {
|
||||
WriteLog "Found $FFUCount FFU Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $FFUCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName}
|
||||
for ($i = 0; $i -le $FFUCount - 1; $i++) {
|
||||
$Properties = [ordered]@{Number = $i + 1 ; FFUFile = $FFUFiles[$i].FullName }
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, FFUFile
|
||||
@@ -174,14 +177,14 @@ If ($FFUCount -gt 1) {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$FFUSelected = Read-Host 'Enter the FFU number to install'
|
||||
$FFUSelected = $FFUSelected -1
|
||||
$FFUSelected = $FFUSelected - 1
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid FFU number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($FFUSelected -le $FFUCount -1) -and $var)
|
||||
} until (($FFUSelected -le $FFUCount - 1) -and $var)
|
||||
|
||||
$FFUFileToInstall = $array[$FFUSelected].FFUFile
|
||||
WriteLog "$FFUFileToInstall was selected"
|
||||
@@ -199,22 +202,22 @@ else {
|
||||
|
||||
#FindAP
|
||||
$APFolder = $USBDrive + "Autopilot\"
|
||||
If (Test-Path -Path $APFolder){
|
||||
If (Test-Path -Path $APFolder) {
|
||||
[array]$APFiles = @(Get-ChildItem -Path $APFolder*.json)
|
||||
$APFilesCount = $APFiles.Count
|
||||
if ($APFilesCount -ge 1){
|
||||
$autopilot = $true
|
||||
if ($APFilesCount -ge 1) {
|
||||
$autopilot = $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#FindPPKG
|
||||
$PPKGFolder = $USBDrive + "PPKG\"
|
||||
if (Test-Path -Path $PPKGFolder){
|
||||
if (Test-Path -Path $PPKGFolder) {
|
||||
[array]$PPKGFiles = @(Get-ChildItem -Path $PPKGFolder*.ppkg)
|
||||
$PPKGFilesCount = $PPKGFiles.Count
|
||||
if ($PPKGFilesCount -ge 1){
|
||||
$PPKG = $true
|
||||
if ($PPKGFilesCount -ge 1) {
|
||||
$PPKG = $true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,35 +226,35 @@ $UnattendFolder = $USBDrive + "unattend\"
|
||||
$UnattendFilePath = $UnattendFolder + "unattend.xml"
|
||||
$UnattendPrefixPath = $UnattendFolder + "prefixes.txt"
|
||||
$UnattendComputerNamePath = $UnattendFolder + "SerialComputerNames.csv"
|
||||
If (Test-Path -Path $UnattendFilePath){
|
||||
If (Test-Path -Path $UnattendFilePath) {
|
||||
$UnattendFile = Get-ChildItem -Path $UnattendFilePath
|
||||
If ($UnattendFile){
|
||||
If ($UnattendFile) {
|
||||
$Unattend = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendPrefixPath){
|
||||
If (Test-Path -Path $UnattendPrefixPath) {
|
||||
$UnattendPrefixFile = Get-ChildItem -Path $UnattendPrefixPath
|
||||
If ($UnattendPrefixFile){
|
||||
If ($UnattendPrefixFile) {
|
||||
$UnattendPrefix = $true
|
||||
}
|
||||
}
|
||||
If (Test-Path -Path $UnattendComputerNamePath){
|
||||
If (Test-Path -Path $UnattendComputerNamePath) {
|
||||
$UnattendComputerNameFile = Get-ChildItem -Path $UnattendComputerNamePath
|
||||
If ($UnattendComputerNameFile){
|
||||
If ($UnattendComputerNameFile) {
|
||||
$UnattendComputerName = $true
|
||||
}
|
||||
}
|
||||
|
||||
#Ask for device name if unattend exists
|
||||
if ($Unattend -and $UnattendPrefix){
|
||||
if ($Unattend -and $UnattendPrefix) {
|
||||
Writelog 'Unattend file found with prefixes.txt. Getting prefixes.'
|
||||
$UnattendPrefixes = @(Get-content $UnattendPrefixFile)
|
||||
$UnattendPrefixCount = $UnattendPrefixes.Count
|
||||
If ($UnattendPrefixCount -gt 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefixes"
|
||||
$array = @()
|
||||
for($i=0;$i -le $UnattendPrefixCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i]}
|
||||
for ($i = 0; $i -le $UnattendPrefixCount - 1; $i++) {
|
||||
$Properties = [ordered]@{Number = $i + 1 ; DeviceNamePrefix = $UnattendPrefixes[$i] }
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, DeviceNamePrefix
|
||||
@@ -259,33 +262,33 @@ if ($Unattend -and $UnattendPrefix){
|
||||
try {
|
||||
$var = $true
|
||||
[int]$PrefixSelected = Read-Host 'Enter the prefix number to use for the device name'
|
||||
$PrefixSelected = $PrefixSelected -1
|
||||
$PrefixSelected = $PrefixSelected - 1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid prefix number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount -1) -and $var)
|
||||
} until (($PrefixSelected -le $UnattendPrefixCount - 1) -and $var)
|
||||
$PrefixToUse = $array[$PrefixSelected].DeviceNamePrefix
|
||||
WriteLog "$PrefixToUse was selected"
|
||||
}
|
||||
elseif ($UnattendPrefixCount -eq 1) {
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
WriteLog "Found $UnattendPrefixCount Prefix"
|
||||
$PrefixToUse = $UnattendPrefixes[0]
|
||||
WriteLog "Will use $PrefixToUse as device name prefix"
|
||||
}
|
||||
#Get serial number to append. This can make names longer than 15 characters. Trim any leading or trailing whitespace
|
||||
$serial = (Get-CimInstance -ClassName win32_bios).SerialNumber.Trim()
|
||||
#Combine prefix with serial
|
||||
$computername = ($PrefixToUse + $serial) -replace "\s","" # Remove spaces because windows does not support spaces in the computer names
|
||||
$computername = ($PrefixToUse + $serial) -replace "\s", "" # Remove spaces because windows does not support spaces in the computer names
|
||||
#If computername is longer than 15 characters, reduce to 15. Sysprep/unattend doesn't like ComputerName being longer than 15 characters even though Windows accepts it
|
||||
If ($computername.Length -gt 15){
|
||||
$computername = $computername.substring(0,15)
|
||||
If ($computername.Length -gt 15) {
|
||||
$computername = $computername.substring(0, 15)
|
||||
}
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
elseif($Unattend -and $UnattendComputerName){
|
||||
elseif ($Unattend -and $UnattendComputerName) {
|
||||
Writelog 'Unattend file found with SerialComputerNames.csv. Getting name for current computer.'
|
||||
$SerialComputerNames = Import-Csv -Path $UnattendComputerNameFile.FullName -Delimiter ","
|
||||
|
||||
@@ -296,14 +299,15 @@ 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]$_ })))
|
||||
[string]$computername = ("FFU-" + ( -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 11 | ForEach-Object { [char]$_ })))
|
||||
$computername = Set-Computername($computername)
|
||||
Writelog "Computer name set to $computername"
|
||||
}
|
||||
}
|
||||
elseif($Unattend) {
|
||||
elseif ($Unattend) {
|
||||
Writelog 'Unattend file found with no prefixes.txt, asking for name'
|
||||
[string]$computername = Read-Host 'Enter device name'
|
||||
Set-Computername($computername)
|
||||
@@ -314,7 +318,7 @@ else {
|
||||
}
|
||||
|
||||
#If both AP and PPKG folder found with files, ask which to use.
|
||||
If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
If ($autopilot -eq $true -and $PPKG -eq $true) {
|
||||
WriteLog 'Both PPKG and Autopilot json files found'
|
||||
Write-Host 'Both Autopilot JSON files and Provisioning packages were found.'
|
||||
do {
|
||||
@@ -328,10 +332,10 @@ If($autopilot -eq $true -and $PPKG -eq $true){
|
||||
$var = $false
|
||||
}
|
||||
} until (($APorPPKG -gt 0 -and $APorPPKG -lt 3) -and $var)
|
||||
If ($APorPPKG -eq 1){
|
||||
If ($APorPPKG -eq 1) {
|
||||
$PPKG = $false
|
||||
}
|
||||
else{
|
||||
else {
|
||||
$autopilot = $false
|
||||
}
|
||||
}
|
||||
@@ -341,8 +345,8 @@ If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
WriteLog "Found $APFilesCount Autopilot json Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $APFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name}
|
||||
for ($i = 0; $i -le $APFilesCount - 1; $i++) {
|
||||
$Properties = [ordered]@{Number = $i + 1 ; APFile = $APFiles[$i].FullName; APFileName = $APFiles[$i].Name }
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, APFileName
|
||||
@@ -357,7 +361,7 @@ If ($APFilesCount -gt 1 -and $autopilot -eq $true) {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid AP json file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($APFileSelected -le $APFilesCount -1) -and $var)
|
||||
} until (($APFileSelected -le $APFilesCount - 1) -and $var)
|
||||
|
||||
$APFileToInstall = $array[$APFileSelected].APFile
|
||||
$APFileName = $array[$APFileSelected].APFileName
|
||||
@@ -378,8 +382,8 @@ If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
WriteLog "Found $PPKGFilesCount PPKG Files"
|
||||
$array = @()
|
||||
|
||||
for($i=0;$i -le $PPKGFilesCount -1;$i++){
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name}
|
||||
for ($i = 0; $i -le $PPKGFilesCount - 1; $i++) {
|
||||
$Properties = [ordered]@{Number = $i + 1 ; PPKGFile = $PPKGFiles[$i].FullName; PPKGFileName = $PPKGFiles[$i].Name }
|
||||
$array += New-Object PSObject -Property $Properties
|
||||
}
|
||||
$array | Format-Table -AutoSize -Property Number, PPKGFileName
|
||||
@@ -394,7 +398,7 @@ If ($PPKGFilesCount -gt 1 -and $PPKG -eq $true) {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid PPKG file number'
|
||||
$var = $false
|
||||
}
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount -1) -and $var)
|
||||
} until (($PPKGFileSelected -le $PPKGFilesCount - 1) -and $var)
|
||||
|
||||
$PPKGFileToInstall = $array[$PPKGFileSelected].PPKGFile
|
||||
WriteLog "$PPKGFileToInstall was selected"
|
||||
@@ -412,73 +416,139 @@ 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)
|
||||
{
|
||||
WriteLog "Searching for driver WIMs and folders in $DriversPath"
|
||||
|
||||
# Get all WIM files
|
||||
$WimFiles = Get-ChildItem -Path $DriversPath -Filter *.wim -Recurse
|
||||
|
||||
# Get all top-level driver folders
|
||||
$DriverFolders = Get-ChildItem -Path $DriversPath -Directory
|
||||
|
||||
# Create a combined list
|
||||
$DriverSources = @()
|
||||
$WimFiles | ForEach-Object {
|
||||
$DriverSources += [PSCustomObject]@{
|
||||
Type = 'WIM'
|
||||
Path = $_.FullName
|
||||
# --- 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
|
||||
}
|
||||
}
|
||||
$DriverFolders | ForEach-Object {
|
||||
$DriverSources += [PSCustomObject]@{
|
||||
Type = 'Folder'
|
||||
Path = $_.FullName
|
||||
else {
|
||||
(Get-CimInstance -Class Win32_ComputerSystem).Model
|
||||
}
|
||||
}
|
||||
WriteLog "Detected System: Manufacturer='$systemManufacturer', Model='$systemModel'"
|
||||
|
||||
$DriverSourcesCount = $DriverSources.Count
|
||||
# Load and parse the mapping file
|
||||
$driverMappings = Get-Content -Path $driverMappingPath -Raw | ConvertFrom-Json
|
||||
|
||||
if ($DriverSourcesCount -gt 0) {
|
||||
WriteLog "Found $DriverSourcesCount total driver sources (WIMs and folders)."
|
||||
if ($DriverSourcesCount -eq 1) {
|
||||
$DriverSourcePath = $DriverSources[0].Path
|
||||
$DriverSourceType = $DriverSources[0].Type
|
||||
WriteLog "Single driver source found. Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
} else {
|
||||
# Multiple sources found, prompt user
|
||||
WriteLog "Multiple driver sources found. Prompting for selection."
|
||||
$displayArray = @()
|
||||
for($i=0; $i -lt $DriverSourcesCount; $i++){
|
||||
$displayArray += [PSCustomObject]@{
|
||||
Number = $i + 1
|
||||
Type = $DriverSources[$i].Type
|
||||
Path = $DriverSources[$i].Path
|
||||
}
|
||||
# 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
|
||||
}
|
||||
$displayArray | Format-Table -AutoSize
|
||||
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriverSelected = Read-Host 'Enter the number of the driver source to install'
|
||||
$DriverSelected = $DriverSelected - 1
|
||||
} catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid number.'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriverSelected -ge 0) -and ($DriverSelected -lt $DriverSourcesCount) -and $var)
|
||||
|
||||
$DriverSourcePath = $DriverSources[$DriverSelected].Path
|
||||
$DriverSourceType = $DriverSources[$DriverSelected].Type
|
||||
WriteLog "User selected Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
}
|
||||
} else {
|
||||
WriteLog "No driver WIMs or folders found in Drivers directory."
|
||||
|
||||
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
|
||||
$WimFiles = Get-ChildItem -Path $DriversPath -Filter *.wim -Recurse
|
||||
|
||||
# Get all top-level driver folders
|
||||
$DriverFolders = Get-ChildItem -Path $DriversPath -Directory
|
||||
|
||||
# Create a combined list
|
||||
$DriverSources = @()
|
||||
$WimFiles | ForEach-Object {
|
||||
$DriverSources += [PSCustomObject]@{
|
||||
Type = 'WIM'
|
||||
Path = $_.FullName
|
||||
}
|
||||
}
|
||||
$DriverFolders | ForEach-Object {
|
||||
$DriverSources += [PSCustomObject]@{
|
||||
Type = 'Folder'
|
||||
Path = $_.FullName
|
||||
}
|
||||
}
|
||||
|
||||
$DriverSourcesCount = $DriverSources.Count
|
||||
|
||||
if ($DriverSourcesCount -gt 0) {
|
||||
WriteLog "Found $DriverSourcesCount total driver sources (WIMs and folders)."
|
||||
if ($DriverSourcesCount -eq 1) {
|
||||
$DriverSourcePath = $DriverSources[0].Path
|
||||
$DriverSourceType = $DriverSources[0].Type
|
||||
WriteLog "Single driver source found. Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
}
|
||||
else {
|
||||
# Multiple sources found, prompt user
|
||||
WriteLog "Multiple driver sources found. Prompting for selection."
|
||||
$displayArray = @()
|
||||
for ($i = 0; $i -lt $DriverSourcesCount; $i++) {
|
||||
$displayArray += [PSCustomObject]@{
|
||||
Number = $i + 1
|
||||
Type = $DriverSources[$i].Type
|
||||
Path = $DriverSources[$i].Path
|
||||
}
|
||||
}
|
||||
$displayArray | Format-Table -AutoSize
|
||||
|
||||
do {
|
||||
try {
|
||||
$var = $true
|
||||
[int]$DriverSelected = Read-Host 'Enter the number of the driver source to install'
|
||||
$DriverSelected = $DriverSelected - 1
|
||||
}
|
||||
catch {
|
||||
Write-Host 'Input was not in correct format. Please enter a valid number.'
|
||||
$var = $false
|
||||
}
|
||||
} until (($DriverSelected -ge 0) -and ($DriverSelected -lt $DriverSourcesCount) -and $var)
|
||||
|
||||
$DriverSourcePath = $DriverSources[$DriverSelected].Path
|
||||
$DriverSourceType = $DriverSources[$DriverSelected].Type
|
||||
WriteLog "User selected Type: $DriverSourceType, Path: $DriverSourcePath"
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "No driver WIMs or folders found in Drivers directory."
|
||||
}
|
||||
}
|
||||
else {
|
||||
WriteLog "Drivers folder not found at $DriversPath. Skipping driver installation."
|
||||
}
|
||||
} else {
|
||||
WriteLog "Drivers folder not found at $DriversPath. Skipping driver installation."
|
||||
}
|
||||
#Partition drive
|
||||
Writelog 'Clean Disk'
|
||||
@@ -512,24 +582,24 @@ if ($recoveryPartition) {
|
||||
$diskpartScript | diskpart.exe | Out-Null
|
||||
WriteLog 'Setting recovery partition attributes complete'
|
||||
}
|
||||
if($LASTEXITCODE -eq 0){
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
WriteLog 'Successfully applied FFU'
|
||||
}
|
||||
elseif($LASTEXITCODE -eq 1393){
|
||||
elseif ($LASTEXITCODE -eq 1393) {
|
||||
WriteLog "Failed to apply FFU - LastExitCode = $LastExitCode"
|
||||
WriteLog "This is likely due to a mismatched LogicalSectorByteSize"
|
||||
WriteLog "BytesPerSector value from Win32_Diskdrive is $BytesPerSector"
|
||||
if ($BytesPerSector -eq 4096){
|
||||
if ($BytesPerSector -eq 4096) {
|
||||
WriteLog "The FFU build process by default uses a 512 LogicalSectorByteSize. Rebuild the FFU by adding -LogicalSectorByteSize 4096 to the command line"
|
||||
}
|
||||
elseif($BytesPerSector -eq 512){
|
||||
elseif ($BytesPerSector -eq 512) {
|
||||
WriteLog "This FFU was likely built with a LogicalSectorByteSize of 4096. Rebuild the FFU by adding -LogicalSectorByteSize 512 to the command line"
|
||||
}
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
exit
|
||||
}
|
||||
else{
|
||||
else {
|
||||
Writelog "Failed to apply FFU - LastExitCode = $LASTEXITCODE also check dism.log on the USB drive for more info"
|
||||
#Copy DISM log to USBDrive
|
||||
invoke-process xcopy.exe "X:\Windows\logs\dism\dism.log $USBDrive /Y"
|
||||
@@ -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"
|
||||
@@ -551,7 +620,7 @@ If (Test-Path -Path $WinRE)
|
||||
WriteLog 'Registering location of recovery tools succeeded'
|
||||
}
|
||||
#Autopilot JSON
|
||||
If ($APFileToInstall){
|
||||
If ($APFileToInstall) {
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot"
|
||||
Invoke-process xcopy.exe "$APFileToInstall W:\Windows\provisioning\autopilot\"
|
||||
WriteLog "Copying $APFileToInstall to W:\windows\provisioning\autopilot succeeded"
|
||||
@@ -561,13 +630,13 @@ If ($APFileToInstall){
|
||||
WriteLog "Renamed W:\Windows\Provisioning\Autopilot\$APFilename to W:\Windows\Provisioning\Autopilot\AutoPilotConfigurationFile.json"
|
||||
}
|
||||
|
||||
catch{
|
||||
catch {
|
||||
Writelog "Copying $APFileToInstall to W:\windows\provisioning\autopilot failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Apply PPKG
|
||||
If ($PPKGFileToInstall){
|
||||
If ($PPKGFileToInstall) {
|
||||
try {
|
||||
#Make sure to delete any existing PPKG on the USB drive
|
||||
Get-Childitem -Path $USBDrive\*.ppkg | ForEach-Object {
|
||||
@@ -578,21 +647,21 @@ If ($PPKGFileToInstall){
|
||||
WriteLog "Copying $PPKGFileToInstall to $USBDrive succeeded"
|
||||
}
|
||||
|
||||
catch{
|
||||
catch {
|
||||
Writelog "Copying $PPKGFileToInstall to $USBDrive failed with error: $_"
|
||||
throw $_
|
||||
}
|
||||
}
|
||||
#Set DeviceName
|
||||
If ($computername){
|
||||
try{
|
||||
If ($computername) {
|
||||
try {
|
||||
$PantherDir = 'w:\windows\panther'
|
||||
If (Test-Path -Path $PantherDir){
|
||||
If (Test-Path -Path $PantherDir) {
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
Invoke-process xcopy "$UnattendFile $PantherDir /Y"
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
else{
|
||||
else {
|
||||
Writelog "$PantherDir doesn't exist, creating it"
|
||||
New-Item -Path $PantherDir -ItemType Directory -Force
|
||||
Writelog "Copying $UnattendFile to $PantherDir"
|
||||
@@ -600,7 +669,7 @@ If ($computername){
|
||||
WriteLog "Copying $UnattendFile to $PantherDir succeeded"
|
||||
}
|
||||
}
|
||||
catch{
|
||||
catch {
|
||||
WriteLog "Copying Unattend.xml to name device failed"
|
||||
throw $_
|
||||
}
|
||||
@@ -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