public/Update-OSDeployBootProfilePreview.ps1

#Requires -PSEdition Core
#Requires -Version 7.4

function Update-OSDeployBootProfilePreview {
    <#
    .SYNOPSIS
        Updates an OSDeploy Boot profile
 
    .DESCRIPTION
        Updates an existing profile without building media. The command preserves saved
        languages, international settings, timezone, and options unless replacements are
        supplied. It reruns the shared driver, WinPE script, media script, WinPEStartup
        profile, and wallpaper selectors.
 
        Driver, script, and startup-profile selections replace their saved values; canceling
        those selectors clears the corresponding value. Canceling wallpaper selection
        preserves the current profile wallpaper.
 
    .PARAMETER ProfileName
        Specifies the exact canonical name of an existing profile. Values tab-complete from
        architecture-specific profile directories containing osdeployboot.json.
 
    .PARAMETER Languages
        Replaces the saved Windows ADK language identifiers. Specify * to request all
        additional language directories during a later build.
 
    .PARAMETER SetAllIntl
        Replaces the saved international-settings value.
 
    .PARAMETER SetInputLocale
        Replaces the saved WinPE input locale.
 
    .PARAMETER SetTimeZone
        Replaces the saved timezone. Values are validated against tzutil /l.
 
    .PARAMETER Options
        Replaces the saved optional WinPE features. Valid values are pwsh and dart.
 
    .EXAMPLE
        PS> Update-OSDeployBootProfilePreview -ProfileName 'Contoso-amd64'
 
        Reselects shared content while preserving saved regional settings and options.
 
    .EXAMPLE
        PS> Update-OSDeployBootProfilePreview -ProfileName 'Contoso-amd64' -Options pwsh -WhatIf
 
        Shows the update action without displaying selectors or modifying the profile.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-09-03
 
        Requires a valid Recast Software Community License and write access to the selected
        profile directory.
 
    .LINK
        Build-OSDeployBoot
 
    .LINK
        New-OSDeployBootProfilePreview
 
    .LINK
        Delete-OSDeployBootProfilePreview
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete)

            $ProfilePath = Join-Path $env:ProgramData 'OSDeployCore\boot-assets\osdeployboot-profiles'
            Get-ChildItem -LiteralPath $ProfilePath -Directory -ErrorAction SilentlyContinue |
                Where-Object {
                    $_.Name -like "$wordToComplete*" -and
                    (Test-Path -LiteralPath (Join-Path $_.FullName 'osdeployboot.json') -PathType Leaf)
                } |
                Sort-Object Name |
                ForEach-Object {
                    $CompletionText = if ($_.Name -match '\s') { "'$($_.Name.Replace("'", "''"))'" } else { $_.Name }
                    [System.Management.Automation.CompletionResult]::new(
                        $CompletionText,
                        $_.Name,
                        [System.Management.Automation.CompletionResultType]::ParameterValue,
                        $_.FullName
                    )
                }
        })]
        [System.String]
        $ProfileName,

        [ValidateSet(
            '*', 'ar-sa', 'bg-bg', 'cs-cz', 'da-dk', 'de-de', 'el-gr',
            'en-gb', 'en-us', 'es-es', 'es-mx', 'et-ee', 'fi-fi',
            'fr-ca', 'fr-fr', 'he-il', 'hr-hr', 'hu-hu', 'it-it',
            'ja-jp', 'ko-kr', 'lt-lt', 'lv-lv', 'nb-no', 'nl-nl',
            'pl-pl', 'pt-br', 'pt-pt', 'ro-ro', 'ru-ru', 'sk-sk',
            'sl-si', 'sr-latn-rs', 'sv-se', 'th-th', 'tr-tr',
            'uk-ua', 'zh-cn', 'zh-tw'
        )]
        [System.String[]]
        $Languages,

        [System.String]
        $SetAllIntl,

        [System.String]
        $SetInputLocale,

        [ValidateScript({
            $TimeZones = tzutil /l
            $ValidOptions = foreach ($TimeZone in $TimeZones) {
                if (($TimeZones.IndexOf($TimeZone) - 1) % 3 -eq 0) {
                    $TimeZone.Trim()
                }
            }
            $ValidOptions -contains $_
        })]
        [System.String]
        $SetTimeZone,

        [ValidateSet('pwsh', 'dart')]
        [System.String[]]
        $Options
    )

    Write-Verbose "[$($MyInvocation.MyCommand.Name)] Start"

    if (-not (Test-OSDeployLicenseGate -CommandName $MyInvocation.MyCommand.Name)) {
        return
    }
    if ([System.String]::IsNullOrWhiteSpace($ProfileName)) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] ProfileName cannot be empty."
    }

    $SelectedProfile = Select-BuildOSDeployBootProfile -Name $ProfileName -SkipPathValidation -SkipTokenMigration
    if (-not $SelectedProfile) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Build profile was not found or could not be read: $ProfileName"
    }
    if (-not $PSCmdlet.ShouldProcess($SelectedProfile.FullName, 'Update OSDeploy Boot profile')) {
        return
    }

    Initialize-OSDeployCorePaths
    $SelectedProfile = Select-BuildOSDeployBootProfile -Name $ProfileName -SkipPathValidation -SkipTokenMigration
    if (-not $SelectedProfile) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Build profile was not found after Boot-Assets initialization: $ProfileName"
    }
    $ExistingProfile = Get-Content -LiteralPath $SelectedProfile.FullName -Raw -ErrorAction Stop | ConvertFrom-Json -ErrorAction Stop
    $Architecture = [System.String]$ExistingProfile.Architecture
    if ($Architecture -notin @('amd64', 'arm64')) {
        throw "[$(Get-Date -Format s)] [$($MyInvocation.MyCommand.Name)] Build profile has an unsupported architecture: $Architecture"
    }

    if (-not $PSBoundParameters.ContainsKey('Languages')) { [System.String[]]$Languages = $ExistingProfile.Languages }
    if (-not $PSBoundParameters.ContainsKey('SetAllIntl')) { $SetAllIntl = $ExistingProfile.SetAllIntl }
    if (-not $PSBoundParameters.ContainsKey('SetInputLocale')) { $SetInputLocale = $ExistingProfile.SetInputLocale }
    if (-not $PSBoundParameters.ContainsKey('SetTimeZone')) { $SetTimeZone = $ExistingProfile.SetTimeZone }
    if (-not $PSBoundParameters.ContainsKey('Options')) { [System.String[]]$Options = $ExistingProfile.Options }

    $SelectedContent = Select-OSDeployBootProfileContent -Architecture $Architecture
    $BuildProfile = [ordered]@{
        Name                 = [System.String]$SelectedProfile.Name
        Architecture         = $Architecture
        Languages            = [System.String[]]$Languages
        SetAllIntl           = [System.String]$SetAllIntl
        SetInputLocale       = [System.String]$SetInputLocale
        SetTimeZone          = [System.String]$SetTimeZone
        Options              = [System.String[]]$Options
        WinPEStartupProfile  = ConvertTo-OSDeployBuildProfileToken $SelectedContent.WinPEStartupProfile
        WinPEDriver          = ConvertTo-OSDeployBuildProfileToken $SelectedContent.WinPEDriver
        WinPEScript          = ConvertTo-OSDeployBuildProfileToken $SelectedContent.WinPEScript
        MediaScript          = ConvertTo-OSDeployBuildProfileToken $SelectedContent.MediaScript
    }

    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [INFO] Exporting Build Profile to $($SelectedProfile.FullName)"
    $BuildProfile | ConvertTo-Json -Depth 5 -WarningAction SilentlyContinue |
        Out-File -LiteralPath $SelectedProfile.FullName -Encoding utf8 -Force
    Set-OSDeployBootProfileWallpaper -ProfilePath (Split-Path -Path $SelectedProfile.FullName -Parent)

    Write-Verbose "[$($MyInvocation.MyCommand.Name)] End"
}