From ed0266029ab680dc989b0b35db52e843ecdd700a Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Tue, 3 Feb 2026 13:37:58 -0800 Subject: [PATCH] Improves USB drive selection for same-model drives Preserves multiple selected drives that share the same model by storing an array of UniqueIds per model. Updates drive discovery and UI restore logic to accept either a single UniqueId or a list, preventing missed selections and skipping duplicate additions. --- FFUDevelopment/BuildFFUVM.ps1 | 79 ++++++++++++------- .../FFUUI.Core/FFUUI.Core.Config.psm1 | 35 +++++++- 2 files changed, 82 insertions(+), 32 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM.ps1 b/FFUDevelopment/BuildFFUVM.ps1 index ecd818d..bc147ee 100644 --- a/FFUDevelopment/BuildFFUVM.ps1 +++ b/FFUDevelopment/BuildFFUVM.ps1 @@ -195,9 +195,11 @@ Path to a JSON file containing a list of user-defined applications to install. D .PARAMETER USBDriveList A hashtable containing USB drives from win32_diskdrive where: - Key: USB drive model name (partial match supported) -- Value: USB drive serial number (trailing partial match supported due to some serial numbers ending with blank spaces) +- Value: USB drive UniqueId string, or an array of UniqueIds (to support selecting multiple drives with the same model) -Example: @{ "SanDisk Ultra" = "1234567890"; "Kingston DataTraveler" = "0987654321" } +Examples: +@{ "SanDisk Ultra" = "1234567890" } +@{ "SanDisk Ultra" = @("1234567890", "ABCDEFG"); "Kingston DataTraveler" = "0987654321" } .PARAMETER MaxUSBDrives Maximum number of USB drives to build in parallel. Default is 5. Set to 0 to process all discovered drives (or all selected drives when USBDriveList or selection is used). Actual throttle will never exceed the number of drives discovered. @@ -3961,40 +3963,57 @@ Function Get-USBDrive { } elseif ($USBDriveList) { # Log the count of specified USB drives - $USBDriveListCount = $USBDriveList.Count + # USBDriveList values can be a single UniqueId string, or an array of UniqueIds (multiple same-model drives) + $USBDriveListCount = 0 + foreach ($model in $USBDriveList.Keys) { + $USBDriveListCount += @($USBDriveList[$model]).Count + } WriteLog "Looking for $USBDriveListCount USB drives from USB Drive List" + # Get only the specified USB drives based on model and UniqueId $USBDrives = @() foreach ($model in $USBDriveList.Keys) { - $configUniqueId = $USBDriveList[$model] - WriteLog "Looking for USB drive model $model with UniqueId $configUniqueId" - # First get candidate drives by model and media type - $candidateDrives = Get-CimInstance -ClassName Win32_DiskDrive -Filter "Model LIKE '%$model%' AND (MediaType='Removable Media' OR MediaType='External hard disk media')" - $foundDrive = $null - foreach ($candidate in $candidateDrives) { - # Get the disk to retrieve UniqueId - $disk = Get-Disk -Number $candidate.Index -ErrorAction SilentlyContinue - if ($disk -and $disk.UniqueId) { - # Trim the machine name suffix (everything after the colon) from UniqueId - $diskUniqueId = if ($disk.UniqueId -match ':') { - $disk.UniqueId.Split(':')[0] - } - else { - $disk.UniqueId - } - # Match on the trimmed UniqueId - if ($diskUniqueId -eq $configUniqueId) { - $foundDrive = $candidate - break + $configUniqueIds = @($USBDriveList[$model]) + + foreach ($configUniqueId in $configUniqueIds) { + if ([string]::IsNullOrWhiteSpace([string]$configUniqueId)) { + continue + } + + WriteLog "Looking for USB drive model $model with UniqueId $configUniqueId" + # First get candidate drives by model and media type + $candidateDrives = Get-CimInstance -ClassName Win32_DiskDrive -Filter "Model LIKE '%$model%' AND (MediaType='Removable Media' OR MediaType='External hard disk media')" + $foundDrive = $null + foreach ($candidate in $candidateDrives) { + # Get the disk to retrieve UniqueId + $disk = Get-Disk -Number $candidate.Index -ErrorAction SilentlyContinue + if ($disk -and $disk.UniqueId) { + # Trim the machine name suffix (everything after the colon) from UniqueId + $diskUniqueId = if ($disk.UniqueId -match ':') { + $disk.UniqueId.Split(':')[0] + } + else { + $disk.UniqueId + } + # Match on the trimmed UniqueId + if ($diskUniqueId -eq $configUniqueId) { + $foundDrive = $candidate + break + } } } - } - if ($foundDrive) { - WriteLog "Found USB drive model $($foundDrive.Model) with UniqueId $configUniqueId" - $USBDrives += $foundDrive - } - else { - WriteLog "USB drive model $model with UniqueId $configUniqueId not found" + if ($foundDrive) { + if ($USBDrives.Index -notcontains $foundDrive.Index) { + WriteLog "Found USB drive model $($foundDrive.Model) with UniqueId $configUniqueId" + $USBDrives += $foundDrive + } + else { + WriteLog "USB drive model $($foundDrive.Model) with UniqueId $configUniqueId was already added. Skipping duplicate." + } + } + else { + WriteLog "USB drive model $model with UniqueId $configUniqueId not found" + } } } $USBDrivesCount = $USBDrives.Count diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 index 8a67c74..b20b7e8 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 @@ -115,8 +115,27 @@ function Get-UIConfig { } # Save selected USB drives using UniqueId for reliable identification + # Multiple physical drives can share the same Model, so store an array of UniqueIds per Model. $State.Controls.lstUSBDrives.Items | Where-Object { $_.IsSelected } | ForEach-Object { - $config.USBDriveList[$_.Model] = $_.UniqueId + $modelName = $_.Model + $uniqueId = $_.UniqueId + + if ([string]::IsNullOrWhiteSpace($modelName) -or [string]::IsNullOrWhiteSpace($uniqueId)) { + return + } + + # Ensure the hashtable value is always an array so multiple same-model drives are preserved + $existingUniqueIds = $config.USBDriveList[$modelName] + if ($null -eq $existingUniqueIds) { + $config.USBDriveList[$modelName] = @($uniqueId) + return + } + + $existingUniqueIds = @($existingUniqueIds) + if (-not ($existingUniqueIds -contains $uniqueId)) { + $existingUniqueIds += $uniqueId + } + $config.USBDriveList[$modelName] = $existingUniqueIds } # Additional FFU file selections @@ -671,7 +690,19 @@ function Update-UIFromConfig { } # Match USB drives by UniqueId instead of SerialNumber - if ($propertyExists -and ($propertyValue -eq $item.UniqueId)) { + # USBDriveList values can be a single UniqueId (string) or an array of UniqueIds (multiple same-model drives) + $isMatch = $false + if ($propertyExists) { + if ($propertyValue -is [string]) { + $isMatch = ($propertyValue -eq $item.UniqueId) + } + else { + $propertyValueArray = @($propertyValue) + $isMatch = ($propertyValueArray -contains $item.UniqueId) + } + } + + if ($isMatch) { WriteLog "LoadConfig: Selecting USB Drive Model '$($item.Model)' with UniqueId '$($item.UniqueId)'." $item.IsSelected = $true }