GuardedFabricTools.psm1

function New-ShieldingDataAnswerFile
{
    <#
    .SYNOPSIS
    Creates a Windows unattended installatoin answer file for use in Shielding Data Files.
 
    .DESCRIPTION
    Creates a Windows unattended installation answer file for use in Shielding Data Files. The answer file assumes the guest OS is Windows Server 2016 (Server with Desktop Experience or Server Core edition) and that the guarded fabric is managed by System Center Virtual Machine Manager 2016.
 
    .PARAMETER AdminPassword
    The password assigned to the default local administrator account.
 
    .PARAMETER BackupBitLockerKeyProtector
    Schedules a task to back up the BitLocker recovery key to Active Directory after deployment.
 
    .PARAMETER DomainJoin
    The name of the domain to which the VM will join. When omitted, the VM will belong to a workgroup.
 
    .PARAMETER DomainJoinCredential
    The credentials used to join the VM to the domain.
 
    .PARAMETER DscPullServerUrl
    The URL of your DSC pull server used to configure the Local Configuration Manager. A configuration ID is also required when using this setting.
 
    .PARAMETER DscConfigurationID
    The configuration ID to request from the DSC pull server.
 
    .PARAMETER Language
    The desired language and locale for the operating system. For example en-us, fr-ca, zh-hk, etc.
 
    .PARAMETER Path
    The location where the answer file will be saved.
 
    .PARAMETER ProductKey
    Configures whether the answer file contains a placeholder for a product key. Use "None" if your OS is volume licensed or an evaluation edition. Use "UserSupplied" when a product key is required. When set to "UserSupplied", Virtual Machine Manager is expected to provide the product key.
 
    .PARAMETER RDPCertificateFilePath
    Path to a certificate file containing the public and private keys used to configure Remote Desktop Services on the VM.
 
    .PARAMETER RDPCertificatePassword
    The password for the RDP certificate file.
 
    .PARAMETER SetupScriptFilePath
    Path to a PowerShell script (.ps1) containing custom configuration information to run during OS intallation.
 
    .PARAMETER StaticIP
    Adds configuration information to the answer file to set IP configuration information using VMM Static IP Pools. You will need to ensure your Static IP Pool shares an IPv4 address, MAC address, gateway, DNS server, and prefix.
 
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -DomainJoin redmond.corp.microsoft.com -DomainJoinCredential $mycreds -RDPCertificateFilePath C:\RDPCert.pfx -RDPCertificatePassword $rdppwd -ProductKey UserSupplied -Path C:\unattend.xml
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -DomainJoin redmond.corp.microsoft.com -DomainJoinCredential $mycreds -ProductKey UserSupplied -Path C:\unattend.xml
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -ProductKey UserSupplied -Path C:\unattend.xml
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -StartupScriptFilename HelloWorld.ps1
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -StaticIP
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -DSCPullServer pullserver.com
 
    .EXAMPLE
 
    New-ShieldingDataAnswerFile -AdminPassword $adminpwd -Language ar-ma
    #>


    Param
    (
        [Parameter (Mandatory=$true)]
        [SecureString]$AdminPassword,

        [Parameter (ParameterSetName='DomainJoin',
                    Mandatory=$true)]
        [Parameter (ParameterSetName='DomainJoinNoRDP',
                    Mandatory=$true)]
        [string]$DomainJoin,

        [Parameter (ParameterSetName='DomainJoin',
                    Mandatory=$true)]
        [Parameter (ParameterSetName='DomainJoinNoRDP',
                    Mandatory=$true)]
        [PSCredential]$DomainJoinCredential,

        [Parameter (ParameterSetName='WorkgroupRDP',
                    Mandatory=$true)]
        [Parameter (ParameterSetName='DomainJoin',
                    Mandatory=$true)]
        [string]$RDPCertificateFilePath,

        [Parameter (ParameterSetName='WorkgroupRDP',
                    Mandatory=$true)]
        [Parameter (ParameterSetName='DomainJoin',
                    Mandatory=$true)]
        [SecureString]$RDPCertificatePassword,

        [Parameter (ParameterSetName='DomainJoin',
                    Mandatory=$false)]
        [Parameter (ParameterSetName='DomainJoinNoRDP',
                    Mandatory=$false)]
        [Parameter (ParameterSetName='WorkgroupRDP',
                    Mandatory=$false)]
        [Parameter (ParameterSetName='WorkgroupNoRDP',
                    Mandatory=$false)]
        [string]$SetupScriptFilePath,

        [string]$DSCPullServerURL,

        [string]$DSCConfigurationID,

        [Parameter()]
        [ValidateSet("None","UserSupplied")]
        [string] $ProductKey = "None",

        [Switch]$StaticIP,

        [Switch]$BackupBitLockerKeyProtector,

        [string]$Path = ".\unattend.xml",

        [string]$Language = "en-US"
    )

    Set-StrictMode -Version 3.0

    ### Helper function ###

    Function SecureStringToString {
        Param ($MySecureString)
        Try 
        {
            $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($MySecureString)
            $MyString = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
            return $MyString
        }
        Finally 
        {
            [System.Runtime.InteropServices.Marshal]::FreeBSTR($BSTR)
        }
    }

    $AdminStringPassword = SecureStringToString $AdminPassword

    ### Computer name ###


    $content = '<?xml version="1.0" encoding="utf-8"?>
    <unattend xmlns="urn:schemas-microsoft-com:unattend">
        <servicing></servicing>
        <settings pass="specialize">
            <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <ComputerName>@ComputerName@</ComputerName>'


    ### Product Key ###

    if ($ProductKey -eq 'UserSupplied') {
        $content += '
                <ProductKey>@ProductKey@</ProductKey>'

    }

    $content += '
            </component>'


    $content += '
            <!-- This is where all the RunSynchronousCommand lies. You can add extra commands here -->
            <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <RunSynchronous>'


    $order = 0


    ### Import RDP file ###


    if ($PSCmdlet.ParameterSetName -eq "DomainJoin" -Or 
        $PSCmdlet.ParameterSetName -eq "WorkgroupRDP") {

        $RDPStringPassword = SecureStringToString $RDPCertificatePassword

        $RDPCertificateFilename = Split-Path -Path $RDPCertificateFilePath -Leaf -Resolve


        Get-Item ($RDPCertificateFilePath) | ForEach-Object  {
            $certobject = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
            $certobject.Import($_.FullName, $RDPCertificatePassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
            $certThumb = $certobject.Thumbprint
        }

        $content += '
                    <!-- You can change the RDP Certificate password here -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <!-- Update with your certificate password -->
                        <Path>certutil -p &quot;'
+[System.Security.SecurityElement]::Escape($RDPStringPassword)+'&quot; -importpfx %SYSTEMDRIVE%\temp\'+[System.Security.SecurityElement]::Escape($RDPCertificateFilename)+'</Path>
                        <WillReboot>OnRequest</WillReboot>
                        <Description>Import certificate</Description>
                    </RunSynchronousCommand>'

        $order += 1
    }

    ### Creating the SetupComplete.cmd ###

    $content += '
                    <!-- We are creating our own SetupComplete.cmd to run scripts during the setup process. Add commands to this cmd file if you have custom setup actions to do -->
                    <RunSynchronousCommand wcm:action="add">
                        <Description>If there is one, copy original setupcomplete.cmd to a unique file</Description>
                        <Order>'
+($order + 1)+'</Order>
                        <Path>cmd /C if exist {%WINDIR%\Setup\Scripts\SetupComplete.cmd} (copy %WINDIR%\Setup\Scripts\SetupComplete.cmd %WINDIR%\Setup\Scripts\SetupComplete_Original.cmd /y)</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 2)+'</Order>
                        <Description>mkdir Scripts since Windows looks for SetupComplete.cmd in that dir. If the dir exists, it should be fine.</Description>
                        <Path>cmd.exe /C mkdir %WINDIR%\Setup\Scripts</Path>
                    </RunSynchronousCommand>'

    $order += 2

    ### Delete the RDPCert file ###

    if ($PSCmdlet.ParameterSetName -eq "DomainJoin" -Or 
        $PSCmdlet.ParameterSetName -eq "WorkgroupRDP") {
        $content += '
                    <!-- We will delete the RDP certificate and the answer files to hide sensitive data -->
                    <RunSynchronousCommand wcm:action="add">
                        <Description>Delete the RDPCert file</Description>
                        <Order>'
+($order + 1)+'</Order>
                        <Path>cmd /C echo del %SYSDRIVE%\temp\'
+[System.Security.SecurityElement]::Escape($RDPCertificateFilename)+' &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

        $order += 1
    }

    ### Remove all unattend.xml files ###

    $content += '
                    <RunSynchronousCommand wcm:action="add">
                        <Description>Delete the answer file from C:</Description>
                        <Order>'
+($order + 1)+'</Order>
                        <Path>cmd /C echo del %SYSDRIVE%\unattend.xml &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

    $order += 1

    $content += '
                    <RunSynchronousCommand wcm:action="add">
                        <Description>Delete the answer file from C:\Windows\Panther</Description>
                        <Order>'
+($order + 1)+'</Order>
                        <Path>cmd /C echo del %WINDIR%\Panther\unattend.xml &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

    $order += 1

    ### RDP Thumbprint ###

    if ($PSCmdlet.ParameterSetName -eq "DomainJoin" -Or 
        $PSCmdlet.ParameterSetName -eq "WorkgroupRDP") {
        $content += '
                    <!-- This is to tell the computer to use the RDP certificate you have just imported rather than using its own -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <Description>Put certificate configuration command in SetupComplete.cmd</Description>
                        <Path>cmd /C echo wmic /namespace:\\root\cimv2\TerminalServices PATH Win32_TSGeneralSetting Set SSLCertificateSHA1Hash=&quot;'
+[System.Security.SecurityElement]::Escape($certThumb)+'&quot; &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

        $order += 1
    }

    ### DSC pull server ###

    if ($DSCPullServerURL) {
        $content += '
            <!-- You can modify the DSC pull server URL and the DSC configuration ID here -->
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 1)+'</Order>
              <Description>Write the Configuration file for the DSC Pull Server Part 1</Description>'

        if ($DSCConfigurationID) {
            $content += '
              <Path>cmd /C echo [DscLocalConfigurationManager()]configuration meta{Node localhost{Settings{ConfigurationMode = "ApplyAndAutocorrect"; ConfigurationID = "'
+[System.Security.SecurityElement]::Escape($DSCConfigurationID)+'"; &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>'
        } else {
            $content += '
              <Path>cmd /C echo [DscLocalConfigurationManager()]configuration meta{Node localhost{Settings{ConfigurationMode = "ApplyAndAutocorrect"; &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>'

        }
        $content += '
            </RunSynchronousCommand>
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 2)+'</Order>
              <Description>Write the Configuration file for the DSC Pull Server Part 2</Description>
              <Path>cmd /C echo RefreshMode = "Pull"}ConfigurationRepositoryWeb PullServer{ServerURL = "'
+[System.Security.SecurityElement]::Escape($DSCPullServerURL)+'"}}} &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>
            </RunSynchronousCommand>
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 3)+'</Order>
              <Description>Make the meta directory to put the localhost.meta.mof</Description>
              <Path>cmd /C echo mkdir %WINDIR%\Setup\Scripts\meta &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>
            </RunSynchronousCommand>
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 4)+'</Order>
              <Description>Write the PullServerConfig.ps1 script - Initializing the script</Description>
              <Path>cmd /C echo meta -output %WINDIR%\Setup\Scripts\meta &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>
            </RunSynchronousCommand>
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 5)+'</Order>
              <Description>Write the PullServerConfig.ps1 script - Set the LCM</Description>
              <Path>cmd /C echo Set-DscLocalConfigurationManager localhost -Path %WINDIR%\Setup\Scripts\meta &gt;&gt; %WINDIR%\Setup\Scripts\PullServerConfig.ps1</Path>
            </RunSynchronousCommand>
            <RunSynchronousCommand wcm:action="add">
              <Order>'
+($order + 6)+'</Order>
              <Description>Run the PullServerConfig.ps1 script</Description>
              <Path>cmd /C echo powershell %WINDIR%\Setup\Scripts\PullServerConfig.ps1 &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
            </RunSynchronousCommand>'

        $order += 6
    }

    ### Setup Script ###

    if ($SetupScriptFilePath){

        $SetupScriptFile = New-Object System.IO.FileInfo($SetupScriptFilePath)
        $SetupScriptFilePath = $SetupScriptFile.BaseName + $SetupScriptFile.Extension

        $content += '
                    <!-- All files attached to the PDK will end up in C:\temp\, hence this will run the setup script you have included -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <Description>Run the provided Powershell Setup Script</Description>
                        <Path>cmd /C echo powershell %SYSTEMDRIVE%\temp\'
+[System.Security.SecurityElement]::Escape($SetupScriptFilePath)+ ' &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

        $order += 1
    }

    ### Script for BitLocker recovery password ###

    if (($PSCmdlet.ParameterSetName -eq "DomainJoin" -Or 
        $PSCmdlet.ParameterSetName -eq "DomainJoinNoRDP") -And
        $BackupBitLockerKeyProtector) {

        ## Create a script that creates a new task for the Task Scheduler
        ## This will be used to run the BitLockerRecoveryPassword Script
        ## as it needs to be run after SetupComplete.exe and during startup

        $content += '
                    <!-- This is writing a CreateScheduledTask PowerShell Script that creates a startup task -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 1/6</Description>
                        <Path>cmd /C echo Start-Transcript -Path %WINDIR%\Setup\Scripts\CreateScheduledTaskOutput.txt &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 2)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 1/6</Description>
                        <Path>cmd /C echo $action = New-ScheduledTaskAction -Execute "Powershell.exe" -Argument ''-NoProfile -WindowStyle Hidden -File %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1'' &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 3)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 2/6</Description>
                        <Path>cmd /C echo $trigger = New-ScheduledTaskTrigger -AtStartup &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 4)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 3/6</Description>
                        <Path>cmd /C echo $Stset = New-ScheduledTaskSettingsSet -RunOnlyIfNetworkAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 5)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 4/6</Description>
                        <Path>cmd /C echo Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "BackupBitLockerRecoveryPassword" `&gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 6)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 5/6</Description>
                        <Path>cmd /C echo -Description "Backup BitLocker Recovery Password to AD" -User "@ComputerName@\Administrator" -Password "'
+[System.Security.SecurityElement]::Escape($AdminStringPassword)+'" -Settings $Stset &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 7)+'</Order>
                        <Description>Create the CreateScheduledTask Script Part 6/6</Description>
                        <Path>cmd /C echo rm %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1 &gt;&gt; %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1</Path>
                    </RunSynchronousCommand>'

        $order += 7

        $content += '
                    <!-- This is writing a Script that runs to create a KeyProtector that uses a recovery password, and it will back up to the Active Directory -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 1/6</Description>
                        <Path>cmd /C echo Start-Transcript -Path %WINDIR%\Setup\Scripts\BitLockerRecoveryScriptOutput.txt &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
                    </RunSynchronousCommand>'


        $content += '
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 2)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 2/6</Description>
                        <Path>cmd /C echo gpupdate /force &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 3)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 3/6</Description>'

        $content += @'
                        <Path>cmd /C echo $b = Add-BitLockerKeyProtector -MountPoint %SYSTEMDRIVE% -RecoveryPasswordProtector; $kp = $b.KeyProtector ^| ?{ $_.KeyProtectorType -eq 'RecoveryPassword' }; $kpId = $kp.KeyProtectorId &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
'@

        $content += '
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 4)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 4/6</Description>
                        <Path>cmd /C echo Backup-BitLockerKeyProtector -MountPoint %SYSTEMDRIVE% -KeyProtectorId $kpId &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 5)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 5/6</Description>
                        <Path>cmd /C echo Invoke-Expression "schtasks.exe /delete /s @ComputerName@ /tn BackupBitLockerRecoveryPassword /F" &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 6)+'</Order>
                        <Description>Create the BitLockerRecoveryPassword Script Part 6/6</Description>
                        <Path>cmd /C echo Stop-Transcript &gt;&gt; %WINDIR%\Setup\Scripts\BitLockerRecoveryPassword.ps1</Path>
                    </RunSynchronousCommand>
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 7)+'</Order>
                        <Description>Run the CreateScheduledTask Script</Description>
                        <Path>cmd /C echo powershell %WINDIR%\Setup\Scripts\CreateScheduledTask.ps1 &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                    </RunSynchronousCommand>'

        $order += 7
    }

    ### End script for recovery password

    ### Shutdown the VM ###

    $content += '
                    <!-- This shuts down the VM -->
                    <RunSynchronousCommand wcm:action="add">
                        <Order>'
+($order + 1)+'</Order>
                        <Description>Put shutdown VM in SetupComplete.cmd</Description>
                        <Path>cmd /C echo shutdown /s /f &gt;&gt; %WINDIR%\Setup\Scripts\SetupComplete.cmd</Path>
                        <WillReboot>OnRequest</WillReboot>
                    </RunSynchronousCommand>'

    $order += 1

    $content += '
                </RunSynchronous>
            </component>'


    ### StaticIP

    if ($StaticIP) {
        $content += '
        <!-- Since you are using StaticIP, you must specify all these parameters -->
        <component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <Interfaces>
            <Interface wcm:action="add">
              <Ipv4Settings>
                <DhcpEnabled>false</DhcpEnabled>
              </Ipv4Settings>
              <UnicastIpAddresses>
                <IpAddress wcm:action="add" wcm:keyValue="1">@IP4Addr-1@</IpAddress>
              </UnicastIpAddresses>
              <Identifier>@MACAddr-1@</Identifier>
              <Routes>
                <Route wcm:action="add">
                  <Identifier>1</Identifier>
                  <Prefix>@Prefix-1-1@</Prefix>
                  <NextHopAddress>@NextHop-1-1@</NextHopAddress>
                </Route>
              </Routes>
            </Interface>
          </Interfaces>
        </component>
        <component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <Interfaces>
                <Interface wcm:action="add">
                    <DNSServerSearchOrder>
                        <IpAddress wcm:action="add" wcm:keyValue="1">@DnsAddr-1-1@</IpAddress>
                    </DNSServerSearchOrder>
                    <Identifier>@MACAddr-1@</Identifier>
                </Interface>
            </Interfaces>
        </component>'

    }

    ### Join Domain

    if ($PSCmdlet.ParameterSetName -eq "DomainJoin" -Or 
        $PSCmdlet.ParameterSetName -eq "DomainJoinNoRDP") {

        $DomainStringPassword = $DomainJoinCredential.GetNetworkCredential().Password
        $DomainStringUsername = $DomainJoinCredential.GetNetworkCredential().Username
        $DomainStringDomain = $DomainJoinCredential.GetNetworkCredential().Domain
    
        $DomainStringPassword = [System.Security.SecurityElement]::Escape($DomainStringPassword)
        $DomainStringUsername = [System.Security.SecurityElement]::Escape($DomainStringUsername)
        $DomainStringDomain = [System.Security.SecurityElement]::Escape($DomainStringDomain)

        $content += '
            <!-- You must provide credentials to join a domain -->
            <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <Identification>
                    <Credentials>'

        if ($DomainStringDomain) {
            $content += "
                        <Domain>$DomainStringDomain</Domain>"

        }
        $content += "
                        <Username>$DomainStringUsername</Username>
                        <Password>$DomainStringPassword</Password>
                    </Credentials>
                    <JoinDomain>$DomainJoin</JoinDomain>
                </Identification>
            </component>"


    }


    ### Allow RDP and Configure Admin Password

    $content += '
            <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <fDenyTSConnections>false</fDenyTSConnections>
            </component>
            <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <FirewallGroups>
                    <!-- Allow RDP connections through the firewall -->
                    <FirewallGroup wcm:action="add" wcm:keyValue="RDGroup">
                        <Active>true</Active>
                        <Group>@FirewallAPI.dll,-28752</Group>
                        <Profile>all</Profile>
                    </FirewallGroup>
                </FirewallGroups>
            </component>
            <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <UserAuthentication>0</UserAuthentication>
            </component>
        </settings>
        <settings pass="oobeSystem">
            <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <OOBE>
                    <HideEULAPage>true</HideEULAPage>
                    <SkipUserOOBE>true</SkipUserOOBE>
                </OOBE>
                <!-- You can add extra user accounts here, in addition to the Administrator -->
                <UserAccounts>
                    <AdministratorPassword>
                        <Value>'
+[System.Security.SecurityElement]::Escape($AdminStringPassword)+'</Value>
                        <PlainText>true</PlainText>
                    </AdministratorPassword>'


    ### Timezone and Languages

    $content += '
                </UserAccounts>
                <TimeZone>@TimeZone@</TimeZone>
            </component>
            <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <UserLocale>'
+$Language+'</UserLocale>
                <SystemLocale>'
+$Language+'</SystemLocale>
                <UILanguage>'
+$Language+'</UILanguage>
                <InputLocale>'
+$Language+'</InputLocale>
            </component>
        </settings>
    </unattend>'


    ### Save to xml file

    $xmldoc = [xml]$content
    $folder = Split-Path $Path | Resolve-Path
    $file = Split-Path $Path -Leaf
    $fullPath = Join-Path $folder.Path $file
    $xmldoc.Save($fullpath)
    if ($StaticIP) {
        Write-Warning -Message @'
    You have enabled StaticIP.
    For this answer file, we expect you to configure the VMM to contain only a single NIC and to have your Static IP Pool setup correctly. You should have configured on VMM to have one DNS server address and IPv4 static IP address. You can modify these values from the answer file if you want to do it manually:
    @IP4Addr-1@
    @MACAddr-1@
    @Prefix-1-1@
    @NextHop-1-1@
    @DnsAddr-1-1@
'@

    }
    Get-Item $fullPath
}