AutomatedLabUnattended.psm1


function Add-UnattendedCloudInitNetworkAdapter
{
    param (
        [string]$InterfaceName,

        [AutomatedLab.IPNetwork[]]$IpAddresses,

        [AutomatedLab.IPAddress[]]$Gateways,

        [AutomatedLab.IPAddress[]]$DnsServers
    )

    $macAddress = ($Interfacename -replace '-', ':').ToLower()
    if (-not $script:un.network.network.ContainsKey('ethernets`'))
    {
        $script:un.network.network.ethernets = @{ }
    }

    if ($script:un.network.network.ethernets.Keys.Count -eq 0)
    {
        $ifName = 'en0'
    }
    else
    {
        [int]$lastIfIndex = ($script:un.network.network.ethernets.Keys.GetEnumerator() | Sort-Object | Select-Object -Last 1) -replace 'en'
        $lastIfIndex++
        $ifName = 'en{0}' -f $lastIfIndex
    }

    $script:un.network.network.ethernets[$ifName] = @{
        match      = @{
            macAddress = $macAddress
        }
        'set-name' = $ifName
    }

    $adapterAddress = $IpAddresses | Select-Object -First 1

    if (-not $adapterAddress)
    {
        $script:un.network.network.ethernets[$ifName].dhcp4 = 'yes'
        $script:un.network.network.ethernets[$ifName].dhcp6 = 'yes'
    }
    else
    {
        $script:un.network.network.ethernets[$ifName].addresses = @()
        foreach ($ip in $IpAddresses)
        {
            $script:un.network.network.ethernets[$ifName].addresses += '{0}/{1}' -f $ip.IPAddress.AddressAsString, $ip.Netmask
        }
    }

    if ($Gateways -and -not $script:un.network.network.ethernets[$ifName].ContainsKey('routes')) { $script:un.network.network.ethernets[$ifName].routes = @() }
    foreach ($gw in $Gateways)
    {
        $script:un.network.network.ethernets[$ifName].routes += @{
            to  = 'default'
            via = $gw.AddressAsString
        }
    }

    if ($DnsServers)
    {
        $script:un.network.network.ethernets[$ifName].nameservers = @{ addresses = $DnsServers.AddressAsString }
    }
}

function Add-UnattendedCloudInitRenameNetworkAdapters
{
    [CmdletBinding()]
    param
    (
    )
    
    Write-PSFMessage -Message 'Method not required on Ubuntu/Cloudinit'
}

function Add-UnattendedCloudInitSynchronousCommand
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]$Command,

        [Parameter(Mandatory)]
        [string]$Description
    )

    $script:un['late-commands'] += $Command
}

function Export-UnattendedCloudInitFile
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $script:un | ConvertTo-Yaml | Set-Content -Path $Path -Force
}
function Import-UnattendedCloudInitContent
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string[]]
        $Content
    )

    $script:un = $Content -join "`r`n" | ConvertFrom-Yaml
}

function Set-UnattendedCloudInitAdministratorName
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Name
    )

    $Script:un.identity.username = $Name
}

function Set-UnattendedCloudInitAdministratorPassword
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $Password
    )

    $pw = [System.Text.Encoding]::UTF8.GetBytes($Password)
    $sha = [System.Security.Cryptography.SHA512Managed]::new()
    $hsh = $sha.ComputeHash($pw)
    $Script:un.identity.password = [Convert]::ToBase64String($hsh)
}

function Set-UnattendedCloudInitAntiMalware
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [bool]$Enabled
    )

    Write-PSFMessage -Message "No anti-malware settings for CloudInit/Ubuntu"
}
function Set-UnattendedCloudInitAutoLogon
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password
    )
    
    Write-PSFMessage -Message "Auto-logon not implemented yet for CloudInit/Ubuntu"
}
function Set-UnattendedCloudInitComputerName
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $ComputerName
    )

    $Script:un.identity.hostname = $ComputerName
}

function Set-UnattendedCloudInitDomain
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password,

        [Parameter()]
        [string]$OrganizationalUnit
    )

    if ($OrganizationalUnit)
    {
        $script:un['late-commands'] += "realm join --computer-ou='{2}' --one-time-password='{0}' {1}" -f $Password, $DomainName, $OrganizationalUnit
    }
    else
    {
        $script:un['late-commands'] += "realm join --one-time-password='{0}' {1}" -f $Password, $DomainName
    }
}

function Set-UnattendedCloudInitFirewallState
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [boolean]$State
    )

    $script:un['late-commands'] += 'ufw enable'
    $script:un['late-commands'] += 'ufw allow 22'
}
function Set-UnattendedCloudInitIpSettings
{
    [CmdletBinding()]
    param (
        [string]$IpAddress,

        [string]$Gateway,

        [String[]]$DnsServers,

        [string]$DnsDomain
    )

    $ifName = 'en0'

    $script:un.network.network.ethernets[$ifName] = @{
        match      = @{
            macAddress = $macAddress
        }
        'set-name' = $ifName
    }

    $adapterAddress = $IpAddress

    if (-not $adapterAddress)
    {
        $script:un.network.network.ethernets[$ifName].dhcp4 = 'yes'
        $script:un.network.network.ethernets[$ifName].dhcp6 = 'yes'
    }
    else
    {
        $script:un.network.network.ethernets[$ifName].addresses = @(
            $IpAddress
        )
    }

    if ($Gateway -and -not $script:un.network.network.ethernets[$ifName].ContainsKey('routes')) 
    {
        $script:un.network.network.ethernets[$ifName].routes = @(
            @{
                to  = 'default'
                via = $Gateway
            })
    }

    if ($DnsServers)
    {
        $script:un.network.network.ethernets[$ifName].nameservers = @{ addresses = $DnsServers }
    }
}
function Set-UnattendedCloudInitLocalIntranetSites
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Values
    )
    
    Write-PSFMessage -Message 'No local intranet sites for CloudInit/Ubuntu'
}
function Set-UnattendedCloudInitPackage
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Package
    )

    foreach ($pack in $Package)
    {
        if ($pack -in $script:un.packages) { continue }
        $script:un.packages += $pack
    }
}
function Set-UnattendedProductKey
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ProductKey
    )

    Write-PSFMessage "No product key required on CloudInit/Ubuntu"
}
function Set-UnattendedCloudInitTimeZone
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$TimeZone
    )

    $tzInfo = Get-TimeZone -Id $TimeZone -ErrorAction SilentlyContinue

    if (-not $tzInfo) { Get-TimeZone }

    Write-PSFMessage -Message ('Since non-standard timezone names are used, we revert to Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours)
    $tzname = if ($tzInfo.BaseUtcOffset.TotalHours -gt 0)
    {
        'timezone Etc/GMT+{0}' -f $tzInfo.BaseUtcOffset.TotalHours
    }
    elseif ($tzInfo.BaseUtcOffset.TotalHours -eq 0)
    {
        'timezone Etc/GMT'
    }
    else
    {
        'timezone Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours
    }

    $script:un['late-commands'] += 'sudo timedatectl set-timezone {0}' -f $tzname
}
function Set-UnattendedCloudInitUserLocale
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$UserLocale
    )

    try
    {
        $ci = [cultureinfo]::new($UserLocale)
    }
    catch
    {
        Write-PSFMessage -Message "Could not determine culture from $UserLocale. Assuming en_us"
        $script:un.locale = 'en_US.UTF-8'
        return
    }

    $script:un.locale = "$($ci.IetfLanguageTag -replace '-','_').UTF-8"
}

function Set-UnattendedCloudInitWorkgroup
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$WorkgroupName
    )
    
    $script:un['late-commands'] += "sed -i 's|[#]*workgroup = WORKGROUP|workgroup = {0}|g' /etc/samba/smb.conf" -f $WorkgroupName
}

function Add-UnattendedKickstartNetworkAdapter
{
    param (
        [string]$Interfacename,

        [AutomatedLab.IPNetwork[]]$IpAddresses,

        [AutomatedLab.IPAddress[]]$Gateways,

        [AutomatedLab.IPAddress[]]$DnsServers
    )

    $linuxInterfaceName = ($Interfacename -replace '-',':').ToLower()
    $adapterAddress = $IpAddresses | Select-Object -First 1

    if (-not $adapterAddress)
    {
        $configurationItem = "network --bootproto=dhcp --device={0}" -f $linuxInterfaceName
    }
    else
    {
        $configurationItem = "network --bootproto=static --device={0} --ip={1} --netmask={2}" -f $linuxInterfaceName,$adapterAddress.IPAddress.AddressAsString,$adapterAddress.Netmask
    }

    if ($Gateways)
    {
        $configurationItem += ' --gateway={0}' -f ($Gateways.AddressAsString -join ',')
    }

    $configurationItem += if ($DnsServers | Where-Object AddressAsString -ne '0.0.0.0')
    {
        ' --nameserver={0}' -f ($DnsServers.AddressAsString -join ',')
    }
    else
    {
        ' --nodns'
    }

    $script:un.Add($configurationItem)
}

function Add-UnattendedKickstartRenameNetworkAdapters
{
    [CmdletBinding()]
    param ( )
    Write-PSFMessage -Message 'Method not yet implemented for RHEL/CentOS/Fedora'
}

function Add-UnattendedKickstartSynchronousCommand
{
    param (
        [Parameter(Mandatory)]
        [string]$Command,

        [Parameter(Mandatory)]
        [string]$Description
    )

    Write-PSFMessage -Message "Adding command to %post section to $Description"

    $idx = $script:un.IndexOf('%post')

    if ($idx -eq -1)
    {
        $script:un.Add('%post')
        $idx = $script:un.IndexOf('%post')
    }

    $script:un.Insert($idx + 1, $Command)
}

function Export-UnattendedKickstartFile
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $idx = $script:un.IndexOf('%post')

    if ($idx -eq -1)
    {
        $script:un.Add('%post')
        $idx = $script:un.IndexOf('%post')
    }

    $repoIp = try {
        ([System.Net.Dns]::GetHostByName('packages.microsoft.com').AddressList | Where-Object AddressFamily -eq InterNetwork).IPAddressToString
    }
    catch
    { '104.214.230.139' }

    try
    {
        $repoContent = (Invoke-RestMethod -Method Get -Uri 'https://packages.microsoft.com/config/rhel/7/prod.repo' -ErrorAction Stop) -split "`n"
    }
    catch { }

    if ($script:un[$idx + 1] -ne '#start')
    {
        @(
            '#start'
            '. /etc/os-release'
            foreach ($line in $repoContent)
            {
                if (-not $line) { continue }
                if ($line -like '*gpgcheck*') {$line = 'gpgcheck=0'}
                'echo "{0}" >> /etc/yum.repos.d/microsoft.repo' -f $line
            }
            'echo "{0} packages.microsoft.com" >> /etc/hosts' -f $repoIp
            'yum install -y openssl'
            'yum install -y omi'
            'yum install -y powershell'
            'yum install -y omi-psrp-server'
            'yum list installed "powershell" > /ksPowerShell'
            'yum list installed "omi-psrp-server" > /ksOmi'
            'rm /etc/yum.repos.d/microsoft.repo'
            foreach ($line in $repoContent)
            {
                if (-not $line) { continue }
                'echo "{0}" >> /etc/yum.repos.d/microsoft.repo' -f $line
            }
            'authselect select sssd with-mkhomedir -f'
            'systemctl restart sssd'
            'echo "Subsystem powershell /usr/bin/pwsh -sshs -NoLogo" >> /etc/ssh/sshd_config'
            'systemctl restart sshd'
        ) | ForEach-Object -Process {
            $idx++
            $script:un.Insert($idx, $_)
        }

        # When index of end is greater then index of package end: add %end to EOF
        # else add %end before %packages

        $idxPackage = $script:un.IndexOf('%packages --ignoremissing')
        $idxPost = $script:un.IndexOf('%post')

        $idxEnd = if (-1 -ne $idxPackage -and $idxPost -lt $idxPackage)
        {
            $idxPackage
        }
        else
        {
            $script:un.Count
        }

        $script:un.Insert($idxEnd, '%end')
    }

    ($script:un | Out-String) -replace "`r`n", "`n" | Set-Content -Path $Path -Force
}

function Import-UnattendedKickstartContent
{
    param
    (
        [Parameter(Mandatory = $true)]
        [System.Collections.Generic.List[string]]
        $Content
    )
    $script:un = $Content
}

function Set-UnattendedKickstartAdministratorName
{
    param
    (
        $Name
    )

    $script:un.Add("user --name=$Name --groups=wheel --password='%PASSWORD%'")
}

function Set-UnattendedKickstartAdministratorPassword
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Password
    )

        $Script:un.Add("rootpw '$Password'")
        $Script:un = [System.Collections.Generic.List[string]]($Script:un.Replace('%PASSWORD%', $Password))
}

function Set-UnattendedKickstartAntiMalware
{
    param (
        [Parameter(Mandatory = $true)]
        [bool]$Enabled
    )

    if ($Enabled)
    {
        $Script:un.Add("selinux --enforcing")
    }
    else
    {
        $Script:un.Add("selinux --permissive") # Not a great idea to disable selinux alltogether
    }
}

function Set-UnattendedKickstartAutoLogon
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password
    )
    Write-PSFMessage -Message "Auto-logon not implemented yet for RHEL/CentOS/Fedora"
}

function Set-UnattendedKickstartComputerName
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )

    $script:un.Add("network --hostname=$ComputerName")
}

function Set-UnattendedKickstartDomain
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password,

        [Parameter()]
        [string]$OrganizationalUnit
    )

    if ($OrganizationalUnit)
    {
        $script:un.Add(("realm join --computer-ou='{2}' --one-time-password='{0}' {1}" -f $Password, $DomainName, $OrganizationalUnit))

    }
    else
    {
        $script:un.Add(("realm join --one-time-password='{0}' {1}" -f $Password, $DomainName))
    }
}

function Set-UnattendedKickstartFirewallState
{
    param
    (
        [Parameter(Mandatory = $true)]
        [boolean]$State
    )

    if ($State)
    {
        $script:un.Add('firewall --enabled')
    }
    else
    {
        $script:un.Add('firewall --disabled')
    }
}

function Set-UnattendedKickstartIpSettings
{
    param (
        [string]$IpAddress,

        [string]$Gateway,

        [String[]]$DnsServers,

        [string]$DnsDomain
    )

    if (-not $IpAddress)
    {
        $configurationItem = "network --bootproto=dhcp"
    }
    else
    {
        $configurationItem = "network --bootproto=static --ip={0}" -f $IpAddress
    }

    if ($Gateway)
    {
        $configurationItem += ' --gateway={0}' -f $Gateway
    }

    $configurationItem += if ($DnsServers)
    {
        ' --nameserver={0}' -f ($DnsServers.AddressAsString -join ',')
    }
    else
    {
        ' --nodns'
    }

    $script:un.Add($configurationItem)
}

function Set-UnattendedKickstartLocalIntranetSites
{
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Values
    )

    Write-PSFMessage -Message 'No local intranet sites for RHEL/CentOS/Fedora'
}

function Set-UnattendedKickstartPackage
{
    param
    (
        [string[]]$Package
    )

    if ($Package -like '*Gnome*')
    {
        $script:un.Add('xconfig --startxonboot --defaultdesktop=GNOME')
    }
    elseif ($Package -like '*KDE*')
    {
        Write-PSFMessage -Level Warning -Message 'Adding KDE UI to RHEL/CentOS via kickstart file is not supported. Please configure your UI manually.'
    }

    $script:un.Add('%packages --ignoremissing')
    $script:un.Add('@^server-product-environment')
    $required = @(
        'oddjob'
        'oddjob-mkhomedir'
        'sssd'
        'adcli'
        'krb5-workstation'
        'realmd'
        'samba-common'
        'samba-common-tools'
        'authselect-compat'
        'sshd'
    )

    foreach ($p in $Package)
    {
        if ($p -eq '@^server-product-environment' -or $p -in $required) { continue }

        $null = $script:un.Add($p)

        if ($p -like '*gnome*' -and $script:un -notcontains '@^graphical-server-environment') { $script:un.Add('@^graphical-server-environment')}
    }

    foreach ($p in $required)
    {
        $script:un.Add($p)
    }

    $script:un.Add('%end')
}

function Set-UnattendedKickstartProductKey
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ProductKey
    )

    Write-PSFMessage -Message 'No product key necessary for RHEL/CentOS/Fedora'
}

function Set-UnattendedKickstartTimeZone
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$TimeZone
    )

    $tzInfo = Get-TimeZone -Id $TimeZone -ErrorAction SilentlyContinue

    if (-not $tzInfo) { Get-TimeZone }

    Write-PSFMessage -Message ('Since non-standard timezone names are used, we revert to Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours)
    if ($tzInfo.BaseUtcOffset.TotalHours -gt 0)
    {
        $script:un.Add(('timezone Etc/GMT+{0}' -f $tzInfo.BaseUtcOffset.TotalHours))
    }
    elseif ($tzInfo.BaseUtcOffset.TotalHours -eq 0)
    {
        $script:un.Add('timezone Etc/GMT')
    }
    else
    {
        $script:un.Add(('timezone Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours))
    }
}

function Set-UnattendedKickstartUserLocale
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$UserLocale
    )

    try
    {
        $ci = [cultureinfo]::new($UserLocale)
    }
    catch
    {
        Write-PSFMessage -Message "Could not determine culture from $UserLocale. Assuming en_us"
        $script:un.Add("keyboard 'us'")
        $script:un.Add('lang en_us')
        return
    }

    $weirdLinuxCultureName = if ($ci.IsNeutralCulture) { $ci.TwoLetterISOLanguageName } else {$ci.Name -split '-' | Select-Object -Last 1}
    $script:un.Add("keyboard '$($weirdLinuxCultureName.ToLower())'")
    $script:un.Add("lang $($ci.IetfLanguageTag -replace '-','_')")
}

function Set-UnattendedKickstartWorkgroup
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $WorkgroupName
    )

    $script:un.Add(('auth --smbworkgroup={0}' -f $WorkgroupName))
}

function Add-UnattendedYastNetworkAdapter
{
    param (
        [string]$Interfacename,

        [AutomatedLab.IPNetwork[]]$IpAddresses,

        [AutomatedLab.IPAddress[]]$Gateways,

        [AutomatedLab.IPAddress[]]$DnsServers,

        [string]$ConnectionSpecificDNSSuffix,

        [string]$DnsDomain,

        [string]$DNSSuffixSearchOrder
    )

    $networking = $script:un.SelectSingleNode('/un:profile/un:networking', $script:nsm)
    $interfaceList = $script:un.SelectSingleNode('/un:profile/un:networking/un:interfaces', $script:nsm)
    $udevList = $script:un.SelectSingleNode('/un:profile/un:networking/un:net-udev', $script:nsm)
    $dns = $script:un.SelectSingleNode('/un:profile/un:networking/un:dns', $script:nsm)
    $nameServers = $script:un.SelectSingleNode('/un:profile/un:networking/un:dns/un:nameservers', $script:nsm)
    $routes = $script:un.SelectSingleNode('/un:profile/un:networking/un:routing/un:routes', $script:nsm)
    $hostName = $script:un.CreateElement('hostname', $script:nsm.LookupNamespace('un'))
    $null = $dns.AppendChild($hostName)

    if ($DnsDomain)
    {
        $domain = $script:un.CreateElement('domain', $script:nsm.LookupNamespace('un'))
        $domain.InnerText = $DnsDomain
        $null = $dns.AppendChild($domain)
    }

    if ($DnsServers)
    {
        foreach ($ns in $DnsServers)
        {
            $nameserver = $script:un.CreateElement('nameserver', $script:nsm.LookupNamespace('un'))
            $nameserver.InnerText = $ns
            $null = $nameservers.AppendChild($nameserver)
        }

        if ($DNSSuffixSearchOrder)
        {
            $searchlist = $script:un.CreateElement('searchlist', $script:nsm.LookupNamespace('un'))
            $nsAttr = $script:un.CreateAttribute('config', 'type', $script:nsm.LookupNamespace('config'))
            $nsAttr.InnerText = 'list'
            $null = $searchlist.Attributes.Append($nsAttr)

            foreach ($suffix in ($DNSSuffixSearchOrder -split ','))
            {
                $suffixEntry = $script:un.CreateElement('search', $script:nsm.LookupNamespace('un'))
                $suffixEntry.InnerText = $suffix
                $null = $searchlist.AppendChild($suffixEntry)
            }

            $null = $dns.AppendChild($searchlist)
        }
    }

    $null = $networking.AppendChild($dns)

    $interface = 'eth0'
    $lastInterface = $script:un.SelectNodes('/un:profile/un:networking/un:interfaces/un:interface/un:device', $script:nsm).InnerText | Sort-Object | Select-Object -Last 1
    if ($lastInterface) { $interface = 'eth{0}' -f ([int]$lastInterface.Substring($lastInterface.Length - 1, 1) + 1) }

    $interfaceNode = $script:un.CreateElement('interface', $script:nsm.LookupNamespace('un'))
    $bootproto = $script:un.CreateElement('bootproto', $script:nsm.LookupNamespace('un'))
    $bootproto.InnerText = if ($IpAddresses.Count -eq 0) { 'dhcp' } else { 'static' }
    $deviceNode = $script:un.CreateElement('device', $script:nsm.LookupNamespace('un'))
    $deviceNode.InnerText = $interface
    $firewallnode = $script:un.CreateElement('firewall', $script:nsm.LookupNamespace('un'))
    $firewallnode.InnerText = 'no'

    if ($IpAddresses.Count -gt 0)
    {
        $ipaddr = $script:un.CreateElement('ipaddr', $script:nsm.LookupNamespace('un'))
        $netmask = $script:un.CreateElement('netmask', $script:nsm.LookupNamespace('un'))
        $network = $script:un.CreateElement('network', $script:nsm.LookupNamespace('un'))
        $ipaddr.InnerText = $IpAddresses[0].IpAddress.AddressAsString
        $netmask.InnerText = $IpAddresses[0].Netmask.AddressAsString
        $network.InnerText = $IpAddresses[0].Network.AddressAsString
        $null = $interfaceNode.AppendChild($ipaddr)
        $null = $interfaceNode.AppendChild($netmask)
        $null = $interfaceNode.AppendChild($network)
    }
    $startmode = $script:un.CreateElement('startmode', $script:nsm.LookupNamespace('un'))

    $startmode.InnerText = 'auto'

    $null = $interfaceNode.AppendChild($bootproto)
    $null = $interfaceNode.AppendChild($deviceNode)
    $null = $interfaceNode.AppendChild($firewallnode)
    $null = $interfaceNode.AppendChild($startmode)

    if ($IpAddresses.Count -gt 1)
    {
        $aliases = $script:un.CreateElement('aliases', $script:nsm.LookupNamespace('un'))
        $count = 0

        foreach ($additionalAdapter in ($IpAddresses | Select-Object -Skip 1))
        {
            $alias = $script:un.CreateElement("alias$count", $script:nsm.LookupNamespace('un'))
            $ipaddr = $script:un.CreateElement('IPADDR', $script:nsm.LookupNamespace('un'))
            $label = $script:un.CreateElement('LABEL', $script:nsm.LookupNamespace('un'))
            $netmask = $script:un.CreateElement('NETMASK', $script:nsm.LookupNamespace('un'))
            $ipaddr.InnerText = $additionalAdapter.IpAddress.AddressAsString
            $netmask.InnerText = $additionalAdapter.Netmask.AddressAsString
            $label.InnerText = "ip$count"
            $null = $alias.AppendChild($ipaddr)
            $null = $alias.AppendChild($label)
            $null = $alias.AppendChild($netmask)
            $null = $aliases.AppendChild($alias)
            $count++
        }

        $null = $interfaceNode.AppendChild($aliases)
    }

    $null = $interfaceList.AppendChild($interfaceNode)

    $udevRuleNode = $script:un.CreateElement('rule', $script:nsm.LookupNamespace('un'))
    $udevRuleNameNode = $script:un.CreateElement('name', $script:nsm.LookupNamespace('un'))
    $udevRuleNameNode.InnerText = $interface
    $udevRuleRuleNode = $script:un.CreateElement('rule', $script:nsm.LookupNamespace('un'))
    $udevRuleRuleNode.InnerText = 'ATTR{address}' # No joke. They really mean it to be written this way
    $udevRuleValueNode = $script:un.CreateElement('value', $script:nsm.LookupNamespace('un'))
    $udevRuleValueNode.InnerText = ($Interfacename -replace '-', ':').ToUpper()
    $null = $udevRuleNode.AppendChild($udevRuleNameNode)
    $null = $udevRuleNode.AppendChild($udevRuleRuleNode)
    $null = $udevRuleNode.AppendChild($udevRuleValueNode)
    $null = $udevList.AppendChild($udevRuleNode)

    if ($Gateways)
    {
        foreach ($gateway in $Gateways)
        {
            $routeNode = $script:un.CreateElement('route', $script:nsm.LookupNamespace('un'))
            $destinationNode = $script:un.CreateElement('destination', $script:nsm.LookupNamespace('un'))
            $deviceNode = $script:un.CreateElement('device', $script:nsm.LookupNamespace('un'))
            $gatewayNode = $script:un.CreateElement('gateway', $script:nsm.LookupNamespace('un'))
            $netmask = $script:un.CreateElement('netmask', $script:nsm.LookupNamespace('un'))

            $destinationNode.InnerText = 'default' # should work for both IPV4 and IPV6 routes

            $devicenode.InnerText = $interface
            $gatewayNode.InnerText = $gateway.AddressAsString
            $netmask.InnerText = '-'

            $null = $routeNode.AppendChild($destinationNode)
            $null = $routeNode.AppendChild($devicenode)
            $null = $routeNode.AppendChild($gatewayNode)
            $null = $routeNode.AppendChild($netmask)
            $null = $routes.AppendChild($routeNode)
        }
    }
}

function Add-UnattendedYastRenameNetworkAdapters
{

}
function Add-UnattendedYastSynchronousCommand
{
    param (
        [Parameter(Mandatory)]
        [string]$Command,

        [Parameter(Mandatory)]
        [string]$Description
    )

}
function Export-UnattendedYastFile
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $script:un.Save($Path)
}
function Import-UnattendedYastContent
{
    param
    (
        [Parameter(Mandatory = $true)]
        [xml]
        $Content
    )

    $script:un = $Content
    $script:ns = @{
        un          = "http://www.suse.com/1.0/yast2ns"
        'un:config' = "http://www.suse.com/1.0/configns"
    }
    $script:nsm =   [System.Xml.XmlNamespaceManager]::new($script:un.NameTable)
    $script:nsm.AddNamespace('un',"http://www.suse.com/1.0/yast2ns")
    $script:nsm.AddNamespace('un:config',"http://www.suse.com/1.0/configns" )
}

function Set-UnattendedYastAdministratorName
{
    param
    (
        $Name
    )

    $userNode = $script:un.SelectSingleNode('/un:profile/un:users', $script:nsm)

    $user = $script:un.CreateElement('user', $script:nsm.LookupNamespace('un'))
    $username = $script:un.CreateElement('username', $script:nsm.LookupNamespace('un'))
    $pw = $script:un.CreateElement('user_password', $script:nsm.LookupNamespace('un'))
    $encrypted = $script:un.CreateElement('encrypted', $script:nsm.LookupNamespace('un'))
    $listAttr = $script:un.CreateAttribute('config','type', $script:nsm.LookupNamespace('config'))
    $listAttr.InnerText = 'boolean'
    $null = $encrypted.Attributes.Append($listAttr)

    $encrypted.InnerText = 'false'
    $pw.InnerText = 'none'
    $username.InnerText = $Name

    $null = $user.AppendChild($pw)
    $null = $user.AppendChild($encrypted)
    $null = $user.AppendChild($username)

    $null = $userNode.AppendChild($user)
}
function Set-UnattendedYastAdministratorPassword
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Password
    )

        $passwordNodes = $script:un.SelectNodes('/un:profile/un:users/un:user/un:user_password', $script:nsm)

        foreach ($node in $passwordNodes)
        {
            $node.InnerText = $Password
        }
}
function Set-UnattendedYastAntiMalware
{
    param (
        [Parameter(Mandatory = $true)]
        [bool]$Enabled
    )
}
function Set-UnattendedYastAutoLogon
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password
    )

    $logonNode = $script:un.CreateElement('login_settings', $script:nsm.LookupNamespace('un'))
    $autoLogon = $script:un.CreateElement('autologin_user', $script:nsm.LookupNamespace('un'))
    $autologon.InnerText = '{0}\{1}' -f $DomainName, $Username
    $null = $logonNode.AppendChild($autoLogon)
    $null = $script:un.DocumentElement.AppendChild($logonNode)
}
function Set-UnattendedYastComputerName
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )
    $component = $script:un.SelectSingleNode('/un:profile/un:networking/un:dns/un:hostname', $script:nsm)
    $component.InnerText = $ComputerName
}
function Set-UnattendedYastDomain
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password,

        [Parameter()]
        [string]$OrganizationalUnit
    )

    $smbClientNode = $script:un.CreateElement('samba-client', $script:nsm.LookupNamespace('un'))
    $boolAttrib = $script:un.CreateAttribute('config','type', $script:nsm.LookupNamespace('config'))
    $boolAttrib.InnerText = 'boolean'
    $adNode = $script:un.CreateElement('active_directory', $script:nsm.LookupNamespace('un'))
    $kdc = $script:un.CreateElement('kdc', $script:nsm.LookupNamespace('un'))
    $disableDhcp = $script:un.CreateElement('disable_dhcp_hostname', $script:nsm.LookupNamespace('un'))
    $globalNode = $script:un.CreateElement('global', $script:nsm.LookupNamespace('un'))
    $securityNode = $script:un.CreateElement('security', $script:nsm.LookupNamespace('un'))
    $shellNode = $script:un.CreateElement('template_shell', $script:nsm.LookupNamespace('un'))
    $guestNode = $script:un.CreateElement('usershare_allow_guests', $script:nsm.LookupNamespace('un'))
    $domainNode = $script:un.CreateElement('workgroup', $script:nsm.LookupNamespace('un'))
    $joinNode = $script:un.CreateElement('join', $script:nsm.LookupNamespace('un'))
    $joinUserNode = $script:un.CreateElement('user', $script:nsm.LookupNamespace('un'))
    $joinPasswordNode = $script:un.CreateElement('password', $script:nsm.LookupNamespace('un'))
    $homedirNode = $script:un.CreateElement('mkhomedir', $script:nsm.LookupNamespace('un'))
    $winbindNode = $script:un.CreateElement('winbind', $script:nsm.LookupNamespace('un'))

    $null = $disableDhcp.Attributes.Append($boolAttrib)
    $null = $homedirNode.Attributes.Append($boolAttrib)
    $null = $winbindNode.Attributes.Append($boolAttrib)

    $kdc.InnerText = $DomainName

    $disableDhcp.InnerText = 'true'
    $securityNode.InnerText = 'ADS'
    $shellNode.InnerText = '/bin/bash'
    $guestNode.InnerText = 'no'
    $domainNode.InnerText = $DomainName
    $joinUserNode.InnerText = $Username
    $joinPasswordNode.InnerText = $Password
    $homedirNode.InnerText = 'true'
    $winbindNode.InnerText = 'false'

    $null = $adNode.AppendChild($kdc)
    $null = $globalNode.AppendChild($securityNode)
    $null = $globalNode.AppendChild($shellNode)
    $null = $globalNode.AppendChild($guestNode)
    $null = $globalNode.AppendChild($domainNode)
    $null = $joinNode.AppendChild($joinUserNode)
    $null = $joinNode.AppendChild($joinPasswordNode)
    $null = $smbClientNode.AppendChild($disableDhcp)
    $null = $smbClientNode.AppendChild($globalNode)
    $null = $smbClientNode.AppendChild($adNode)
    $null = $smbClientNode.AppendChild($joinNode)
    $null = $smbClientNode.AppendChild($homedirNode)
    $null = $smbClientNode.AppendChild($winbindNode)

    $null = $script:un.DocumentElement.AppendChild($smbClientNode)

    # SSSD configuration
    $authClientNode = $script:un.CreateElement('auth-client', $script:nsm.LookupNamespace('un'))
    $authClientSssd = $script:un.CreateElement('sssd', $script:nsm.LookupNamespace('un'))
    $authClientLdaps = $script:un.CreateElement('nssldap', $script:nsm.LookupNamespace('un'))
    $sssdConf = $script:un.CreateElement('sssd_conf', $script:nsm.LookupNamespace('un'))
    $sssdConfFile = $script:un.CreateElement('config_file_version', $script:nsm.LookupNamespace('un'))
    $sssdConfServices = $script:un.CreateElement('services', $script:nsm.LookupNamespace('un'))
    $sssdConfNode = $script:un.CreateElement('sssd', $script:nsm.LookupNamespace('un'))
    $sssdConfDomains = $script:un.CreateElement('domains', $script:nsm.LookupNamespace('un'))
    $authDomains = $script:un.CreateElement('auth_domains', $script:nsm.LookupNamespace('un'))
    $authDomain = $script:un.CreateElement('domain', $script:nsm.LookupNamespace('un'))
    $authDomainName = $script:un.CreateElement('domain_name', $script:nsm.LookupNamespace('un'))
    $authDomainIdp = $script:un.CreateElement('id_provider', $script:nsm.LookupNamespace('un'))
    $authDomainUri = $script:un.CreateElement('ldap_uri', $script:nsm.LookupNamespace('un'))

    $authClientSssd.InnerText = 'yes'
    $authClientLdaps.InnerText = 'no'
    $sssdConfFile.InnerText = 2
    $sssdConfServices.InnerText = 'nss, pam'
    $sssdConfDomains.InnerText = $DomainName
    $authDomainName.InnerText = $DomainName
    $authDomainIdp.InnerText = 'ldap'
    $authDomainUri.InnerText = "ldap://$DomainName"

    $authDomain.AppendChild($authDomainName)
    $authDomain.AppendChild($authDomainIdp)
    $authDomain.AppendChild($authDomainUri)
    $authDomains.AppendChild($authDomain)
    $sssdConf.AppendChild($authDomains)

    $sssdConfNode.AppendChild($sssdConfFile)
    $sssdConfNode.AppendChild($sssdConfServices)
    $sssdConfNode.AppendChild($sssdConfDomains)
    $sssdConf.AppendChild($sssdConfNode)

    $authClientNode.AppendChild($authClientSssd)
    $authClientNode.AppendChild($authClientLdaps)
    $authClientNode.AppendChild($sssdConf)
    $script:un.DocumentElement.AppendChild($authClientNode)
}
function Set-UnattendedYastFirewallState
{
    param (
        [Parameter(Mandatory = $true)]
        [boolean]$State
        )

        $fwState = $script:un.SelectSingleNode('/un:profile/un:firewall/un:enable_firewall', $script:nsm)
        $fwState.InnerText = $State.ToString().ToLower()
}
function Set-UnattendedYastIpSettings
{
    param (
        [string]$IpAddress,

        [string]$Gateway,

        [String[]]$DnsServers,

        [string]$DnsDomain
    )

}
function Set-UnattendedYastLocalIntranetSites
{
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Values
    )
}
function Set-UnattendedYastPackage
{
    param
    (
        [string[]]$Package
    )

    $packagesNode = $script:un.SelectSingleNode('/un:profile/un:software/un:patterns', $script:nsm)
    foreach ($p in $Package)
    {
        $packageNode = $script:un.CreateElement('pattern', $script:nsm.LookupNamespace('un'))
        $packageNode.InnerText = $p
        $null = $packagesNode.AppendChild($packageNode)
    }
}
function Set-UnattendedYastProductKey
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ProductKey
    )
}
function Set-UnattendedYastTimeZone
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$TimeZone
    )

    $tzInfo = Get-TimeZone -Id $TimeZone
    Write-PSFMessage -Message ('Since non-standard timezone names are used, we revert to Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours)

    $timeNode = $script:un.SelectSingleNode('/un:profile/un:timezone/un:timezone', $script:nsm)

    $timeNode.InnerText = if ($tzInfo.BaseUtcOffset.TotalHours -gt 0)
    {
        'Etc/GMT+{0}' -f $tzInfo.BaseUtcOffset.TotalHours
    }
    elseif ($tzInfo.BaseUtcOffset.TotalHours -eq 0)
    {
        'Etc/GMT'
    }
    else
    {
        'Etc/GMT{0}' -f $tzInfo.BaseUtcOffset.TotalHours
    }
}

function Set-UnattendedYastUserLocale
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$UserLocale
    )

    $language = $script:un.SelectSingleNode('/un:profile/un:language', $script:nsm)
    $languageNode = $script:un.SelectSingleNode('/un:profile/un:language/un:language', $script:nsm)
    $keyboard = $script:un.SelectSingleNode('/un:profile/un:keyboard/un:keymap', $script:nsm)

    try
    {
        $ci = [cultureinfo]::new($UserLocale)
    }
    catch
    {
        $ci = [cultureinfo]::new('en-us')
    }

    # Primary language
    $languageNode.InnerText = $ci.IetfLanguageTag -replace '-', '_'

    # Secondary language
    if ($ci.Name -ne 'en-US')
    {
        $languagesNode = $script:un.CreateElement('languages', $script:nsm.LookupNamespace('un'))
        $languagesNode.InnerText = 'en-us'
        $null = $language.AppendChild($languagesNode)
    }

    $keyMapName = '{0}-{1}' -f ($ci.EnglishName -split " ")[0].Trim().ToLower(), ($ci.Name -split '-')[-1].ToLower()
    $keyboard.InnerText = $keyMapName
}
function Set-UnattendedYastWorkgroup
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $WorkgroupName
    )

    $smbClientNode = $script:un.CreateElement('samba-client', $script:nsm.LookupNamespace('un'))
    $boolAttrib = $script:un.CreateAttribute('config','type', $script:nsm.LookupNamespace('config'))
    $boolAttrib.InnerText = 'boolean'
    $disableDhcp = $script:un.CreateElement('disable_dhcp_hostname', $script:nsm.LookupNamespace('un'))
    $globalNode = $script:un.CreateElement('global', $script:nsm.LookupNamespace('un'))
    $securityNode = $script:un.CreateElement('security', $script:nsm.LookupNamespace('un'))
    $shellNode = $script:un.CreateElement('template_shell', $script:nsm.LookupNamespace('un'))
    $guestNode = $script:un.CreateElement('usershare_allow_guests', $script:nsm.LookupNamespace('un'))
    $domainNode = $script:un.CreateElement('workgroup', $script:nsm.LookupNamespace('un'))
    $homedirNode = $script:un.CreateElement('mkhomedir', $script:nsm.LookupNamespace('un'))
    $winbindNode = $script:un.CreateElement('winbind', $script:nsm.LookupNamespace('un'))

    $null = $disableDhcp.Attributes.Append($boolAttrib)
    $null = $homedirNode.Attributes.Append($boolAttrib)
    $null = $winbindNode.Attributes.Append($boolAttrib)

    $disableDhcp.InnerText = 'true'
    $securityNode.InnerText = 'domain'
    $shellNode.InnerText = '/bin/bash'
    $guestNode.InnerText = 'no'
    $domainNode.InnerText = $DomainName
    $homedirNode.InnerText = 'true'
    $winbindNode.InnerText = 'true'

    $null = $globalNode.AppendChild($securityNode)
    $null = $globalNode.AppendChild($shellNode)
    $null = $globalNode.AppendChild($guestNode)
    $null = $globalNode.AppendChild($domainNode)
    $null = $smbClientNode.AppendChild($disableDhcp)
    $null = $smbClientNode.AppendChild($globalNode)
    $null = $smbClientNode.AppendChild($homedirNode)
    $null = $smbClientNode.AppendChild($winbindNode)

    $null = $script:un.DocumentElement.AppendChild($smbClientNode)
}
function Add-UnattendedWindowsNetworkAdapter
{
    param (
        [string]$Interfacename,

        [AutomatedLab.IPNetwork[]]$IpAddresses,

        [AutomatedLab.IPAddress[]]$Gateways,

        [AutomatedLab.IPAddress[]]$DnsServers,

        [string]$ConnectionSpecificDNSSuffix,

        [string]$DnsDomain,

        [string]$UseDomainNameDevolution,

        [string]$DNSSuffixSearchOrder,

        [string]$EnableAdapterDomainNameRegistration,

        [string]$DisableDynamicUpdate,

        [string]$NetbiosOptions
    )

    function Add-XmlGroup
    {
        param
        (
            [string]$XPath,
            [string]$ElementName,
            [string]$Action,
            [string]$KeyValue
        )

        Write-Debug -Message "XPath=$XPath"
        Write-Debug -Message "ElementName=$ElementName"

        #$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
        #$wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'

        $rootElement = $script:un | Select-Xml -XPath $XPath -Namespace $script:ns | Select-Object -ExpandProperty Node

        $element = $script:un.CreateElement($ElementName, $script:un.DocumentElement.NamespaceURI)
        [Void]$rootElement.AppendChild($element)
        #[Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, 'add')
        if ($Action)   { [Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, $Action) }
        if ($KeyValue) { [Void]$element.SetAttribute('keyValue', $script:wcmNamespaceUrl, $KeyValue) }
    }

    function Add-XmlElement
    {
        param
        (
            [string]$XPath,
            [string]$ElementName,
            [string]$Text,
            [string]$Action,
            [string]$KeyValue
        )

        Write-Debug -Message "XPath=$XPath"
        Write-Debug -Message "ElementName=$ElementName"
        Write-Debug -Message "Text=$Text"

        #$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
        #$wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'

        $rootElement = $script:un | Select-Xml -XPath $xPath -Namespace $script:ns | Select-Object -ExpandProperty Node

        $element = $script:un.CreateElement($elementName, $script:un.DocumentElement.NamespaceURI)
        [Void]$rootElement.AppendChild($element)
        if ($Action)   { [Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, $Action) }
        if ($KeyValue) { [Void]$element.SetAttribute('keyValue', $script:wcmNamespaceUrl, $KeyValue) }
        $element.InnerText = $Text
    }

    $TCPIPInterfacesNode = '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-TCPIP"]'


    if (-not ($script:un | Select-Xml -XPath "$TCPIPInterfacesNode/un:Interfaces" -Namespace $script:ns | Select-Object -ExpandProperty Node))
    {
        Add-XmlGroup -XPath "$TCPIPInterfacesNode" -ElementName 'Interfaces'
        $order = 1
    }

    Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces" -ElementName 'Interface' -Action 'add'
    Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface" -ElementName 'Ipv4Settings'
    Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv4Settings" -ElementName 'DhcpEnabled' -Text "$(([string](-not ([boolean]($ipAddresses -match '\.')))).ToLower())"
    #Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv4Settings" -ElementName 'Metric' -Text '10'
    #Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv4Settings" -ElementName 'RouterDiscoveryEnabled' -Text 'false'

    Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface" -ElementName 'Ipv6Settings'
    Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv6Settings" -ElementName 'DhcpEnabled' -Text "$(([string](-not ([boolean]($ipAddresses -match ':')))).ToLower())"
    #Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv6Settings" -ElementName 'Metric' -Text '10'
    #Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Ipv6Settings" -ElementName 'RouterDiscoveryEnabled' -Text 'false'

    Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface" -ElementName 'Identifier' -Text "$Interfacename"

    if ($IpAddresses)
    {
        Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface" -ElementName 'UnicastIpAddresses'
        $ipCount = 1
        foreach ($ipAddress in $IpAddresses)
        {
            Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:UnicastIpAddresses" -ElementName 'IpAddress' -Text "$($ipAddress.IpAddress.AddressAsString)/$($ipAddress.Cidr)" -Action 'add' -KeyValue "$(($ipCount++))"
        }
    }

    if ($gateways)
    {
        Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface" -ElementName 'Routes'
        $gatewayCount = 0
        foreach ($gateway in $gateways)
        {
            Add-XmlGroup -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Routes" -ElementName 'Route' -Action 'add'
            Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Routes/un:Route" -ElementName 'Identifier' -Text "$(($gatewayCount++))"
            #Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Routes/un:Route" -ElementName 'Metric' -Text '0'
            Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Routes/un:Route" -ElementName 'NextHopAddress' -Text $gateway
            if ($gateway -match ':')
            {
                $prefix = '::/0'
            }
            else
            {
                $prefix = '0.0.0.0/0'
            }
            Add-XmlElement -XPath "$TCPIPInterfacesNode/un:Interfaces/un:Interface/un:Routes/un:Route" -ElementName 'Prefix' -Text $prefix
        }

    }

    $DNSClientNode = '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-DNS-Client"]'

    #if ($UseDomainNameDevolution)
    #{
    # Add-XmlElement -XPath "$DNSClientNode" -ElementName 'UseDomainNameDevolution' -Text "$UseDomainNameDevolution"
    #}

    if ($DNSSuffixSearchOrder)
    {
        if (-not ($script:un | Select-Xml -XPath "$DNSClientNode/un:DNSSuffixSearchOrder" -Namespace $script:ns | Select-Object -ExpandProperty Node))
        {
            Add-XmlGroup -XPath "$DNSClientNode" -ElementName 'DNSSuffixSearchOrder' -Action 'add'
            $order = 1
        }
        else
        {
            $nodes = ($script:un | Select-Xml -XPath "$DNSClientNode/un:DNSSuffixSearchOrder" -Namespace $script:ns  | Select-Object -ExpandProperty Node).childnodes
            $order = ($nodes | Measure-Object).count+1
        }

        foreach ($DNSSuffix in $DNSSuffixSearchOrder)
        {
            Add-XmlElement -XPath "$DNSClientNode/un:DNSSuffixSearchOrder" -ElementName 'DomainName' -Text $DNSSuffix -Action 'add' -KeyValue "$(($order++))"
        }
    }

    if (-not ($script:un | Select-Xml -XPath "$DNSClientNode/un:Interfaces" -Namespace $script:ns | Select-Object -ExpandProperty Node))
    {
        Add-XmlGroup -XPath "$DNSClientNode" -ElementName 'Interfaces'
        $order = 1
    }

    Add-XmlGroup -XPath "$DNSClientNode/un:Interfaces" -ElementName 'Interface' -Action 'add'
    Add-XmlElement -XPath "$DNSClientNode/un:Interfaces/un:Interface" -ElementName 'Identifier' -Text "$Interfacename"

    if ($DnsDomain)
    {
        Add-XmlElement -XPath "$DNSClientNode/un:Interfaces/un:Interface" -ElementName 'DNSDomain' -Text "$DnsDomain"
    }

    if ($dnsServers)
    {
        Add-XmlGroup -XPath "$DNSClientNode/un:Interfaces/un:Interface" -ElementName 'DNSServerSearchOrder'
        $dnsServersCount = 1
        foreach ($dnsServer in $dnsServers)
        {
            Add-XmlElement -XPath "$DNSClientNode/un:Interfaces/un:Interface/un:DNSServerSearchOrder" -ElementName 'IpAddress' -Text $dnsServer -Action 'add' -KeyValue "$(($dnsServersCount++))"
        }
    }

    Add-XmlElement -XPath "$DNSClientNode/un:Interfaces/un:Interface" -ElementName 'EnableAdapterDomainNameRegistration' -Text $EnableAdapterDomainNameRegistration

    Add-XmlElement -XPath "$DNSClientNode/un:Interfaces/un:Interface" -ElementName 'DisableDynamicUpdate' -Text $DisableDynamicUpdate


    $NetBTNode = '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-NetBT"]'

    if (-not ($script:un | Select-Xml -XPath "$NetBTNode/un:Interfaces" -Namespace $script:ns | Select-Object -ExpandProperty Node))
    {
        Add-XmlGroup -XPath "$NetBTNode" -ElementName 'Interfaces'
    }

    Add-XmlGroup -XPath "$NetBTNode/un:Interfaces" -ElementName 'Interface' -Action 'add'
    Add-XmlElement -XPath "$NetBTNode/un:Interfaces/un:Interface" -ElementName 'NetbiosOptions' -Text $NetbiosOptions
    Add-XmlElement -XPath "$NetBTNode/un:Interfaces/un:Interface" -ElementName 'Identifier' -Text "$Interfacename"

}
function Add-UnattendedWindowsRenameNetworkAdapters
{
    function Add-XmlGroup
    {
        param
        (
            $XPath,
            $ElementName,
            $Action,
            $KeyValue
        )

        Write-Debug -Message "XPath=$XPath"
        Write-Debug -Message "ElementName=$ElementName"

        #$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
        #$wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'

        $rootElement = $script:un | Select-Xml -XPath $xPath -Namespace $script:ns | Select-Object -ExpandProperty Node

        $element = $script:un.CreateElement($elementName)
        [Void]$rootElement.AppendChild($element)
        #[Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, 'add')
        if ($Action)   { [Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, $Action) }
        if ($KeyValue) { [Void]$element.SetAttribute('keyValue', $script:wcmNamespaceUrl, $KeyValue) }
    }

    function Add-XmlElement
    {
        param
        (
            $rootElement,
            $ElementName,
            $Text,
            $Action,
            $KeyValue
        )

        Write-Debug -Message "XPath=$XPath"
        Write-Debug -Message "ElementName=$ElementName"
        Write-Debug -Message "Text=$Text"

        #$ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
        #$wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'

        #$rootElement = $script:un | Select-Xml -XPath $xPath -Namespace $script:ns | Select-Object -ExpandProperty Node

        $element = $script:un.CreateElement($elementName)
        [Void]$rootElement.AppendChild($element)
        if ($Action)   { [Void]$element.SetAttribute('action', $script:wcmNamespaceUrl, $Action) }
        if ($KeyValue) { [Void]$element.SetAttribute('keyValue', $script:wcmNamespaceUrl, $KeyValue) }
        $element.InnerText = $Text
    }

    $order = (($script:un | Select-Xml -XPath "$WinPENode/un:RunSynchronousCommand" -Namespace $script:ns).node.childnodes.order | Measure-Object -Maximum).maximum
    $order++

    Add-XmlGroup -XPath '//un:settings[@pass = "oobeSystem"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]/un:FirstLogonCommands' -ElementName 'SynchronousCommand' -Action 'add'

    $nodes = ($script:un | Select-Xml -XPath '//un:settings[@pass = "oobeSystem"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]/un:FirstLogonCommands' -Namespace $script:ns  |
    Select-Object -ExpandProperty Node).childnodes

    $order = ($nodes | Measure-Object).count
    $rootElement = $nodes[$order-1]

    Add-XmlElement -RootElement $rootElement -ElementName 'Description' -Text 'Rename network adapters'
    Add-XmlElement -RootElement $rootElement -ElementName 'Order' -Text "$order"
    Add-XmlElement -RootElement $rootElement -ElementName 'CommandLine' -Text 'powershell.exe -executionpolicy bypass -file "c:\RenameNetworkAdapters.ps1"'

}
function Add-UnattendedWindowsSynchronousCommand
{
    param (
        [Parameter(Mandatory)]
        [string]$Command,

        [Parameter(Mandatory)]
        [string]$Description
    )

    $highestOrder = ($un | Select-Xml -Namespace $ns -XPath //un:RunSynchronous).Node.RunSynchronousCommand.Order |
    Sort-Object -Property { [int]$_ } -Descending |
    Select-Object -First 1

    $runSynchronousNode = ($un | Select-Xml -Namespace $ns -XPath //un:RunSynchronous).Node

    $runSynchronousCommandNode = $un.CreateElement('RunSynchronousCommand')

    [Void]$runSynchronousCommandNode.SetAttribute('action', $wcmNamespaceUrl, 'add')

    $runSynchronousCommandDescriptionNode = $un.CreateElement('Description')
    $runSynchronousCommandDescriptionNode.InnerText = $Description

    $runSynchronousCommandOrderNode = $un.CreateElement('Order')
    $runSynchronousCommandOrderNode.InnerText = ([int]$highestOrder + 1)

    $runSynchronousCommandPathNode = $un.CreateElement('Path')
    $runSynchronousCommandPathNode.InnerText = $Command

    [void]$runSynchronousCommandNode.AppendChild($runSynchronousCommandDescriptionNode)
    [void]$runSynchronousCommandNode.AppendChild($runSynchronousCommandOrderNode)
    [void]$runSynchronousCommandNode.AppendChild($runSynchronousCommandPathNode)

    [void]$runSynchronousNode.AppendChild($runSynchronousCommandNode)
}
function Export-UnattendedWindowsFile
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $script:un.Save($Path)
}
function Import-UnattendedWindowsContent
{
    param
    (
        [Parameter(Mandatory = $true)]
        [xml]
        $Content
    )

    $script:un = $Content
    $script:ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
    $Script:wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'
}
function Set-UnattendedWindowsDomain
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password,

        [Parameter()]
        [string]$OrganizationalUnit
    )

    $idNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-UnattendedJoin"]/un:Identification' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $idNode.RemoveAll()

    $joinDomainNode = $script:un.CreateElement('JoinDomain')
    $joinDomainNode.InnerText = $DomainName

    $credentialsNode = $script:un.CreateElement('Credentials')
    $domainNode = $script:un.CreateElement('Domain')
    $domainNode.InnerText = $DomainName
    $userNameNode = $script:un.CreateElement('Username')
    $userNameNode.InnerText = $Username
    $passwordNode = $script:un.CreateElement('Password')
    $passwordNode.InnerText = $Password

    if ($OrganizationalUnit)
    {
        $ouNode = $script:un.CreateElement('MachineObjectOU')
        $ouNode.InnerText = $OrganizationalUnit
        $null = $idNode.AppendChild($ouNode)
    }

    [Void]$credentialsNode.AppendChild($domainNode)
    [Void]$credentialsNode.AppendChild($userNameNode)
    [Void]$credentialsNode.AppendChild($passwordNode)

    [Void]$idNode.AppendChild($credentialsNode)
    [Void]$idNode.AppendChild($joinDomainNode)
}

function Set-UnattendedWindowsAdministratorName
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Name
    )

    $shellNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "oobeSystem"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $shellNode.UserAccounts.LocalAccounts.LocalAccount.Name = $Name
    $shellNode.UserAccounts.LocalAccounts.LocalAccount.DisplayName = $Name
}
function Set-UnattendedWindowsAdministratorPassword
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$Password
    )

    $shellNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "oobeSystem"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $shellNode.UserAccounts.AdministratorPassword.Value = $Password
    $shellNode.UserAccounts.AdministratorPassword.PlainText = 'true'

    $shellNode.UserAccounts.LocalAccounts.LocalAccount.Password.Value = $Password
}
function Set-UnattendedWindowsAntiMalware
{
    param (
        [Parameter(Mandatory = $true)]
        [bool]$Enabled
    )

    $node = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Security-Malware-Windows-Defender"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    if ($Enabled)
    {
        $node.DisableAntiSpyware = 'true'
    }
    else
    {
        $node.DisableAntiSpyware = 'false'
    }
}
function Set-UnattendedWindowsAutoLogon
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$DomainName,

        [Parameter(Mandatory = $true)]
        [string]$Username,

        [Parameter(Mandatory = $true)]
        [string]$Password
    )

    $shellNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $autoLogonNode = $script:un.CreateElement('AutoLogon')

    $passwordNode = $script:un.CreateElement('Password')
    $passwordValueNode = $script:un.CreateElement('Value')
    $passwordValueNode.InnerText = $Password

    $domainNode = $script:un.CreateElement('Domain')
    $domainNode.InnerText = $DomainName

    $enabledNode = $script:un.CreateElement('Enabled')
    $enabledNode.InnerText = 'true'

    $logonCount = $script:un.CreateElement('LogonCount')
    $logonCount.InnerText = '9999'

    $userNameNode = $script:un.CreateElement('Username')
    $userNameNode.InnerText = $Username

    [Void]$autoLogonNode.AppendChild($passwordNode)
    [Void]$passwordNode.AppendChild($passwordValueNode)
    [Void]$autoLogonNode.AppendChild($domainNode)
    [Void]$autoLogonNode.AppendChild($enabledNode)
    [Void]$autoLogonNode.AppendChild($logonCount)
    [Void]$autoLogonNode.AppendChild($userNameNode)

    [Void]$shellNode.AppendChild($autoLogonNode)
}

function Set-UnattendedWindowsComputerName
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )
    $component = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $component.ComputerName = $ComputerName
}
function Set-UnattendedWindowsFirewallState
{
    param (
        [Parameter(Mandatory = $true)]
        [boolean]$State
    )

    $setupNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Networking-MPSSVC-Svc"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $WindowsFirewallStateNode = $script:un.CreateElement('DomainProfile_EnableFirewall')
    $WindowsFirewallStateNode.InnerText = ([string]$State).ToLower()
    [Void]$setupNode.AppendChild($WindowsFirewallStateNode)

    $WindowsFirewallStateNode = $script:un.CreateElement('PrivateProfile_EnableFirewall')
    $WindowsFirewallStateNode.InnerText = ([string]$State).ToLower()
    [Void]$setupNode.AppendChild($WindowsFirewallStateNode)

    $WindowsFirewallStateNode = $script:un.CreateElement('PublicProfile_EnableFirewall')
    $WindowsFirewallStateNode.InnerText = ([string]$State).ToLower()
    [Void]$setupNode.AppendChild($WindowsFirewallStateNode)
}
function Set-UnattendedWindowsIpSettings
{
    param (
        [string]$IpAddress,

        [string]$Gateway,

        [String[]]$DnsServers,

        [string]$DnsDomain
    )

    $ethernetInterface = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-TCPIP"]/un:Interfaces/un:Interface[un:Identifier = "Ethernet"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    if (-not $ethernetInterface)
    {
        $ethernetInterface = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-TCPIP"]/un:Interfaces/un:Interface[un:Identifier = "Local Area Connection"]' -Namespace $ns |
        Select-Object -ExpandProperty Node
    }

    if ($IpAddress)
    {
        $ethernetInterface.Ipv4Settings.DhcpEnabled = 'false'
        $ethernetInterface.UnicastIpAddresses.IpAddress.InnerText = $IpAddress
    }

    if ($Gateway)
    {
        $InterfaceElement = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-TCPIP"]/un:Interfaces/un:Interface' -Namespace $ns |
        Select-Object -ExpandProperty Node

        $RoutesNode = $script:un.CreateElement('Routes')
        [Void]$InterfaceElement.AppendChild($RoutesNode)

        $routes = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-TCPIP"]/un:Interfaces/un:Interface/un:Routes' -Namespace $ns |
        Select-Object -ExpandProperty Node

        $routeElement = $script:un.CreateElement('Route')
        $identifierElement = $script:un.CreateElement('Identifier')
        $prefixElement = $script:un.CreateElement('Prefix')
        $nextHopAddressElement = $script:un.CreateElement('NextHopAddress')
        [void]$routeElement.AppendChild($identifierElement)
        [void]$routeElement.AppendChild($prefixElement)
        [void]$routeElement.AppendChild($nextHopAddressElement)

        [Void]$routeElement.SetAttribute('action', $wcmNamespaceUrl, 'add')
        $identifierElement.InnerText = '0'
        $prefixElement.InnerText = '0.0.0.0/0'
        $nextHopAddressElement.InnerText = $Gateway

        [void]$RoutesNode.AppendChild($routeElement)
    }

  <#
    <Routes>
    <Route wcm:action="add">
    <Identifier>0</Identifier>
    <Prefix>0.0.0.0/0</Prefix>
    <NextHopAddress></NextHopAddress>
    </Route>
    </Routes>
  #>


    if ($DnsServers)
    {
        $ethernetInterface = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-DNS-Client"]/un:Interfaces/un:Interface[un:Identifier = "Ethernet"]' -Namespace $ns |
        Select-Object -ExpandProperty Node -ErrorAction SilentlyContinue

        if (-not $ethernetInterface)
        {
            $ethernetInterface = $script:un |
            Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-DNS-Client"]/un:Interfaces/un:Interface[un:Identifier = "Local Area Connection"]' -Namespace $ns |
            Select-Object -ExpandProperty Node -ErrorAction SilentlyContinue
        }

    <#
        <DNSServerSearchOrder>
        <IpAddress wcm:action="add" wcm:keyValue="1">10.0.0.10</IpAddress>
        </DNSServerSearchOrder>
    #>


        $dnsServerSearchOrder = $script:un.CreateElement('DNSServerSearchOrder')
        $i = 1
        foreach ($dnsServer in $DnsServers)
        {
            $ipAddressElement = $script:un.CreateElement('IpAddress')
            [Void]$ipAddressElement.SetAttribute('action', $wcmNamespaceUrl, 'add')
            [Void]$ipAddressElement.SetAttribute('keyValue', $wcmNamespaceUrl, "$i")
            $ipAddressElement.InnerText = $dnsServer

            [Void]$dnsServerSearchOrder.AppendChild($ipAddressElement)
            $i++
        }

        [Void]$ethernetInterface.AppendChild($dnsServerSearchOrder)
    }

    <#
        <DNSDomain>something.com</DNSDomain>
    #>

    if ($DnsDomain)
    {
        $ethernetInterface = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-DNS-Client"]/un:Interfaces/un:Interface[un:Identifier = "Ethernet"]' -Namespace $ns |
        Select-Object -ExpandProperty Node -ErrorAction SilentlyContinue

        if (-not $ethernetInterface)
        {
            $ethernetInterface = $script:un |
            Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-DNS-Client"]/un:Interfaces/un:Interface[un:Identifier = "Local Area Connection"]' -Namespace $ns |
            Select-Object -ExpandProperty Node -ErrorAction SilentlyContinue
        }

        $dnsDomainElement = $script:un.CreateElement('DNSDomain')
        $dnsDomainElement.InnerText = $DnsDomain

        [Void]$ethernetInterface.AppendChild($dnsDomainElement)
    }
}
function Set-UnattendedWindowsLocalIntranetSites
{
    param (
        [Parameter(Mandatory = $true)]
        [string[]]$Values
    )

    $ieNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-IE-InternetExplorer"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $ieNode.LocalIntranetSites = $Values -join ';'
}
function Set-UnattendedWindowsPackage
{
    param
    (
        [string[]]$Package
    )
}
function Set-UnattendedWindowsProductKey
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$ProductKey
    )

    $setupNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $productKeyNode = $script:un.CreateElement('ProductKey')
    $productKeyNode.InnerText = $ProductKey
    [Void]$setupNode.AppendChild($productKeyNode)
}
function Set-UnattendedWindowsTimeZone
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$TimeZone
    )

    $component = $script:un |
        Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns |
        Select-Object -ExpandProperty Node

    $component.TimeZone = $TimeZone
}

function Set-UnattendedWindowsUserLocale
{
    param (
        [Parameter(Mandatory = $true)]
        [string]$UserLocale
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $component = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "oobeSystem"]/un:component[@name = "Microsoft-Windows-International-Core"]' -Namespace $ns |
    Select-Object -ExpandProperty Node

    #this is for getting the input locale strings like '0409:00000409'
    $component.UserLocale = $UserLocale

    if ($IsLinux)
    {
        $inputLocale = '0409:00000409'
    }
    else
    {
        try
        {
            $inputLocale = @((New-WinUserLanguageList -Language $UserLocale).InputMethodTips)
            $inputLocale += (New-WinUserLanguageList -Language 'en-us').InputMethodTips
        }
        catch
        {
            Remove-Module -Name International -ErrorAction SilentlyContinue -Force
            Get-ChildItem -Directory -Path ([IO.Path]::GetTempPath()) -Filter RemoteIpMoProxy_International*_localhost_* | Remove-Item -Recurse -Force 
            if ((Get-Command Import-Module).Parameters.ContainsKey('UseWindowsPowerShell'))
            {
                Import-Module -Name International -UseWindowsPowerShell -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -Force
            }
            else
            {
                Import-WinModule -Name International -WarningAction SilentlyContinue -ErrorAction SilentlyContinue -Force
            }

            $inputLocale = @((New-WinUserLanguageList -Language $UserLocale).InputMethodTips)
            $inputLocale += (New-WinUserLanguageList -Language 'en-us').InputMethodTips
        }
    }
    if ($inputLocale)
    {
        $component.InputLocale = ($inputLocale -join ';')
    }
}

function Set-UnattendedWindowsWorkgroup
{
    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $WorkgroupName
    )

    $idNode = $script:un |
    Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-UnattendedJoin"]/un:Identification' -Namespace $ns |
    Select-Object -ExpandProperty Node

    $idNode.RemoveAll()

    $workGroupNode = $script:un.CreateElement('JoinWorkgroup')
    $workGroupNode.InnerText = $WorkgroupName
    [Void]$idNode.AppendChild($workGroupNode)
}
function Add-UnattendedNetworkAdapter
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$Interfacename,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [AutomatedLab.IPNetwork[]]$IpAddresses,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [AutomatedLab.IPAddress[]]$Gateways,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [AutomatedLab.IPAddress[]]$DnsServers,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$ConnectionSpecificDNSSuffix,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$DnsDomain,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$UseDomainNameDevolution,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$DNSSuffixSearchOrder,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$EnableAdapterDomainNameRegistration,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$DisableDynamicUpdate,

        [Parameter(ParameterSetName='Windows')]
        [Parameter(ParameterSetName='Kickstart')]
        [Parameter(ParameterSetName='Yast')]
        [Parameter(ParameterSetName='CloudInit')]
        [string]$NetbiosOptions,

        [Parameter(ParameterSetName='Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName='Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName='CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Add-UnattendedRenameNetworkAdapters
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param
    (
        [Parameter(ParameterSetName='Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName='Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName='CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    & $command
}
function Add-UnattendedSynchronousCommand
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName='Windows', Mandatory = $true)]
        [Parameter(ParameterSetName='Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName='Yast', Mandatory = $true)]
        [Parameter(ParameterSetName='CloudInit', Mandatory = $true)]
        [string]$Command,

        [Parameter(ParameterSetName='Windows', Mandatory = $true)]
        [Parameter(ParameterSetName='Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName='Yast', Mandatory = $true)]
        [Parameter(ParameterSetName='CloudInit', Mandatory = $true)]
        [string]$Description,

        [Parameter(ParameterSetName='Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName='Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName='CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $commandObject = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $commandObject -Parameters $PSBoundParameters
    & $commandObject @parameters
}

function Export-UnattendedFile
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName='Windows', Mandatory = $true)]
        [Parameter(ParameterSetName='Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName='Yast', Mandatory = $true)]
        [Parameter(ParameterSetName='CloudInit', Mandatory = $true)]
        [string]$Path,

        [Parameter(ParameterSetName='Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName='Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName='CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Get-UnattendedContent
{
    [CmdletBinding()]
    param ()

    return $script:un
}
function Import-UnattendedContent
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string[]]
        $Content,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}

function Import-UnattendedFile
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $script:un = [xml](Get-Content -Path $Path)
    $script:ns = @{ un = 'urn:schemas-microsoft-com:unattend' }
    $Script:wcmNamespaceUrl = 'http://schemas.microsoft.com/WMIConfig/2002/State'
}

function Set-UnattendedAdministratorName
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Name,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedAdministratorPassword
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Password,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedAntiMalware
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [bool]$Enabled,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedAutoLogon
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$DomainName,

        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Username,

        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Password,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedComputerName
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$ComputerName,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedDomain
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$DomainName,

        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Username,

        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$Password,

        [Parameter()]
        [string]$OrganizationalUnit,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}

function Set-UnattendedFirewallState
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [boolean]$State,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedIpSettings
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows')]
        [Parameter(ParameterSetName = 'Kickstart')]
        [Parameter(ParameterSetName = 'Yast')]
        [Parameter(ParameterSetName = 'CloudInit')]
        [string]$IpAddress,

        [Parameter(ParameterSetName = 'Windows')]
        [Parameter(ParameterSetName = 'Kickstart')]
        [Parameter(ParameterSetName = 'Yast')]
        [Parameter(ParameterSetName = 'CloudInit')]
        [string]$Gateway,

        [Parameter(ParameterSetName = 'Windows')]
        [Parameter(ParameterSetName = 'Kickstart')]
        [Parameter(ParameterSetName = 'Yast')]
        [Parameter(ParameterSetName = 'CloudInit')]
        [String[]]$DnsServers,

        [Parameter(ParameterSetName = 'Windows')]
        [Parameter(ParameterSetName = 'Kickstart')]
        [Parameter(ParameterSetName = 'Yast')]
        [Parameter(ParameterSetName = 'CloudInit')]
        [string]$DnsDomain,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedLocalIntranetSites
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string[]]$Values,

        [Parameter(ParameterSetName='Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName='Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName='CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedPackage
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string[]]$Package,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedProductKey
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$ProductKey,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedTimeZone
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param
    (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$TimeZone,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedUserLocale
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$UserLocale,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}
function Set-UnattendedWorkgroup
{
    [CmdletBinding(DefaultParameterSetName = 'Windows')]
    param (
        [Parameter(ParameterSetName = 'Windows', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Kickstart', Mandatory = $true)]
        [Parameter(ParameterSetName = 'Yast', Mandatory = $true)]
        [Parameter(ParameterSetName = 'CloudInit', Mandatory = $true)]
        [string]$WorkgroupName,

        [Parameter(ParameterSetName = 'Kickstart')]
        [switch]
        $IsKickstart,

        [Parameter(ParameterSetName = 'Yast')]
        [switch]
        $IsAutoYast,

        [Parameter(ParameterSetName = 'CloudInit')]
        [switch]
        $IsCloudInit
    )

    if (-not $script:un)
    {
        Write-Error 'No unattended file imported. Please use Import-UnattendedFile first'
        return
    }

    $command = Get-Command -Name $PSCmdlet.MyInvocation.MyCommand.Name.Replace('Unattended', "Unattended$($PSCmdlet.ParameterSetName)")
    $parameters = Sync-Parameter $command -Parameters $PSBoundParameters
    & $command @parameters
}

Export-ModuleMember -Function Add-UnattendedNetworkAdapter,Add-UnattendedRenameNetworkAdapters,Add-UnattendedSynchronousCommand,Export-UnattendedFile,Get-UnattendedContent,Import-UnattendedContent,Import-UnattendedFile,Set-UnattendedAdministratorName,Set-UnattendedAdministratorPassword,Set-UnattendedAntiMalware,Set-UnattendedAutoLogon,Set-UnattendedComputerName,Set-UnattendedDomain,Set-UnattendedFirewallState,Set-UnattendedIpSettings,Set-UnattendedLocalIntranetSites,Set-UnattendedPackage,Set-UnattendedProductKey,Set-UnattendedTimeZone,Set-UnattendedUserLocale,Set-UnattendedWorkgroup