private/BootMedia/Steps/Select-OSDeployCoreBuildProfile.ps1
|
#Requires -PSEdition Core function Select-OSDeployCoreBuildProfile { <# .SYNOPSIS Displays build profiles in an Out-GridView picker. .DESCRIPTION Scans the OSDRepo\build-profiles\{Architecture} subdirectory for saved JSON profiles and presents them in an Out-GridView for interactive selection. When no Architecture is specified, profiles from both amd64 and arm64 subfolders are shown. Each entry shows key profile properties (Architecture, Languages, TimeZone, etc.) read from the JSON file. The returned object has a FullName property with the full path to the selected JSON file. .OUTPUTS PSCustomObject with FullName property, or $null if no selection is made. .NOTES Author: David Segura Company: Recast Software Change Summary: - Initial version. - Updated output path from builds to boot; moved build-profiles under Repository. - Added JSON content display in Out-GridView showing Architecture, Languages, and other profile properties. - Profiles stored in architecture-specific subfolders (build-profiles\amd64, build-profiles\arm64). - Auto-migrates legacy flat build-profiles\*.json files to the correct arch subfolder on first run. Dependencies: Module Functions: ConvertTo-OSDeployBuildProfileToken, Expand-OSDeployBuildProfileToken, Write-OSDeployCoreProgress Windows Features: Windows ADK WinPE Addon #> [CmdletBinding()] param ( [ValidateSet('amd64', 'arm64')] [System.String] $Architecture ) # Migrate any legacy flat-folder profiles to architecture subfolders $legacyBase = Join-Path $script:OSDeployOSDRepoPath 'build-profiles' if (Test-Path -Path $legacyBase) { $legacyFiles = @(Get-ChildItem -Path $legacyBase -Filter '*.json' -File -ErrorAction SilentlyContinue) foreach ($file in $legacyFiles) { try { $j = Get-Content -LiteralPath $file.FullName -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop $targetArch = if ($j.Architecture -in @('amd64', 'arm64')) { $j.Architecture } else { 'amd64' } $targetDir = Join-Path $legacyBase $targetArch if (-not (Test-Path -Path $targetDir)) { New-Item -ItemType Directory -Path $targetDir -Force | Out-Null } $targetFile = Join-Path $targetDir $file.Name if (-not (Test-Path -LiteralPath $targetFile)) { Move-Item -LiteralPath $file.FullName -Destination $targetFile -Force Write-Host -ForegroundColor DarkYellow "[$(Get-Date -format 'yyyy-MM-ddTHH:mm:ss')] Migrated build profile to $targetFile" } } catch { Write-Warning "Could not migrate build profile: $($file.FullName) - $($_.Exception.Message)" } } } if ($Architecture) { $profilePath = Join-Path $script:OSDeployOSDRepoPath 'build-profiles' $Architecture if (-not (Test-Path -Path $profilePath)) { return $null } $results = @(Get-ChildItem -Path $profilePath -Filter '*.json' -File -ErrorAction SilentlyContinue) } else { $profilePath = Join-Path $script:OSDeployOSDRepoPath 'build-profiles' $results = @() foreach ($arch in @('amd64', 'arm64')) { $archPath = Join-Path $script:OSDeployOSDRepoPath 'build-profiles' $arch if (Test-Path -Path $archPath) { $results += @(Get-ChildItem -Path $archPath -Filter '*.json' -File -ErrorAction SilentlyContinue) } } } if ($results.Count -gt 0) { Write-Host -ForegroundColor DarkGreen "[$(Get-Date -format 'yyyy-MM-ddTHH:mm:ss')] Build Profiles are saved in $profilePath" Write-OSDeployCoreProgress 'Select an OSDeploy Build Profile (Cancel to create a new one)' $profileObjects = foreach ($file in $results) { try { $json = Get-Content -LiteralPath $file.FullName -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } catch { $json = $null } $driverCount = if ($json.WinPEDriver) { @($json.WinPEDriver).Count } else { 0 } [PSCustomObject]@{ Name = $file.BaseName LastModified = $file.LastWriteTime Architecture = $json.Architecture Languages = ($json.Languages -join ', ') SetInputLocale = $json.SetInputLocale SetAllIntl = $json.SetAllIntl SetTimeZone = $json.SetTimeZone WinPEStartupProfile = $json.WinPEStartupProfile WinPECustomWallpaper = $json.WinPECustomWallpaper WinPEAppScript = ($json.WinPEAppScript -join ', ') WinPEScript = ($json.WinPEScript -join ', ') WinPEMediaScript = ($json.WinPEMediaScript -join ', ') WinPEDrivers = $driverCount FullName = $file.FullName } } while ($true) { $selected = $profileObjects | Out-GridView -OutputMode Single -Title 'Select an OSDeploy Build Profile (Cancel to create a new one)' if (-not $selected) { return $null } # Validate all path-bearing properties in the selected profile try { $profileJson = Get-Content -LiteralPath $selected.FullName -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop } catch { Write-Warning "Could not read build profile: $($selected.FullName)" Write-Warning $_.Exception.Message continue } $pathProperties = [ordered]@{ WinPEDriver = $profileJson.WinPEDriver WinPEAppScript = $profileJson.WinPEAppScript WinPEScript = $profileJson.WinPEScript WinPEMediaScript = $profileJson.WinPEMediaScript WinPEStartupProfile = $profileJson.WinPEStartupProfile WinPECustomWallpaper = $profileJson.WinPECustomWallpaper } # Expand tokens to absolute paths before Test-Path validation $expandedProperties = [ordered]@{} foreach ($prop in $pathProperties.Keys) { $expandedProperties[$prop] = Expand-OSDeployBuildProfileToken $pathProperties[$prop] } $invalidPaths = foreach ($prop in $expandedProperties.Keys) { foreach ($entry in @($expandedProperties[$prop])) { if ($entry -and -not (Test-Path -LiteralPath $entry)) { [PSCustomObject]@{ Property = $prop; Path = $entry } } } } if ($invalidPaths) { Write-Warning "Build profile has path errors: $($selected.FullName)" foreach ($bad in $invalidPaths) { Write-Warning "[$($bad.Property)] Path not found: $($bad.Path)" } Write-Warning 'Fix the build profile and try again, or cancel to create a new one.' continue } # Auto-migrate: re-tokenize paths and rewrite the file if anything changed $tokenizedProperties = [ordered]@{} foreach ($prop in $pathProperties.Keys) { $tokenizedProperties[$prop] = ConvertTo-OSDeployBuildProfileToken $pathProperties[$prop] } $needsMigration = $false foreach ($prop in $pathProperties.Keys) { $original = @($pathProperties[$prop]) | Where-Object { $_ } $tokenized = @($tokenizedProperties[$prop]) | Where-Object { $_ } if (($original -join '|') -ine ($tokenized -join '|')) { $needsMigration = $true break } } if ($needsMigration) { Write-Verbose "Migrating build profile to use module path tokens: $($selected.FullName)" $updatedProfile = [ordered]@{ Architecture = $profileJson.Architecture WinPEDriver = $tokenizedProperties['WinPEDriver'] WinPEAppScript = $tokenizedProperties['WinPEAppScript'] WinPEScript = $tokenizedProperties['WinPEScript'] WinPEMediaScript = $tokenizedProperties['WinPEMediaScript'] WinPEStartupProfile = if ($tokenizedProperties['WinPEStartupProfile']) { $tokenizedProperties['WinPEStartupProfile'][0] } else { $null } WinPECustomWallpaper = if ($tokenizedProperties['WinPECustomWallpaper']) { $tokenizedProperties['WinPECustomWallpaper'][0] } else { $null } Languages = $profileJson.Languages SetAllIntl = $profileJson.SetAllIntl SetInputLocale = $profileJson.SetInputLocale SetTimeZone = $profileJson.SetTimeZone } try { $updatedProfile | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue | Out-File -LiteralPath $selected.FullName -Encoding utf8 -Force } catch { Write-Warning "Could not migrate build profile tokens: $($_.Exception.Message)" } } return $selected } } return $null } |