private/Initialize-OSDeployCoreBuildProfilePaths.ps1

#Requires -PSEdition Core

function Initialize-OSDeployCoreBuildProfilePaths {
    <#
    .SYNOPSIS
        Initializes profile-local content paths for an OSDeploy build profile
 
    .DESCRIPTION
        Creates missing WinPEStartup\profiles and WinPEStartup\assets directories below the
        specified build profile path. Legacy WinPEStartup\scripts content is migrated into
        WinPEStartup\assets without overwriting destination conflicts.
 
        Legacy build-mediascript and build-winpescript content is migrated to boot-mediascript
        and boot-winpescript without overwriting destination conflicts. The first alphabetically
        sorted JPG in a legacy build-winpewallpaper directory is
        migrated to wallpaper.jpg at the profile root. An existing root wallpaper is not
        overwritten, and additional legacy content is preserved with a warning. A legacy
        directory is removed only after all of its content is migrated.
 
    .PARAMETER Path
        Specifies the build profile directory that contains osdeployboot.json. This function
        does not validate that the file exists.
 
    .EXAMPLE
        PS> Initialize-OSDeployCoreBuildProfilePaths -Path 'C:\ProgramData\OSDeployCore\boot-assets\osdeployboot-profiles\Default-amd64'
 
        Creates the profile-local WinPE startup directories and migrates legacy profile content.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Version: 1.0.0
        Date: 2026-08-28
 
        The supplied path and its parent location must be writable. This function creates
        directories, moves legacy profile content, and may write migration warnings.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [System.String]
        $Path
    )

    $FunctionName = $MyInvocation.MyCommand.Name
    $LegacyScriptsPath = Join-Path $Path 'WinPEStartup' 'scripts'
    $AssetsPath = Join-Path $Path 'WinPEStartup' 'assets'

    $moveDirectoryContent = {
        param (
            [System.String]$SourcePath,
            [System.String]$DestinationPath
        )

        if (-not (Test-Path -LiteralPath $SourcePath -PathType Container)) { return }

        if (-not (Test-Path -LiteralPath $DestinationPath -PathType Container)) {
            New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null
        }

        foreach ($Item in @(Get-ChildItem -LiteralPath $SourcePath -Force -ErrorAction SilentlyContinue)) {
            $DestinationItem = Join-Path $DestinationPath $Item.Name
            if (Test-Path -LiteralPath $DestinationItem) {
                if ($Item.PSIsContainer -and (Test-Path -LiteralPath $DestinationItem -PathType Container)) {
                    & $moveDirectoryContent $Item.FullName $DestinationItem
                    continue
                }

                Write-Warning "[$FunctionName] Could not migrate '$($Item.FullName)' because '$DestinationItem' already exists."
                continue
            }

            try {
                Move-Item -LiteralPath $Item.FullName -Destination $DestinationPath -ErrorAction Stop
                Write-Verbose "[$FunctionName] Migrated '$($Item.FullName)' to '$DestinationPath'"
            }
            catch {
                Write-Warning "[$FunctionName] Could not migrate '$($Item.FullName)' to '$DestinationPath': $($_.Exception.Message)"
            }
        }

        if (-not (Get-ChildItem -LiteralPath $SourcePath -Force -ErrorAction SilentlyContinue | Select-Object -First 1)) {
            Remove-Item -LiteralPath $SourcePath -Force -ErrorAction SilentlyContinue
        }
    }

    & $moveDirectoryContent $LegacyScriptsPath $AssetsPath

    $ProfileScriptFolderMap = [ordered]@{
        'build-mediascript' = 'boot-mediascript'
        'build-winpescript' = 'boot-winpescript'
    }
    foreach ($LegacyFolderName in $ProfileScriptFolderMap.Keys) {
        & $moveDirectoryContent `
            (Join-Path $Path $LegacyFolderName) `
            (Join-Path $Path $ProfileScriptFolderMap[$LegacyFolderName])
    }

    $LegacyWallpaperPath = Join-Path $Path 'build-winpewallpaper'
    if (Test-Path -LiteralPath $LegacyWallpaperPath -PathType Container) {
        $LegacyWallpapers = @(Get-ChildItem -LiteralPath $LegacyWallpaperPath -Filter '*.jpg' -File -ErrorAction SilentlyContinue | Sort-Object FullName)
        $ProfileWallpaperPath = Join-Path $Path 'wallpaper.jpg'

        if ($LegacyWallpapers.Count -gt 0) {
            if (Test-Path -LiteralPath $ProfileWallpaperPath) {
                Write-Warning "[$FunctionName] Could not migrate '$($LegacyWallpapers[0].FullName)' because '$ProfileWallpaperPath' already exists."
            }
            else {
                try {
                    Move-Item -LiteralPath $LegacyWallpapers[0].FullName -Destination $ProfileWallpaperPath -ErrorAction Stop
                    Write-Verbose "[$FunctionName] Migrated '$($LegacyWallpapers[0].FullName)' to '$ProfileWallpaperPath'"
                }
                catch {
                    Write-Warning "[$FunctionName] Could not migrate '$($LegacyWallpapers[0].FullName)' to '$ProfileWallpaperPath': $($_.Exception.Message)"
                }
            }
        }

        $RemainingLegacyContent = @(Get-ChildItem -LiteralPath $LegacyWallpaperPath -Force -ErrorAction SilentlyContinue)
        if ($RemainingLegacyContent.Count -eq 0) {
            Remove-Item -LiteralPath $LegacyWallpaperPath -Force -ErrorAction SilentlyContinue
        }
        elseif (-not (Test-Path -LiteralPath $ProfileWallpaperPath)) {
            Write-Warning "[$FunctionName] Legacy wallpaper content remains in '$LegacyWallpaperPath'."
        }
        elseif ($LegacyWallpapers.Count -gt 1) {
            Write-Warning "[$FunctionName] Additional legacy wallpaper content remains in '$LegacyWallpaperPath'."
        }
    }

    $ProfileContentFolders = @(
        (Join-Path 'WinPEStartup' 'profiles')
        (Join-Path 'WinPEStartup' 'assets')
    )

    foreach ($FolderName in $ProfileContentFolders) {
        $FolderPath = Join-Path $Path $FolderName
        # Preserve existing profile content and create only missing directories.
        if (-not (Test-Path -LiteralPath $FolderPath -PathType Container)) {
            New-Item -ItemType Directory -Path $FolderPath -Force | Out-Null
            Write-Verbose "[$($MyInvocation.MyCommand.Name)] Created directory: $FolderPath"
        }
    }
}