Add configurable FFU build partition drive letters

Add System, Windows, and Recovery partition drive-letter settings to the Hyper-V Settings UI, config save/load, BuildFFUVM parameters, and sample/docs. Defaults remain S, W, and R.

Validate selected letters early, log validation failures for the UI, include drive-letter metadata in VHDX cache matching, and normalize legacy malformed cache values. This only affects FFU build-time partitioning; ApplyFFU deployment letters remain unchanged.
This commit is contained in:
rbalsleyMSFT
2026-06-05 09:46:00 -07:00
parent c32cb93434
commit 9fb9e81701
10 changed files with 262 additions and 21 deletions
+113 -11
View File
@@ -147,6 +147,15 @@ Path to the Windows 10/11 ISO file.
.PARAMETER LogicalSectorSizeBytes
UInt32 value of 512 or 4096. Useful for 4Kn drives or devices shipping with UFS drives. Default is 512.
.PARAMETER SystemPartitionDriveLetter
Drive letter used for the System partition while building the FFU VHDX. Default is S.
.PARAMETER WindowsPartitionDriveLetter
Drive letter used for the Windows partition while building the FFU VHDX. Default is W.
.PARAMETER RecoveryPartitionDriveLetter
Drive letter used for the Recovery partition while building the FFU VHDX. Default is R.
.PARAMETER Make
Make of the device to download drivers. Accepted values are: 'Microsoft', 'Dell', 'HP', 'Lenovo'.
@@ -411,6 +420,9 @@ param(
[string]$MediaType = 'consumer',
[ValidateSet(512, 4096)]
[uint32]$LogicalSectorSizeBytes = 512,
[string]$SystemPartitionDriveLetter = 'S',
[string]$WindowsPartitionDriveLetter = 'W',
[string]$RecoveryPartitionDriveLetter = 'R',
[bool]$Optimize = $true,
[string]$DriversJsonPath,
[bool]$CompressDownloadedDriversToWim = $false,
@@ -866,6 +878,9 @@ class VhdxCacheItem {
[string]$VhdxFileName = ""
[uint32]$LogicalSectorSizeBytes = ""
[uint64]$Disksize = ""
[string]$SystemPartitionDriveLetter = ""
[string]$WindowsPartitionDriveLetter = ""
[string]$RecoveryPartitionDriveLetter = ""
[string]$WindowsSKU = ""
[string]$WindowsRelease = ""
[string]$WindowsVersion = ""
@@ -3048,21 +3063,80 @@ function New-ScratchVhdx {
Writelog "Done."
return $toReturn
}
function Get-NormalizedPartitionDriveLetters {
param(
[string]$SystemPartitionDriveLetter,
[string]$WindowsPartitionDriveLetter,
[string]$RecoveryPartitionDriveLetter,
[switch]$ValidateAvailable
)
$requestedLetters = [ordered]@{
SystemPartitionDriveLetter = $SystemPartitionDriveLetter
WindowsPartitionDriveLetter = $WindowsPartitionDriveLetter
RecoveryPartitionDriveLetter = $RecoveryPartitionDriveLetter
}
$normalizedLetters = [ordered]@{}
foreach ($entry in $requestedLetters.GetEnumerator()) {
$driveLetter = ([string]$entry.Value).Trim().TrimEnd(':').ToUpperInvariant()
if ([string]::IsNullOrWhiteSpace($driveLetter) -or $driveLetter -notmatch '^[A-Z]$') {
throw "$($entry.Key) must be a single drive letter from A to Z without a colon."
}
$normalizedLetters[$entry.Key] = $driveLetter
}
$duplicateLetters = @($normalizedLetters.Values | Group-Object | Where-Object { $_.Count -gt 1 })
if ($duplicateLetters.Count -gt 0) {
$duplicateLetterList = ($duplicateLetters | ForEach-Object { $_.Name }) -join ', '
throw "System, Windows, and Recovery partition drive letters must be unique. Duplicate value(s): $duplicateLetterList."
}
if ($ValidateAvailable) {
foreach ($entry in $normalizedLetters.GetEnumerator()) {
$existingDrive = Get-PSDrive -Name $entry.Value -PSProvider FileSystem -ErrorAction SilentlyContinue
if ($null -ne $existingDrive) {
throw "$($entry.Key) uses drive letter $($entry.Value), but that letter is already assigned on this host. Choose an unused drive letter."
}
}
}
return [pscustomobject]$normalizedLetters
}
function Get-PartitionDriveLetterCacheValue {
param(
[object]$DriveLetterValue
)
$driveLetter = ([string]$DriveLetterValue).Trim().TrimEnd(':').ToUpperInvariant()
if ($driveLetter -match '^[A-Z]$') {
return $driveLetter
}
$trailingDriveLetter = [regex]::Match($driveLetter, '(?i)(?:^|[^A-Z])([A-Z])$')
if ($trailingDriveLetter.Success) {
return $trailingDriveLetter.Groups[1].Value.ToUpperInvariant()
}
return $driveLetter
}
#Add System Partition
function New-SystemPartition {
param(
[Parameter(Mandatory = $true)]
[ciminstance]$VhdxDisk,
[string]$DriveLetter = 'S',
[uint64]$SystemPartitionSize = 260MB
)
WriteLog "Creating System partition..."
$sysPartition = $VhdxDisk | New-Partition -DriveLetter 'S' -Size $SystemPartitionSize -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -IsHidden
$sysPartition | Format-Volume -FileSystem FAT32 -Force -NewFileSystemLabel "System"
$sysPartition = $VhdxDisk | New-Partition -DriveLetter $DriveLetter -Size $SystemPartitionSize -GptType "{c12a7328-f81f-11d2-ba4b-00a0c93ec93b}" -IsHidden
$sysPartition | Format-Volume -FileSystem FAT32 -Force -NewFileSystemLabel "System" | Out-Null
WriteLog 'Done.'
return $sysPartition.DriveLetter
return [string]$sysPartition.DriveLetter
}
#Add MSRPartition
function New-MSRPartition {
@@ -3088,16 +3162,17 @@ function New-OSPartition {
[Parameter(Mandatory = $true)]
[string]$WimPath,
[uint32]$WimIndex,
[string]$DriveLetter = 'W',
[uint64]$OSPartitionSize = 0
)
WriteLog "Creating OS partition..."
if ($OSPartitionSize -gt 0) {
$osPartition = $vhdxDisk | New-Partition -DriveLetter 'W' -Size $OSPartitionSize -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
$osPartition = $vhdxDisk | New-Partition -DriveLetter $DriveLetter -Size $OSPartitionSize -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
}
else {
$osPartition = $vhdxDisk | New-Partition -DriveLetter 'W' -UseMaximumSize -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
$osPartition = $vhdxDisk | New-Partition -DriveLetter $DriveLetter -UseMaximumSize -GptType "{ebd0a0a2-b9e5-4433-87c0-68b6b72699c7}"
}
$osPartition | Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel "Windows"
@@ -3129,6 +3204,7 @@ function New-RecoveryPartition {
[Parameter(Mandatory = $true)]
$OsPartition,
[uint64]$RecoveryPartitionSize = 0,
[string]$DriveLetter = 'R',
[ciminstance]$DataPartition
)
@@ -3163,7 +3239,7 @@ function New-RecoveryPartition {
WriteLog "OS partition shrunk by $calculatedRecoverySize bytes for Recovery partition."
}
$recoveryPartition = $VhdxDisk | New-Partition -DriveLetter 'R' -UseMaximumSize -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}" `
$recoveryPartition = $VhdxDisk | New-Partition -DriveLetter $DriveLetter -UseMaximumSize -GptType "{de94bba4-06d1-4d40-a16a-bfd50179d6ac}" `
| Format-Volume -FileSystem NTFS -Confirm:$false -Force -NewFileSystemLabel 'Recovery'
WriteLog "Done. Recovery partition at drive $($recoveryPartition.DriveLetter):"
@@ -5877,7 +5953,6 @@ if ($ExportConfigFile) {
####### End Generate Config File #######
#Setting long path support - this prevents issues where some applications have deep directory structures
#and oscdimg fails to create the Apps ISO
try {
@@ -5896,6 +5971,21 @@ if ($LongPathsEnabled -ne 1) {
Set-Progress -Percentage 2 -Message "Validating parameters..."
###PARAMETER VALIDATION
#Set build partition drive letters and validate they are available for use; this is required before any build steps that require drive access to ensure the expected drive letters are reserved and to fail fast if there are conflicts.
try {
$partitionDriveLetters = Get-NormalizedPartitionDriveLetters -SystemPartitionDriveLetter $SystemPartitionDriveLetter -WindowsPartitionDriveLetter $WindowsPartitionDriveLetter -RecoveryPartitionDriveLetter $RecoveryPartitionDriveLetter -ValidateAvailable
$SystemPartitionDriveLetter = $partitionDriveLetters.SystemPartitionDriveLetter
$WindowsPartitionDriveLetter = $partitionDriveLetters.WindowsPartitionDriveLetter
$RecoveryPartitionDriveLetter = $partitionDriveLetters.RecoveryPartitionDriveLetter
WriteLog "Using build partition drive letters: System=$SystemPartitionDriveLetter, Windows=$WindowsPartitionDriveLetter, Recovery=$RecoveryPartitionDriveLetter"
}
catch {
$partitionDriveLetterValidationError = "Build validation failed: $($_.Exception.Message)"
Set-Progress -Percentage 2 -Message $partitionDriveLetterValidationError
WriteLog $partitionDriveLetterValidationError
throw $partitionDriveLetterValidationError
}
#Validate CopyDrivers dependency on BuildUSBDrive
if ($CopyDrivers -and (-not $BuildUSBDrive)) {
WriteLog "-CopyDrivers is set to `$true, but -BuildUSBDrive is not set to `$true"
@@ -7187,6 +7277,15 @@ try {
[uint64]$cachedDisksize = 0
if (-not [uint64]::TryParse([string]$vhdxCacheItem.Disksize, [ref]$cachedDisksize)) { WriteLog "Disksize invalid in cached config ($($vhdxCacheItem.Disksize)), continuing"; continue }
if ($cachedDisksize -ne $Disksize) { WriteLog "Disksize mismatch (cached: $cachedDisksize, current: $Disksize), continuing"; continue }
if ($vhdxCacheItem.PSObject.Properties.Name -notcontains 'SystemPartitionDriveLetter') { WriteLog 'SystemPartitionDriveLetter missing in cached config, continuing'; continue }
if ($vhdxCacheItem.PSObject.Properties.Name -notcontains 'WindowsPartitionDriveLetter') { WriteLog 'WindowsPartitionDriveLetter missing in cached config, continuing'; continue }
if ($vhdxCacheItem.PSObject.Properties.Name -notcontains 'RecoveryPartitionDriveLetter') { WriteLog 'RecoveryPartitionDriveLetter missing in cached config, continuing'; continue }
$cachedSystemPartitionDriveLetter = Get-PartitionDriveLetterCacheValue -DriveLetterValue $vhdxCacheItem.SystemPartitionDriveLetter
$cachedWindowsPartitionDriveLetter = Get-PartitionDriveLetterCacheValue -DriveLetterValue $vhdxCacheItem.WindowsPartitionDriveLetter
$cachedRecoveryPartitionDriveLetter = Get-PartitionDriveLetterCacheValue -DriveLetterValue $vhdxCacheItem.RecoveryPartitionDriveLetter
if ($cachedSystemPartitionDriveLetter -ne $SystemPartitionDriveLetter) { WriteLog "SystemPartitionDriveLetter mismatch (cached: $($vhdxCacheItem.SystemPartitionDriveLetter), current: $SystemPartitionDriveLetter), continuing"; continue }
if ($cachedWindowsPartitionDriveLetter -ne $WindowsPartitionDriveLetter) { WriteLog "WindowsPartitionDriveLetter mismatch (cached: $($vhdxCacheItem.WindowsPartitionDriveLetter), current: $WindowsPartitionDriveLetter), continuing"; continue }
if ($cachedRecoveryPartitionDriveLetter -ne $RecoveryPartitionDriveLetter) { WriteLog "RecoveryPartitionDriveLetter mismatch (cached: $($vhdxCacheItem.RecoveryPartitionDriveLetter), current: $RecoveryPartitionDriveLetter), continuing"; continue }
$cachedUpdateNames = @()
if ($vhdxCacheItem.IncludedUpdates -and $vhdxCacheItem.IncludedUpdates.Count -gt 0) {
@@ -7504,21 +7603,21 @@ try {
$vhdxDisk = New-ScratchVhdx -VhdxPath $VHDXPath -SizeBytes $disksize -LogicalSectorSizeBytes $LogicalSectorSizeBytes
$systemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk
$createdSystemPartitionDriveLetter = New-SystemPartition -VhdxDisk $vhdxDisk -DriveLetter $SystemPartitionDriveLetter
New-MSRPartition -VhdxDisk $vhdxDisk
Set-Progress -Percentage 16 -Message "Applying base Windows image to VHDX..."
$osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index
$osPartition = New-OSPartition -VhdxDisk $vhdxDisk -OSPartitionSize $OSPartitionSize -WimPath $WimPath -WimIndex $index -DriveLetter $WindowsPartitionDriveLetter
$osPartitionDriveLetter = $osPartition[1].DriveLetter
$WindowsPartition = $osPartitionDriveLetter + ':\'
#$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DataPartition $dataPartition
$recoveryPartition = New-RecoveryPartition -VhdxDisk $vhdxDisk -OsPartition $osPartition[1] -RecoveryPartitionSize $RecoveryPartitionSize -DriveLetter $RecoveryPartitionDriveLetter -DataPartition $dataPartition
WriteLog 'All necessary partitions created.'
Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $systemPartitionDriveLetter[1] -AdkPath $adkPath -WindowsArch $WindowsArch
Add-BootFiles -OsPartitionDriveLetter $osPartitionDriveLetter -SystemPartitionDriveLetter $createdSystemPartitionDriveLetter -AdkPath $adkPath -WindowsArch $WindowsArch
#Add Windows packages
if ($UpdateLatestCU -or $UpdateLatestNet -or $UpdatePreviewCU ) {
@@ -7689,6 +7788,9 @@ try {
$cachedVHDXInfo.VhdxFileName = $("$VMName.vhdx")
$cachedVHDXInfo.LogicalSectorSizeBytes = $LogicalSectorSizeBytes
$cachedVHDXInfo.Disksize = $Disksize
$cachedVHDXInfo.SystemPartitionDriveLetter = [string]$SystemPartitionDriveLetter
$cachedVHDXInfo.WindowsPartitionDriveLetter = [string]$WindowsPartitionDriveLetter
$cachedVHDXInfo.RecoveryPartitionDriveLetter = [string]$RecoveryPartitionDriveLetter
$cachedVHDXInfo.WindowsSKU = $WindowsSKU
$cachedVHDXInfo.WindowsRelease = $WindowsRelease
$cachedVHDXInfo.WindowsVersion = $WindowsVersion
+24 -10
View File
@@ -338,7 +338,6 @@ $script:uiState.Controls.btnRun.Add_Click({
$script:uiState.Flags.isCleanupRunning = $true
$script:uiState.Data.pollTimer.Add_Tick({
param($sender, $e)
$currentProcess = $script:uiState.Data.currentBuildProcess
# Read new lines from log
@@ -353,13 +352,13 @@ $script:uiState.Controls.btnRun.Add_Click({
}
if ($null -eq $currentProcess -or $null -eq $script:uiState.Data.pollTimer) {
if ($null -ne $sender) { $sender.Stop() }
if ($null -ne $script:uiState.Data.pollTimer) { $script:uiState.Data.pollTimer.Stop() }
$script:uiState.Data.pollTimer = $null
return
}
if ($currentProcess.HasExited) {
if ($null -ne $sender) { $sender.Stop() }
if ($null -ne $script:uiState.Data.pollTimer) { $script:uiState.Data.pollTimer.Stop() }
$script:uiState.Data.pollTimer = $null
if ($null -ne $script:uiState.Data.logStreamReader) {
@@ -621,7 +620,6 @@ $script:uiState.Controls.btnRun.Add_Click({
# Add the Tick event handler
$script:uiState.Data.pollTimer.Add_Tick({
param($sender, $e)
# This scriptblock runs on the UI thread, so it can safely access script-scoped variables
$currentProcess = $script:uiState.Data.currentBuildProcess
@@ -649,8 +647,8 @@ $script:uiState.Controls.btnRun.Add_Click({
# If process is somehow null or the timer has been nulled out, stop the timer
if ($null -eq $currentProcess -or $null -eq $script:uiState.Data.pollTimer) {
if ($null -ne $sender) {
$sender.Stop()
if ($null -ne $script:uiState.Data.pollTimer) {
$script:uiState.Data.pollTimer.Stop()
}
$script:uiState.Data.pollTimer = $null
return
@@ -659,8 +657,8 @@ $script:uiState.Controls.btnRun.Add_Click({
# Check if the build process has exited
if ($currentProcess.HasExited) {
# Stop the timer, we're done polling
if ($null -ne $sender) {
$sender.Stop()
if ($null -ne $script:uiState.Data.pollTimer) {
$script:uiState.Data.pollTimer.Stop()
}
$script:uiState.Data.pollTimer = $null
@@ -698,9 +696,25 @@ $script:uiState.Controls.btnRun.Add_Click({
# Determine final status based on process exit code
$finalStatusText = "FFU build completed successfully."
if ($exitCode -ne 0) {
$finalStatusText = "FFU build failed. Check FFUDevelopment.log for details."
$failureDetail = $null
if ($null -ne $script:uiState.Data.logData) {
for ($logIndex = $script:uiState.Data.logData.Count - 1; $logIndex -ge 0; $logIndex--) {
$logLine = [string]$script:uiState.Data.logData[$logIndex]
if ($logLine -match '(?i)(Build validation failed|Exception|ERROR|already assigned|must be a single drive letter|must be unique)') {
$failureDetail = $logLine
break
}
}
}
$finalStatusText = if ($failureDetail) { "FFU build failed. $failureDetail" } else { "FFU build failed. Check FFUDevelopment.log for details." }
WriteLog "BuildFFUVM.ps1 process failed with exit code: $exitCode"
[System.Windows.MessageBox]::Show("The build process failed. Please check the $FFUDevelopmentPath\FFUDevelopment.log file for details.`n`nExit code: $exitCode", "Build Error", "OK", "Error") | Out-Null
$buildErrorMessage = "The build process failed. Please check the $FFUDevelopmentPath\FFUDevelopment.log file for details."
if ($failureDetail) {
$buildErrorMessage += "`n`n$failureDetail"
}
$buildErrorMessage += "`n`nExit code: $exitCode"
[System.Windows.MessageBox]::Show($buildErrorMessage, "Build Error", "OK", "Error") | Out-Null
$script:uiState.Controls.pbOverallProgress.Visibility = 'Collapsed'
}
else {
+90
View File
@@ -339,6 +339,96 @@
<!-- VM Name Prefix -->
<TextBlock Text="VM Name Prefix" Margin="0,0,0,8" ToolTip="Prefix for the VM Name. The default is _FFU."/>
<TextBox x:Name="txtVMNamePrefix" HorizontalAlignment="Stretch" Margin="0,0,0,20" ToolTip="Prefix for the VM Name. The default is _FFU."/>
<!-- System Partition Drive Letter -->
<TextBlock Text="System Partition Drive Letter" Margin="0,0,0,8" ToolTip="Drive letter used for the System partition while building the FFU VHDX. Default is S."/>
<ComboBox x:Name="cmbSystemPartitionDriveLetter" HorizontalAlignment="Left" Margin="0,0,0,20" ToolTip="Drive letter used for the System partition while building the FFU VHDX. Default is S.">
<ComboBoxItem Content="A"/>
<ComboBoxItem Content="B"/>
<ComboBoxItem Content="C"/>
<ComboBoxItem Content="D"/>
<ComboBoxItem Content="E"/>
<ComboBoxItem Content="F"/>
<ComboBoxItem Content="G"/>
<ComboBoxItem Content="H"/>
<ComboBoxItem Content="I"/>
<ComboBoxItem Content="J"/>
<ComboBoxItem Content="K"/>
<ComboBoxItem Content="L"/>
<ComboBoxItem Content="M"/>
<ComboBoxItem Content="N"/>
<ComboBoxItem Content="O"/>
<ComboBoxItem Content="P"/>
<ComboBoxItem Content="Q"/>
<ComboBoxItem Content="R"/>
<ComboBoxItem Content="S" IsSelected="True"/>
<ComboBoxItem Content="T"/>
<ComboBoxItem Content="U"/>
<ComboBoxItem Content="V"/>
<ComboBoxItem Content="W"/>
<ComboBoxItem Content="X"/>
<ComboBoxItem Content="Y"/>
<ComboBoxItem Content="Z"/>
</ComboBox>
<!-- Windows Partition Drive Letter -->
<TextBlock Text="Windows Partition Drive Letter" Margin="0,0,0,8" ToolTip="Drive letter used for the Windows partition while building the FFU VHDX. Default is W."/>
<ComboBox x:Name="cmbWindowsPartitionDriveLetter" HorizontalAlignment="Left" Margin="0,0,0,20" ToolTip="Drive letter used for the Windows partition while building the FFU VHDX. Default is W.">
<ComboBoxItem Content="A"/>
<ComboBoxItem Content="B"/>
<ComboBoxItem Content="C"/>
<ComboBoxItem Content="D"/>
<ComboBoxItem Content="E"/>
<ComboBoxItem Content="F"/>
<ComboBoxItem Content="G"/>
<ComboBoxItem Content="H"/>
<ComboBoxItem Content="I"/>
<ComboBoxItem Content="J"/>
<ComboBoxItem Content="K"/>
<ComboBoxItem Content="L"/>
<ComboBoxItem Content="M"/>
<ComboBoxItem Content="N"/>
<ComboBoxItem Content="O"/>
<ComboBoxItem Content="P"/>
<ComboBoxItem Content="Q"/>
<ComboBoxItem Content="R"/>
<ComboBoxItem Content="S"/>
<ComboBoxItem Content="T"/>
<ComboBoxItem Content="U"/>
<ComboBoxItem Content="V"/>
<ComboBoxItem Content="W" IsSelected="True"/>
<ComboBoxItem Content="X"/>
<ComboBoxItem Content="Y"/>
<ComboBoxItem Content="Z"/>
</ComboBox>
<!-- Recovery Partition Drive Letter -->
<TextBlock Text="Recovery Partition Drive Letter" Margin="0,0,0,8" ToolTip="Drive letter used for the Recovery partition while building the FFU VHDX. Default is R."/>
<ComboBox x:Name="cmbRecoveryPartitionDriveLetter" HorizontalAlignment="Left" Margin="0,0,0,20" ToolTip="Drive letter used for the Recovery partition while building the FFU VHDX. Default is R.">
<ComboBoxItem Content="A"/>
<ComboBoxItem Content="B"/>
<ComboBoxItem Content="C"/>
<ComboBoxItem Content="D"/>
<ComboBoxItem Content="E"/>
<ComboBoxItem Content="F"/>
<ComboBoxItem Content="G"/>
<ComboBoxItem Content="H"/>
<ComboBoxItem Content="I"/>
<ComboBoxItem Content="J"/>
<ComboBoxItem Content="K"/>
<ComboBoxItem Content="L"/>
<ComboBoxItem Content="M"/>
<ComboBoxItem Content="N"/>
<ComboBoxItem Content="O"/>
<ComboBoxItem Content="P"/>
<ComboBoxItem Content="Q"/>
<ComboBoxItem Content="R" IsSelected="True"/>
<ComboBoxItem Content="S"/>
<ComboBoxItem Content="T"/>
<ComboBoxItem Content="U"/>
<ComboBoxItem Content="V"/>
<ComboBoxItem Content="W"/>
<ComboBoxItem Content="X"/>
<ComboBoxItem Content="Y"/>
<ComboBoxItem Content="Z"/>
</ComboBox>
<!-- Logical Sector Size -->
<TextBlock Text="Logical Sector Size" Margin="0,0,0,8" ToolTip="Unit32 value of 512 or 4096. Useful for 4Kn drives or devices shipping with UFS drives. Default is 512."/>
<ComboBox x:Name="cmbLogicalSectorSize" HorizontalAlignment="Left" ToolTip="Unit32 value of 512 or 4096. Useful for 4Kn drives or devices shipping with UFS drives. Default is 512.">
@@ -62,6 +62,9 @@ function Get-UIConfig {
InstallWingetApps = $State.Controls.chkInstallWingetApps.IsChecked
ISOPath = $State.Controls.txtISOPath.Text
WindowsMediaSource = if ($null -ne $State.Controls.rbProvideISO -and $State.Controls.rbProvideISO.IsChecked) { "Provide Windows ISO" } else { "Download Windows ESD" }
SystemPartitionDriveLetter = $State.Controls.cmbSystemPartitionDriveLetter.SelectedItem.Content
WindowsPartitionDriveLetter = $State.Controls.cmbWindowsPartitionDriveLetter.SelectedItem.Content
RecoveryPartitionDriveLetter = $State.Controls.cmbRecoveryPartitionDriveLetter.SelectedItem.Content
LogicalSectorSizeBytes = [int]$State.Controls.cmbLogicalSectorSize.SelectedItem.Content
# Make = $null
MediaType = $State.Controls.cmbMediaType.SelectedItem
@@ -523,6 +526,9 @@ function Update-UIFromConfig {
Set-UIValue -ControlName 'txtProcessors' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'Processors' -State $State
Set-UIValue -ControlName 'txtVMLocation' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'VMLocation' -State $State
Set-UIValue -ControlName 'txtVMNamePrefix' -PropertyName 'Text' -ConfigObject $ConfigContent -ConfigKey 'FFUPrefix' -State $State
Set-UIValue -ControlName 'cmbSystemPartitionDriveLetter' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'SystemPartitionDriveLetter' -TransformValue { param($val) ([string]$val).Trim().TrimEnd(':').ToUpperInvariant() } -State $State
Set-UIValue -ControlName 'cmbWindowsPartitionDriveLetter' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'WindowsPartitionDriveLetter' -TransformValue { param($val) ([string]$val).Trim().TrimEnd(':').ToUpperInvariant() } -State $State
Set-UIValue -ControlName 'cmbRecoveryPartitionDriveLetter' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'RecoveryPartitionDriveLetter' -TransformValue { param($val) ([string]$val).Trim().TrimEnd(':').ToUpperInvariant() } -State $State
Set-UIValue -ControlName 'cmbLogicalSectorSize' -PropertyName 'SelectedItem' -ConfigObject $ConfigContent -ConfigKey 'LogicalSectorSizeBytes' -TransformValue { param($val) $val.ToString() } -State $State
$State.Controls.spVMNetworkingSettings.IsEnabled = $true -eq $State.Controls.chkEnableVMNetworking.IsChecked
if (-not ($true -eq $State.Controls.chkEnableVMNetworking.IsChecked)) {
@@ -255,6 +255,9 @@ function Initialize-UIControls {
$State.Controls.txtProcessors = $window.FindName('txtProcessors')
$State.Controls.txtVMLocation = $window.FindName('txtVMLocation')
$State.Controls.txtVMNamePrefix = $window.FindName('txtVMNamePrefix')
$State.Controls.cmbSystemPartitionDriveLetter = $window.FindName('cmbSystemPartitionDriveLetter')
$State.Controls.cmbWindowsPartitionDriveLetter = $window.FindName('cmbWindowsPartitionDriveLetter')
$State.Controls.cmbRecoveryPartitionDriveLetter = $window.FindName('cmbRecoveryPartitionDriveLetter')
$State.Controls.cmbLogicalSectorSize = $window.FindName('cmbLogicalSectorSize')
$State.Controls.txtProductKey = $window.FindName('txtProductKey')
$State.Controls.txtOfficePath = $window.FindName('txtOfficePath')
@@ -445,6 +448,9 @@ function Initialize-UIDefaults {
$State.Controls.txtProcessors.Text = $State.Defaults.generalDefaults.Processors
$State.Controls.txtVMLocation.Text = $State.Defaults.generalDefaults.VMLocation
$State.Controls.txtVMNamePrefix.Text = $State.Defaults.generalDefaults.VMNamePrefix
$State.Controls.cmbSystemPartitionDriveLetter.SelectedItem = ($State.Controls.cmbSystemPartitionDriveLetter.Items | Where-Object { $_.Content -eq $State.Defaults.generalDefaults.SystemPartitionDriveLetter })
$State.Controls.cmbWindowsPartitionDriveLetter.SelectedItem = ($State.Controls.cmbWindowsPartitionDriveLetter.Items | Where-Object { $_.Content -eq $State.Defaults.generalDefaults.WindowsPartitionDriveLetter })
$State.Controls.cmbRecoveryPartitionDriveLetter.SelectedItem = ($State.Controls.cmbRecoveryPartitionDriveLetter.Items | Where-Object { $_.Content -eq $State.Defaults.generalDefaults.RecoveryPartitionDriveLetter })
$State.Controls.cmbLogicalSectorSize.SelectedItem = ($State.Controls.cmbLogicalSectorSize.Items | Where-Object { $_.Content -eq $State.Defaults.generalDefaults.LogicalSectorSize.ToString() })
# Populate Windows Release, Version, and SKU comboboxes
@@ -159,6 +159,9 @@ function Get-GeneralDefaults {
Processors = 4
VMLocation = $vmLocationPath
VMNamePrefix = "_FFU"
SystemPartitionDriveLetter = 'S'
WindowsPartitionDriveLetter = 'W'
RecoveryPartitionDriveLetter = 'R'
LogicalSectorSize = 512
# Updates Tab Defaults
UpdateLatestCU = $true
Binary file not shown.