Private/core-dev/Start-RecastOSDCloudGUI.ps1

function Start-RecastOSDCloudGUI {
    <#
    .SYNOPSIS
    Starts the Recast OSDCloud graphical deployment workflow.
 
    .DESCRIPTION
    Initializes device and deployment context, discovers matching operating systems,
    resolves driver pack metadata for the current device (or supplied overrides),
    validates required dependencies, and then prepares global state consumed by
    the Recast OSDCloud GUI workflow. The GUI launches only when the Force switch
    is supplied.
 
    .PARAMETER BrandName
    Sets the branding text shown in the OSDCloud GUI title/header.
    Defaults to the module resource value.
 
    .PARAMETER BrandColor
    Sets the branding color shown in the OSDCloud GUI.
    Provide a hex color value, for example '#0096D6'.
 
    .PARAMETER OSArchitecture
    Operating system architecture used when selecting catalog entries.
    Supported values are amd64 and arm64.
 
    .PARAMETER OSReleaseID
    Operating system release identifier used for catalog selection.
 
    .PARAMETER OSLanguageCode
    Operating system language code used for catalog selection.
    If not specified, the value is inferred from the current keyboard layout.
 
    .PARAMETER OSActivation
    Operating system activation channel used for catalog selection.
 
    .PARAMETER OSEdition
    Operating system edition used for catalog selection.
    Valid values depend on OSArchitecture at runtime.
 
    .PARAMETER OSDManufacturer
    Overrides the detected computer manufacturer for driver pack matching.
    If omitted, the detected device manufacturer is used.
 
    .PARAMETER OSDModel
    Overrides the detected computer model for logging and context alignment.
    If omitted, the detected device model is used.
 
    .PARAMETER OSDProduct
    Overrides the detected computer product/system ID for driver pack matching.
    If omitted, the detected device product value is used.
 
    .PARAMETER v2
    Legacy compatibility switch. This parameter is non-functional and retained
    temporarily to avoid breaking existing scripts.
 
    .PARAMETER Force
    Confirms that OSDCloud should run after initialization. This switch is required
    to launch the GUI because the workflow can modify the deployment disk.
 
    .EXAMPLE
    Start-RecastOSDCloudGUI -Force
    Starts OSDCloud GUI using detected device values and default branding.
 
    .EXAMPLE
    Start-RecastOSDCloudGUI -BrandName 'Contoso' -BrandColor '#005A9C' -Force
    Starts OSDCloud GUI with custom branding.
 
    .EXAMPLE
    Start-RecastOSDCloudGUI -OSArchitecture arm64 -OSEdition Pro -OSReleaseID 24H2 -Force
    Starts OSDCloud GUI with an ARM64 Windows 11 Pro 24H2 deployment selection.
 
    .LINK
    https://github.com/OSDeploy/OSD/tree/master/docs
 
    .NOTES
    Author: David Segura - Recast Software
    2026-07-09 - Standardized comment-based help metadata and links.
    2026-07-09 - The -v2 parameter is deprecated and will be removed in a future release.
    2026-07-14 - Added complete parameter help coverage and updated examples.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false, HelpMessage = 'Custom brand text for the OSDCloud GUI.')]
        [Alias('Brand')]
        [ValidateNotNullOrEmpty()]
        [string]
        $BrandName = $Global:OSDModuleResource.StartOSDCloudGUI.BrandName,

        [Parameter(Mandatory = $false, HelpMessage = 'Brand color in hex format (for example #0096D6).')]
        [Alias('Color')]
        [ValidatePattern('^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$')]
        [string]
        $BrandColor = $Global:OSDModuleResource.StartOSDCloudGUI.BrandColor,

        [Parameter(Mandatory = $false, HelpMessage = 'Operating system architecture for deployment selection.')]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('amd64','arm64')]
        [string]
        $OSArchitecture = $env:PROCESSOR_ARCHITECTURE,

        [Parameter(Mandatory = $false, HelpMessage = 'Operating system release identifier for deployment selection.')]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('26H2','25H2','24H2','23H2','22H2','21H2')]
        [string]
        $OSReleaseID = '26H2',

        [Parameter(Mandatory = $false, HelpMessage = 'Operating system language code for deployment selection.')]
        [ValidateNotNullOrEmpty()]
        [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'
        )]
        [string]
        $OSLanguageCode,

        [Parameter(Mandatory = $false, HelpMessage = 'Operating system activation channel for deployment selection.')]
        [ValidateNotNullOrEmpty()]
        [ValidateSet('Retail','Volume')]
        [string]
        $OSActivation = 'Retail',

        [Parameter(Mandatory = $false, HelpMessage = 'Operating system edition identifier for deployment selection.')]
        [ValidateNotNullOrEmpty()]
        [string]
        $OSEdition = 'Pro',

        [Parameter(Mandatory = $false, HelpMessage = 'Optional manufacturer override used for driver pack selection.')]
        [ValidateNotNullOrEmpty()]
        [string]
        $OSDManufacturer,

        [Parameter(Mandatory = $false, HelpMessage = 'Optional model override used for driver pack selection.')]
        [ValidateNotNullOrEmpty()]
        [string]
        $OSDModel,

        [Parameter(Mandatory = $false, HelpMessage = 'Optional product/system ID override used for driver pack selection.')]
        [ValidateNotNullOrEmpty()]
        [string]
        $OSDProduct,

        [Parameter(Mandatory = $false, HelpMessage = 'Legacy compatibility switch. This parameter is deprecated and non-functional.')]
        [System.Management.Automation.SwitchParameter]
        $v2,

        [Parameter(Mandatory = $false, HelpMessage = 'Required to launch the OSDCloud GUI workflow.')]
        [switch]
        $Force
    )
    #=================================================
    # Parameter Changes
    if ($v2) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] v2 parameter is deprecated and non-functional and will be removed in a future release. Please remove the v2 parameter from your command."
    }
    #=================================================
    # Emit function/version context and surface legacy parameter usage.
    $ModuleVersion = $($MyInvocation.MyCommand.Module.Version)
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] $ModuleVersion"
    #=================================================
    # Resolve architecture-specific edition constraints and normalize edition metadata.
    $OSEditionValuesByArchitecture = @{
        amd64 = @('Home','Home N','Education','Education N','Pro','Pro N','Enterprise','Enterprise N')
        arm64 = @('Home','Pro','Enterprise')
    }
    if (-not $OSEditionValuesByArchitecture.ContainsKey($OSArchitecture)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unsupported OSArchitecture '$OSArchitecture'."
    }

    $OSEditionValues = $OSEditionValuesByArchitecture[$OSArchitecture]
    if ($OSEdition -notin $OSEditionValues) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSEdition '$OSEdition' is not valid for OSArchitecture '$OSArchitecture'. Valid values: $($OSEditionValues -join ', ')."
    }

    $OSEditionIdByName = @{
        'Home' = 'Core'
        'Home N' = 'CoreN'
        'Education' = 'Education'
        'Education N' = 'EducationN'
        'Pro' = 'Professional'
        'Pro N' = 'ProfessionalN'
        'Enterprise' = 'Enterprise'
        'Enterprise N' = 'EnterpriseN'
    }
    $OSEditionId = $OSEditionIdByName[$OSEdition]
    #=================================================
    # OSDCoreDevice
    if (-not ($global:OSDCoreDevice)) {
        Initialize-OSDCoreDevice
    }
    #=================================================
    # OSDCoreOperatingSystems
    if ($PSBoundParameters.ContainsKey('OSArchitecture')) {
        $global:OSDCoreOperatingSystems = Get-OSDCoreOperatingSystems | Where-Object { $_.Architecture -match "$OSArchitecture" }
    }

    # Validate that the OS catalog was preloaded for this architecture.
    if (-not $global:OSDCoreOperatingSystems) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Unable to load Operating Systems"
    }

    # Automatically determine default OSLanguageCode from the detected keyboard layout if not explicitly provided.
    if (-not $PSBoundParameters.ContainsKey('OSLanguageCode')) {
        if (Get-Command -Name 'Convert-KeyboardLayoutToLanguageCode' -ErrorAction SilentlyContinue) {
            $OSLanguageCode = Convert-KeyboardLayoutToLanguageCode -KeyboardLayout $global:OSDCoreDevice.KeyboardLayout -FallbackLanguageCode 'en-US'
        }
        else {
            $OSLanguageCode = 'en-US'
        }
    }

    # Select the matching operating system object from the preloaded catalog based on the provided or default parameters.
    $global:OSDCoreOperatingSystemCloudObject = $global:OSDCoreOperatingSystems | `
        Where-Object { $_.Activation -eq $OSActivation } | `
        Where-Object { $_.Architecture -match $OSArchitecture } | `
        Where-Object { $_.Language -eq $OSLanguageCode } | `
        Where-Object { $_.ReleaseID -eq $OSReleaseID } | `
        Where-Object { $_.Version -eq 'Windows 11' }

    if (-not $global:OSDCoreOperatingSystemCloudObject) {
        throw "[$(Get-Date -format s)] Unable to find a matching operating system object for OSReleaseID '$OSReleaseID', OSArchitecture '$OSArchitecture', Activation '$OSActivation', and Language '$OSLanguageCode'."
    }
    #=================================================
    # OSDCoreDriverPacks
    # Resolve driver pack metadata for the detected device, with optional manufacturer overrides supplied by the caller.
    if ($PSBoundParameters.ContainsKey('OSDManufacturer')) {
        $global:OSDCoreDevice.OSDManufacturer = $OSDManufacturer
        $global:OSDCoreDriverPacks = Initialize-ModuleCoreDriverPacks -OSDManufacturer $OSDManufacturer
    }

    # Resolve driver pack metadata for the detected device, with optional model overrides supplied by the caller.
    if ($PSBoundParameters.ContainsKey('OSDModel')) {
        $global:OSDCoreDevice.OSDModel = $OSDModel
    }

    # Resolve driver pack metadata for the detected device, with optional product overrides supplied by the caller.
    if ($PSBoundParameters.ContainsKey('OSDProduct')) {
        $global:OSDCoreDevice.OSDProduct = $OSDProduct
    }
    $global:OSDCoreDriverPackCloudObject = $global:OSDCoreDriverPacks | Where-Object { $_.SystemId -match $global:OSDCoreDevice.OSDProduct } | Select-Object -First 1
    #================================================
    # Output OSDCore Objects
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDManufacturer: $($global:OSDCoreDevice.OSDManufacturer)"
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDModel: $($global:OSDCoreDevice.OSDModel)"
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] OSDProduct: $($global:OSDCoreDevice.OSDProduct)"
    if ($global:OSDCoreDriverPackCloudObject) {
        Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] OSDCoreDriverPackCloudObject"
        $global:OSDCoreDriverPackCloudObject | Out-Host
    }
    if ($global:OSDCoreOperatingSystemCloudObject) {
        Write-Host -ForegroundColor DarkCyan "[$(Get-Date -format s)] OSDCoreOperatingSystemCloudObject"
        $global:OSDCoreOperatingSystemCloudObject | Out-Host
    }
    #=================================================
    # Dependency guard: OSDCloud relies on curl.exe for downloads.
    if (-not (Get-Command -Name 'curl.exe' -ErrorAction SilentlyContinue)) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloud requires 'curl.exe' which is not available on this system. Please ensure curl.exe is available in the system PATH."
    }
    #=================================================
    # Detect candidate deployment disk(s).
    $DeploymentDiskObject = Get-OSDCoreDeploymentDisk

    # Stop immediately if no eligible local deployment disk is found.
    if (-not $DeploymentDiskObject) {
        throw "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] OSDCloud requires at least one Local Disk, but no compatible Local Disk was found."
    }
    # If multiple disks are discovered, keep behavior deterministic by using
    # the first disk and logging all candidates for troubleshooting visibility.
    if (@($DeploymentDiskObject).Count -gt 1) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] Multiple Local Disks were found. OSDCloud will default to DiskNumber: $($DeploymentDiskObject[0].DiskNumber)"
        $DeploymentDiskObject | ForEach-Object {
            Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] DiskNumber: $($_.DiskNumber), FriendlyName: $($_.FriendlyName), Size(GB): $([math]::Round($_.Size / 1GB, 2))"
        }
    }
    # Limit to the selected disk object expected by downstream workflow code.
    $DeploymentDiskObject = $DeploymentDiskObject | Select-Object -First 1
    #=================================================
    # Build deployment state consumed by the broader OSDCloud workflow.
    $global:RecastOSDCloud = $null
    $global:RecastOSDCloud = [ordered]@{
        DeploymentDiskObject  = $DeploymentDiskObject
        # DriverFolderName = $null
        # DriverFolderNames = @()
        # DriverFolderPath = $null
        # DriverFolderPaths = @()
        # DriverFolderSelections = @()
        # DriverPackName = $DriverPackName
        # DriverPackCloudObject = $DriverPackCloudObject
        # DriverPackValues = [array]$DriverPackValues
        # Flows = [array]$global:OSDCloudWorkflowTasks
        Function              = $($MyInvocation.MyCommand.Name)
        # ImageFileName = $ImageFileName
        # ImageFileUrl = $ImageFileUrl
        LaunchMethod          = 'RecastOSDCloudGUI'
        Module                = $($MyInvocation.MyCommand.Module.Name)
        OperatingSystem       = $OperatingSystem
        # OperatingSystemCloudObject = $OperatingSystemCloudObject
        # OperatingSystemValues = $OperatingSystemValues
        OSActivation          = $OSActivation
        OSActivationValues    = $OSActivationValues
        OSArchitecture        = $OSArchitecture
        OSBuild               = $OSBuild
        OSBuildVersion        = $OSBuildVersion
        OSEdition             = $OSEdition
        OSEditionId           = $OSEditionId
        OSEditionValues       = $OSEditionValues
        OSLanguageCode        = $OSLanguageCode
        OSLanguageCodeValues  = $OSLanguageCodeValues
        OSVersion             = $OSVersion
        TimeStart             = $null
        # WorkflowName = $WorkflowName
        # WorkflowTaskName = $WorkflowTaskName
        # WorkflowTaskObject = $WorkflowTaskObject
    }
    #================================================
    # Build GUI configuration defaults from module resources and current device context.
    $Global:OSDCloudGUI = $null
    $Global:OSDCloudGUI = [ordered]@{
        Function              = [System.String]'Start-RecastOSDCloudGUI'
        LaunchMethod          = [System.String]'OSDCloudGUI'
        AutomateConfiguration = $null
        AutomateJsonFile      = $null
        BrandName             = [System.String]$BrandName
        BrandColor            = [System.String]$BrandColor
        ComputerManufacturer  = [System.String]$OSDManufacturer
        ComputerModel         = [System.String]$OSDModel
        ComputerProduct       = [System.String]$OSDProduct
        # DriverPack = $null
        # DriverPacks = $null
        # DriverPackName = $DriverPackName
        # DriverPackCloudObject = $DriverPackCloudObject
        # DriverPackValues = [array]$DriverPackValues
        IsOnBattery           = [System.Boolean]$global:OSDCoreDevice.IsOnBattery
        OSActivation          = [System.String]$Global:OSDModuleResource.OSDCloud.Default.Activation
        OSEdition             = [System.String]$Global:OSDModuleResource.OSDCloud.Default.Edition
        OSLanguage            = [System.String]$Global:OSDModuleResource.OSDCloud.Default.Language
        OSImageIndex          = [System.Int32]$Global:OSDModuleResource.OSDCloud.Default.ImageIndex
        OSName                = [System.String]$Global:OSDModuleResource.OSDCloud.Default.Name
        OSReleaseID           = [System.String]$Global:OSDModuleResource.OSDCloud.Default.ReleaseID
        OSVersion             = [System.String]$Global:OSDModuleResource.OSDCloud.Default.Version
        OSActivationValues    = [array]$Global:OSDModuleResource.OSDCloud.Values.Activation
        OSEditionValues       = [array]$Global:OSDModuleResource.OSDCloud.Values.Edition
        OSLanguageValues      = [array]$Global:OSDModuleResource.OSDCloud.Values.Language
        OSNameValues          = [array]$Global:OSDModuleResource.OSDCloud.Values.Name
        OSNameARM64Values     = [array]$Global:OSDModuleResource.OSDCloud.Values.NameARM64
        OSReleaseIDValues     = [array]$Global:OSDModuleResource.OSDCloud.Values.ReleaseID
        OSVersionValues       = [array]$Global:OSDModuleResource.OSDCloud.Values.Version

        ClearDiskConfirm      = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.ClearDiskConfirm
        restartComputer       = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.restartComputer

        updateDiskDrivers     = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.updateDiskDrivers
        updateFirmware        = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.updateFirmware
        updateNetworkDrivers  = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.updateNetworkDrivers
        updateSCSIDrivers     = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.updateSCSIDrivers
        SyncMSUpCatDriverUSB  = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.SyncMSUpCatDriverUSB

        OEMActivation         = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.OEMActivation
        WindowsUpdate         = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.WindowsUpdate
        WindowsUpdateDrivers  = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.WindowsUpdateDrivers
        WindowsDefenderUpdate = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.WindowsDefenderUpdate

        HPIAALL               = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPIAALL
        HPIADrivers           = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPIADrivers
        HPIAFirmware          = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPIAFirmware
        HPIASoftware          = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPIASoftware
        HPTPMUpdate           = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPTPMUpdate
        HPBIOSUpdate          = [System.Boolean]$Global:OSDModuleResource.StartOSDCloudGUI.HPBIOSUpdate

        TimeStart             = [datetime](Get-Date)
    }
    #================================================
    # Export baseline GUI settings, then look for external automation JSON
    # on non-C drives to override interactive defaults when present.
    Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Exporting default configuration to $env:Temp\Start-RecastOSDCloudGUI.json"
    $Global:OSDCloudGUI | ConvertTo-Json -Depth 10 | Out-File -FilePath "$env:TEMP\Start-RecastOSDCloudGUI.json" -Force

    $Global:OSDCloudGUI.AutomateJsonFile = Get-PSDrive -PSProvider FileSystem | Where-Object { $_.Name -ne 'C' } | ForEach-Object {
        Get-ChildItem "$($_.Root)OSDCloud\Automate" -Include "Start-RecastOSDCloudGUI.json" -File -Force -Recurse -ErrorAction Ignore
    }
    if ($Global:OSDCloudGUI.AutomateJsonFile) {
        foreach ($Item in $Global:OSDCloudGUI.AutomateJsonFile) {
            Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $($Item.FullName)"
            $Global:OSDCloudGUI.AutomateConfiguration = Get-Content -Path "$($Item.FullName)" -Raw | ConvertFrom-Json -ErrorAction "Stop" | ConvertTo-Hashtable
        }
    }
    if ($Global:OSDCloudGUI.AutomateConfiguration) {
        # Apply each discovered automation setting onto the active GUI config.
        foreach ($Key in $Global:OSDCloudGUI.AutomateConfiguration.Keys) {
            $Global:OSDCloudGUI.$Key = $Global:OSDCloudGUI.AutomateConfiguration.$Key
        }
    }
    #================================================
    $Global:OSDCloudGuiBranding = @{
        Title = $Global:OSDCloudGUI.BrandName
        Color = $Global:OSDCloudGUI.BrandColor
    }
    Write-Host -ForegroundColor Green "OSDCloudGUI Configuration"
    $Global:OSDCloudGUI | Out-Host
    #================================================
    if (-not $Force) {
        Write-Warning "[$(Get-Date -format s)] [$($MyInvocation.MyCommand.Name)] -Force is required to run OSDCloud. Initialization completed, but the GUI was not launched."
        return
    }
    # Launch the WPF GUI entry point with the prepared global state.
    & "$($MyInvocation.MyCommand.Module.ModuleBase)\Projects\RecastOSDCloudGUI\MainWindow.ps1"
    Start-Sleep -Seconds 2
    #================================================
}