mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Improves Windows image index selection
Updates selection to match images by language-independent edition metadata instead of localized names, reducing failures across ISO/ESD sources and languages. Adds server Desktop Experience vs Core handling via installation type and prefers the best match deterministically, falling back to a user prompt only when needed with better logging.
This commit is contained in:
+119
-27
@@ -2386,43 +2386,135 @@ function Get-Index {
|
|||||||
[string]$WindowsSKU
|
[string]$WindowsSKU
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get the available indexes in the WIM/ESD
|
||||||
# Get the available indexes using Get-WindowsImage
|
|
||||||
$imageIndexes = Get-WindowsImage -ImagePath $WindowsImagePath
|
$imageIndexes = Get-WindowsImage -ImagePath $WindowsImagePath
|
||||||
|
|
||||||
# Get the ImageName of ImageIndex 1 if an ISO was specified, else use ImageIndex 4 - this is usually Home or Education SKU on ESD MCT media
|
# Normalize SKU and determine if Desktop Experience is explicitly requested (Server only)
|
||||||
if ($ISOPath) {
|
$normalizedWindowsSKU = $WindowsSKU.Trim()
|
||||||
if ($WindowsSKU -notmatch "Standard|Datacenter") {
|
$isDesktopExperienceRequested = $normalizedWindowsSKU -match '\(Desktop Experience\)'
|
||||||
$imageIndex = $imageIndexes | Where-Object ImageIndex -eq 1
|
$normalizedWindowsSKU = $normalizedWindowsSKU -replace '\s*\(Desktop Experience\)\s*', ''
|
||||||
$WindowsImage = $imageIndex.ImageName.Substring(0, 10)
|
|
||||||
}
|
# Map user-selected SKU to language-independent EditionId values
|
||||||
else {
|
# Notes:
|
||||||
$imageIndex = $imageIndexes | Where-Object ImageIndex -eq 1
|
# - Client: EditionId values are stable across languages (e.g. Professional, Core, Education)
|
||||||
$WindowsImage = $imageIndex.ImageName.Substring(0, 19)
|
# - Server: Desktop Experience vs Core is differentiated by InstallationType (EditionId is the same)
|
||||||
}
|
$editionIdCandidates = switch ($normalizedWindowsSKU) {
|
||||||
}
|
'Home' { @('Core') }
|
||||||
else {
|
'Core' { @('Core') }
|
||||||
$imageIndex = $imageIndexes | Where-Object ImageIndex -eq 4
|
|
||||||
$WindowsImage = $imageIndex.ImageName.Substring(0, 10)
|
'Home N' { @('CoreN') }
|
||||||
|
'CoreN' { @('CoreN') }
|
||||||
|
|
||||||
|
'Home Single Language' { @('CoreSingleLanguage') }
|
||||||
|
'CoreSingleLanguage' { @('CoreSingleLanguage') }
|
||||||
|
|
||||||
|
'Education' { @('Education') }
|
||||||
|
'Education N' { @('EducationN') }
|
||||||
|
'EducationN' { @('EducationN') }
|
||||||
|
|
||||||
|
'Pro' { @('Professional') }
|
||||||
|
'Professional' { @('Professional') }
|
||||||
|
|
||||||
|
'Pro N' { @('ProfessionalN') }
|
||||||
|
'ProfessionalN' { @('ProfessionalN') }
|
||||||
|
|
||||||
|
'Pro Education' { @('ProfessionalEducation') }
|
||||||
|
'ProfessionalEducation' { @('ProfessionalEducation') }
|
||||||
|
|
||||||
|
'Pro Education N' { @('ProfessionalEducationN') }
|
||||||
|
'ProfessionalEducationN' { @('ProfessionalEducationN') }
|
||||||
|
|
||||||
|
'Pro for Workstations' { @('ProfessionalWorkstation') }
|
||||||
|
'ProfessionalWorkstation' { @('ProfessionalWorkstation') }
|
||||||
|
|
||||||
|
'Pro N for Workstations' { @('ProfessionalWorkstationN') }
|
||||||
|
'ProfessionalWorkstationN' { @('ProfessionalWorkstationN') }
|
||||||
|
|
||||||
|
'Enterprise' { @('Enterprise') }
|
||||||
|
'Enterprise N' { @('EnterpriseN') }
|
||||||
|
'EnterpriseN' { @('EnterpriseN') }
|
||||||
|
|
||||||
|
'Enterprise LTSC' { @('EnterpriseS') }
|
||||||
|
'Enterprise 2016 LTSB' { @('EnterpriseS') }
|
||||||
|
'EnterpriseS' { @('EnterpriseS') }
|
||||||
|
|
||||||
|
'Enterprise N LTSC' { @('EnterpriseSN') }
|
||||||
|
'Enterprise N 2016 LTSB' { @('EnterpriseSN') }
|
||||||
|
'EnterpriseSN' { @('EnterpriseSN') }
|
||||||
|
|
||||||
|
'IoT Enterprise LTSC' { @('IoTEnterpriseS') }
|
||||||
|
'IoTEnterpriseS' { @('IoTEnterpriseS') }
|
||||||
|
|
||||||
|
'IoT Enterprise N LTSC' { @('IoTEnterpriseSN') }
|
||||||
|
'IoTEnterpriseSN' { @('IoTEnterpriseSN') }
|
||||||
|
|
||||||
|
'Standard' { @('ServerStandard') }
|
||||||
|
'ServerStandard' { @('ServerStandard') }
|
||||||
|
|
||||||
|
'Datacenter' { @('ServerDatacenter') }
|
||||||
|
'ServerDatacenter' { @('ServerDatacenter') }
|
||||||
|
|
||||||
|
default { @() }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Concatenate $WindowsImage and $WindowsSKU (E.g. Windows 11 Pro)
|
# Determine preferred InstallationType for Server images
|
||||||
$ImageNameToFind = "$WindowsImage $WindowsSKU"
|
$preferredInstallationType = $null
|
||||||
|
if ($normalizedWindowsSKU -in @('Standard', 'Datacenter', 'ServerStandard', 'ServerDatacenter')) {
|
||||||
# Find the ImageName in all of the indexes in the image
|
if ($isDesktopExperienceRequested) {
|
||||||
$matchingImageIndex = $imageIndexes | Where-Object ImageName -eq $ImageNameToFind
|
$preferredInstallationType = 'Server'
|
||||||
|
|
||||||
# Return the index that matches exactly
|
|
||||||
if ($matchingImageIndex) {
|
|
||||||
return $matchingImageIndex.ImageIndex
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
$preferredInstallationType = 'Server Core'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# If we can map SKU -> EditionId, attempt a non-interactive match
|
||||||
|
if ($editionIdCandidates.Count -gt 0) {
|
||||||
|
# Build per-index metadata (EditionId, InstallationType) to match deterministically
|
||||||
|
$imageMetadata = @(foreach ($imageIndex in $imageIndexes) {
|
||||||
|
try {
|
||||||
|
$details = Get-WindowsImage -ImagePath $WindowsImagePath -Index $imageIndex.ImageIndex
|
||||||
|
[pscustomobject]@{
|
||||||
|
ImageIndex = $details.ImageIndex
|
||||||
|
ImageName = $details.ImageName
|
||||||
|
ImageSize = $details.ImageSize
|
||||||
|
EditionId = $details.EditionId
|
||||||
|
InstallationType = $details.InstallationType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$null
|
||||||
|
}
|
||||||
|
}) | Where-Object { $null -ne $_ }
|
||||||
|
|
||||||
|
# Match by EditionId first
|
||||||
|
$imageMatches = $imageMetadata | Where-Object { $_.EditionId -in $editionIdCandidates }
|
||||||
|
|
||||||
|
# If this is a Server SKU, prefer the requested InstallationType (Server vs Server Core)
|
||||||
|
if ($null -ne $preferredInstallationType -and $imageMatches.Count -gt 0) {
|
||||||
|
$preferredImageMatches = $imageMatches | Where-Object { $_.InstallationType -eq $preferredInstallationType }
|
||||||
|
if ($preferredImageMatches.Count -gt 0) {
|
||||||
|
$imageMatches = $preferredImageMatches
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# If multiple matches remain, pick the largest image (Desktop Experience tends to be larger)
|
||||||
|
if ($imageMatches.Count -gt 0) {
|
||||||
|
$bestMatch = $imageMatches | Sort-Object -Property ImageSize -Descending | Select-Object -First 1
|
||||||
|
WriteLog "Selected Windows image index $($bestMatch.ImageIndex) (SKU='$WindowsSKU', EditionId='$($bestMatch.EditionId)', InstallationType='$($bestMatch.InstallationType)'): $($bestMatch.ImageName)"
|
||||||
|
return $bestMatch.ImageIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Final fallback: prompt the user to select an ImageName
|
||||||
# Look for the numbers 10, 11, 2016, 2019, 2022+ in the ImageName
|
# Look for the numbers 10, 11, 2016, 2019, 2022+ in the ImageName
|
||||||
$relevantImageIndexes = $imageIndexes | Where-Object { ($_.ImageName -match "(10|11|2016|2019|202\d)") }
|
$relevantImageIndexes = $imageIndexes | Where-Object { ($_.ImageName -match "(10|11|2016|2019|202\d)") }
|
||||||
|
|
||||||
|
WriteLog "No matching image index found for SKU '$WindowsSKU' in '$WindowsImagePath'. Prompting user to select an ImageName."
|
||||||
|
|
||||||
while ($true) {
|
while ($true) {
|
||||||
# Present list of ImageNames to the end user if no matching ImageIndex is found
|
# Present list of ImageNames to the end user if no matching ImageIndex is found
|
||||||
Write-Host "No matching ImageIndex found for $ImageNameToFind. Please select an ImageName from the list below:"
|
Write-Host "No matching ImageIndex found for Windows SKU '$WindowsSKU'. Please select an ImageName from the list below:"
|
||||||
|
|
||||||
$i = 1
|
$i = 1
|
||||||
$relevantImageIndexes | ForEach-Object {
|
$relevantImageIndexes | ForEach-Object {
|
||||||
@@ -2437,6 +2529,7 @@ function Get-Index {
|
|||||||
$selectedImage = $relevantImageIndexes[$inputValue - 1]
|
$selectedImage = $relevantImageIndexes[$inputValue - 1]
|
||||||
|
|
||||||
if ($selectedImage) {
|
if ($selectedImage) {
|
||||||
|
WriteLog "User selected Windows image index $($selectedImage.ImageIndex) (SKU='$WindowsSKU'): $($selectedImage.ImageName)"
|
||||||
return $selectedImage.ImageIndex
|
return $selectedImage.ImageIndex
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2444,7 +2537,6 @@ function Get-Index {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#Create VHDX
|
#Create VHDX
|
||||||
function New-ScratchVhdx {
|
function New-ScratchVhdx {
|
||||||
|
|||||||
Reference in New Issue
Block a user