src/public/System/New-AitherWindowsUnattendXml.ps1

#Requires -Version 7.0

<#
.SYNOPSIS
    Generate Windows Unattend.xml from configuration

.DESCRIPTION
    Creates an Unattend.xml file for Windows automated installation
    based on settings from config.windows.psd1.

.PARAMETER ConfigPath
    Path to Windows configuration file

.PARAMETER OutputPath
    Output directory for generated file

.PARAMETER FileName
    Output filename (default: Autounattend.xml)

.EXAMPLE
    New-AitherWindowsUnattendXml -ConfigPath ./config.windows.psd1

    Generate Unattend.xml from Windows configuration

.OUTPUTS
    String - Path to generated file, or null if generation is disabled

.NOTES
    Requires config.windows.psd1 with DeploymentArtifacts.Unattend section.
    Generation can be disabled in configuration.

.LINK
    New-AitherDeploymentArtifact
    New-AitherWindowsRegistryFile
#>

function New-AitherWindowsUnattendXml {
[CmdletBinding()]
param(
    [Parameter(Mandatory=$false)]
    [string]$ConfigPath,

    [string]$OutputPath = './artifacts/windows',

    [string]$FileName = 'Autounattend.xml',

    [Parameter(HelpMessage = "Show command output in console.")]
    [switch]$ShowOutput
)

begin {
    # Manage logging targets for this execution
    $originalLogTargets = $script:AitherLogTargets
    if ($ShowOutput) {
        if ($script:AitherLogTargets -notcontains 'Console') {
            $script:AitherLogTargets += 'Console'
        }
    }
    else {
        # Ensure Console is NOT in targets if ShowOutput is not specified
        $script:AitherLogTargets = $script:AitherLogTargets | Where-Object { $_ -ne 'Console' }
    }

    if (-not [System.IO.Path]::IsPathRooted($OutputPath)) {
        $moduleRoot = Get-AitherModuleRoot
        $OutputPath = Join-Path $moduleRoot $OutputPath
    }
}

process { try {
        # During module validation, skip execution
        if ($PSCmdlet.MyInvocation.InvocationName -eq '.') {
            return $null
        }

        $hasWriteAitherLog = Get-Command Write-AitherLog -ErrorAction SilentlyContinue

        if ($hasWriteAitherLog) {
            Write-AitherLog -Message "Generating Windows Unattend.xml from $ConfigPath" -Level Information -Source 'New-AitherWindowsUnattendXml'
        }

        # Load configuration
        if (-not (Get-Command Get-AitherConfigs -ErrorAction SilentlyContinue)) {
            Write-AitherLog -Level Warning -Message "Get-AitherConfigs is not available. Cannot generate Unattend.xml." -Source 'New-AitherWindowsUnattendXml'
            return $null
        }

        $config = Get-AitherConfigs -ConfigFile $ConfigPath

        if (-not $config.Windows.DeploymentArtifacts.Unattend.Generate) {
            if ($hasWriteAitherLog) {
                Write-AitherLog -Message "Unattend.xml generation is disabled in configuration" -Level Warning -Source 'New-AitherWindowsUnattendXml'
            } else {
                Write-AitherLog -Level Warning -Message "Unattend.xml generation is disabled in configuration" -Source 'New-AitherWindowsUnattendXml'
            }
            return $null
        }

        $unattendConfig = $config.Windows.DeploymentArtifacts.Unattend

        # Create XML document
        $xml = [xml]@'
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
</unattend>
'@


        # Add windowsPE configuration pass
        $windowsPE = $xml.CreateElement('settings', $xml.DocumentElement.NamespaceURI)
        $windowsPE.SetAttribute('pass', 'windowsPE')

        # Add specialize configuration pass
        $specialize = $xml.CreateElement('settings', $xml.DocumentElement.NamespaceURI)
        $specialize.SetAttribute('pass', 'specialize')

        # Add oobeSystem configuration pass
        $oobeSystem = $xml.CreateElement('settings', $xml.DocumentElement.NamespaceURI)
        $oobeSystem.SetAttribute('pass', 'oobeSystem')

        # Add computer name if configured
        if ($unattendConfig.ComputerName) {
            $component = $xml.CreateElement('component', $xml.DocumentElement.NamespaceURI)
            $component.SetAttribute('name', 'Microsoft-Windows-Shell-Setup')
            $component.SetAttribute('processorArchitecture', 'amd64')
            $component.SetAttribute('publicKeyToken', '31bf3856ad364e35')
            $component.SetAttribute('language', 'neutral')
            $component.SetAttribute('versionScope', 'nonSxS')

            $computerName = $xml.CreateElement('ComputerName', $xml.DocumentElement.NamespaceURI)
            $computerName.InnerText = $unattendConfig.ComputerName
            $component.AppendChild($computerName) | Out-Null

            $specialize.AppendChild($component) | Out-Null
        }

        $xml.DocumentElement.AppendChild($windowsPE) | Out-Null
        $xml.DocumentElement.AppendChild($specialize) | Out-Null
        $xml.DocumentElement.AppendChild($oobeSystem) | Out-Null

        # Ensure output directory exists
        if (-not (Test-Path $OutputPath)) {
            New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
        }

        # Save XML file
        $outputFile = Join-Path $OutputPath $FileName
        $xml.Save($outputFile)

        if ($hasWriteAitherLog) {
            Write-AitherLog -Message "Generated Unattend.xml: $outputFile" -Level Information -Source 'New-AitherWindowsUnattendXml'
        }
        return $outputFile
    }
    catch {
        if ($hasWriteAitherLog) {
            Write-AitherLog -Message "Error generating Unattend.xml: $($_.Exception.Message)" -Level Error -Source 'New-AitherWindowsUnattendXml' -Exception $_
        } else {
            Write-AitherLog -Level Error -Message "Error generating Unattend.xml: $($_.Exception.Message)" -Source 'New-AitherWindowsUnattendXml' -Exception $_
        }
        throw
    }
    finally {
        # Restore original log targets
        $script:AitherLogTargets = $originalLogTargets
    }
}

}