Public/New-UnattendXml.ps1
function New-UnattendXml { <# .SYNOPSIS Creates a new Unattend.xml file for automated Windows setup. .DESCRIPTION This command creates a new Unattend.xml file that automates Windows setup, skipping prompts and setting the administrator password. Options include joining a domain, adding user accounts, auto logon, setting the computer name, running PowerShell scripts at first boot or logon, specifying product key, time zone, input/system/user locales, UI language, registered owner/organization, and custom commands for first boot/logon/every logon. If no path is provided, the file is created in a temp folder and the path is returned. .PARAMETER AdminCredential The credential object for the local Administrator account. Required. .PARAMETER UserAccount Array of credential objects for user accounts to create and add to Administrators group. .PARAMETER JoinAccount Credential object for joining the domain. Must use Domain\User or user@domain format. .PARAMETER domain The domain to join. .PARAMETER OU The organizational unit to place the computer account into. .PARAMETER Path Output path for the Unattend.xml file. Defaults to a temp folder. .PARAMETER LogonCount Number of times the local Administrator account should automatically log in. Default is 0. .PARAMETER ComputerName The computer name to set. Default is '*'. .PARAMETER FirstLogonScriptPath PowerShell script to run at first logon. .PARAMETER FirstBootScriptPath PowerShell script to run at first boot (specialize phase). .PARAMETER ProductKey Product key to use for unattended installation. .PARAMETER TimeZone Time zone to set. Default is the local computer's time zone. .PARAMETER InputLocale System input locale and keyboard layout. Default is current system language. .PARAMETER SystemLocale Language for non-Unicode programs. Default is current system language. .PARAMETER UserLocale Per-user settings for formatting dates, times, currency, and numbers. Default is current system language. .PARAMETER UILanguage System default user interface (UI) language. Default is current system language. .PARAMETER RegisteredOwner Registered owner name. Default is 'Valued Customer'. .PARAMETER RegisteredOrganization Registered organization name. Default is 'Valued Customer'. .PARAMETER FirstBootExecuteCommand Array of hashTables for commands to execute at first boot (system context). .PARAMETER FirstLogonExecuteCommand Array of hashTables for commands to execute at first logon (auto elevated). .PARAMETER EveryLogonExecuteCommand Array of hashTables for commands to execute at every logon (not elevated). .PARAMETER enableAdministrator Enables the local Administrator account (needed for client OS if not using autoLogon or additional admin users). .EXAMPLE New-UnattendXml -AdminCredential (Get-Credential) -LogonCount 1 Creates a randomly named Unattend.xml in $env:temp that sets the Administrator password and auto logon 1 time. .EXAMPLE New-UnattendXml -Path c:\temp\Unattend.xml -AdminCredential (Get-Credential) -LogonCount 100 -FirstLogonScriptPath c:\PsTemp\firstRun.ps1 Creates an Unattend.xml at c:\temp\Unattend.xml that sets the Administrator password, sets auto logon count to 100, and runs c:\PsTemp\firstRun.ps1 at each new user's first logon. .NOTES Author: WindowsImageTools Team Requires: Administrator privileges #> [CmdletBinding(DefaultParameterSetName = 'Basic_FirstLogonScript', SupportsShouldProcess = $true)] [OutputType([System.IO.FileInfo])] Param ( # The password to have unattend.xml set the local Administrator to (minimum length 8) [Parameter(Mandatory = $true, HelpMessage = 'Local Administrator Credentials', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, Position = 0)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [Alias('AdminPassword')] [System.Management.Automation.Credential()][PSCredential] $AdminCredential, # User account/password to create and add to Administrators group [System.Management.Automation.Credential()][PSCredential[]] $UserAccount, # User account/password to join do the domain MUST use Domain\User or user@domain format [System.Management.Automation.Credential()][PSCredential] $JoinAccount, # Domain to join [string] $domain, # OU to place computer account into [string] $OU, # Output Path [Alias('FilePath', 'FullName', 'psPath', 'outfile')] [string] $Path = "$(New-TemporaryDirectory)\unattend.xml", # Number of times that the local Administrator account should automatically login (default 0) [ValidateRange(0, 1000)] [int] $LogonCount, # ComputerName (default = *) [ValidateLength(1, 15)] [string] $ComputerName = '*', # PowerShell Script to run on FirstLogon (ie. %SystemDrive%\PSTemp\FirstRun.ps1 ) [Parameter(ParameterSetName = 'Basic_FirstLogonScript')] [string] $FirstLogonScriptPath, # PowerShell Script to run on FirstBoot (ie.: %SystemDrive%\PSTemp\FirstRun.ps1 ) Executed in system context during specialize phase [Parameter(ParameterSetName = 'Basic_FirstBootScript')] [string] $FirstBootScriptPath, # The product key to use for the unattended installation. [ValidatePattern('^[A-Z0-9]{5,5}-[A-Z0-9]{5,5}-[A-Z0-9]{5,5}-[A-Z0-9]{5,5}-[A-Z0-9]{5,5}$')] [string] $ProductKey, # Timezone (default: Timezone of the local Computer) [ArgumentCompleter( { param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters # Suppress unused variable warnings $possibleValues = [System.TimeZoneInfo]::GetSystemTimeZones().ID | Where-Object { $_ -like "$wordToComplete*" } | Foreach-Object { if ($_ -like '* *') { "'{0}'" -f $_ } else { $_ } } $possibleValues | ForEach-Object { $_ } } )] [ValidateScript({ trap [System.TimeZoneNotFoundException] { $false } $null -ne [System.TimeZoneInfo]::FindSystemTimeZoneById($_) })] [string] $TimeZone = [System.TimeZoneInfo]::Local.Id, # Specifies the system input locale and the keyboard layout (default: current system language) [Parameter(ValueFromPipelineByPropertyName)] [ArgumentCompleter( { param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters # Suppress unused variable warnings $possibleValues = [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures).Name | Where-Object { $_ -like '*-*' } | Where-Object { $_ -like "$wordToComplete*" } $possibleValues | ForEach-Object { $_ } } )] [ValidateScript({ trap [System.Globalization.CultureNotFoundException] { $false } $null -ne [System.Globalization.CultureInfo]::GetCultureInfo($_) })] [Alias('keyboardLayout')] [String] $InputLocale = [System.Globalization.CultureInfo]::CurrentCulture.Name, # Specifies the language for non-Unicode programs (default: Current system language) [Parameter(ValueFromPipelineByPropertyName)] [ArgumentCompleter( { param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters # Suppress unused variable warnings $possibleValues = [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures).Name | Where-Object { $_ -like '*-*' } | Where-Object { $_ -like "$wordToComplete*" } $possibleValues | ForEach-Object { $_ } } )] [ValidateScript({ trap [System.Globalization.CultureNotFoundException] { $false } $null -ne [System.Globalization.CultureInfo]::GetCultureInfo($_) })] [String] $SystemLocale = [System.Globalization.CultureInfo]::CurrentCulture.Name, # Specifies the per-user settings used for formatting dates, times, currency and numbers (default: current system language) [Parameter(ValueFromPipelineByPropertyName)] [ArgumentCompleter( { param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters # Suppress unused variable warnings $possibleValues = [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures).Name | Where-Object { $_ -like '*-*' } | Where-Object { $_ -like "$wordToComplete*" } $possibleValues | ForEach-Object { $_ } } )] [ValidateScript({ trap [System.Globalization.CultureNotFoundException] { $false } $null -ne [System.Globalization.CultureInfo]::GetCultureInfo($_) })] [String] $UserLocale = [System.Globalization.CultureInfo]::CurrentCulture.Name, # Specifies the system default user interface (UI) language (default: current system language) [Parameter(ValueFromPipelineByPropertyName)] [ArgumentCompleter( { param ( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters ) $null = $commandName, $parameterName, $commandAst, $fakeBoundParameters # Suppress unused variable warnings $possibleValues = [System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures).Name | Where-Object { $_ -like '*-*' } | Where-Object { $_ -like "$wordToComplete*" } $possibleValues | ForEach-Object { $_ } } )] [ValidateScript({ trap [System.Globalization.CultureNotFoundException] { $false } $null -ne [System.Globalization.CultureInfo]::GetCultureInfo($_) })] [String] $UILanguage = [System.Globalization.CultureInfo]::CurrentCulture.Name, # Registered Owner (default: 'Valued Customer') [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [String] $RegisteredOwner, # Registered Organization (default: 'Valued Customer') [Parameter(ValueFromPipelineByPropertyName)] [ValidateNotNull()] [String] $RegisteredOrganization, # Array of hashtable with Description, Order, and Path keys, and optional Domain, Password(plain text), username keys. Executed by in the system context [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Advanced')] [Hashtable[]] $FirstBootExecuteCommand, # Array of hashtable with Description, Order and CommandLine keys. Executed at first logon of an Administrator, will auto elevate [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Advanced')] [Hashtable[]] $FirstLogonExecuteCommand, # Array of hashtable with Description, Order and CommandLine keys. Executed at every logon, does not elevate. [Parameter(ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Advanced')] [Hashtable[]] $EveryLogonExecuteCommand, # Enable Local Administrator account (default $true) this is needed for client OS if your not using autoLogon or adding additional admin users. [switch] $enableAdministrator ) Begin { $templateUnattendXml = [xml] @' <?xml version="1.0" encoding="utf-8"?> <unattend xmlns="urn:schemas-microsoft-com:unattend"> <settings pass="specialize"> <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"></component> <component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></component> <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"></component> <component name="Microsoft-Windows-Deployment" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></component> <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"></component> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></component> </settings> <settings pass="oobeSystem"> <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"> <InputLocale>en-US</InputLocale> <SystemLocale>en-US</SystemLocale> <UILanguage>en-US</UILanguage> <UserLocale>en-US</UserLocale> </component> <component name="Microsoft-Windows-International-Core" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <InputLocale>en-US</InputLocale> <SystemLocale>en-US</SystemLocale> <UILanguage>en-US</UILanguage> <UserLocale>en-US</UserLocale> </component> <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> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipUserOOBE>true</SkipUserOOBE> <SkipMachineOOBE>true</SkipMachineOOBE> </OOBE> <TimeZone>GMT Standard Time</TimeZone> <UserAccounts> <AdministratorPassword> <Value></Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> <RegisteredOrganization>Generic Organization</RegisteredOrganization> <RegisteredOwner>Generic Owner</RegisteredOwner> </component> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="x86" 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> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <NetworkLocation>Work</NetworkLocation> <ProtectYourPC>1</ProtectYourPC> <SkipUserOOBE>true</SkipUserOOBE> <SkipMachineOOBE>true</SkipMachineOOBE> </OOBE> <TimeZone>GMT Standard Time</TimeZone> <UserAccounts> <AdministratorPassword> <Value></Value> <PlainText>false</PlainText> </AdministratorPassword> </UserAccounts> <RegisteredOrganization>Generic Organization</RegisteredOrganization> <RegisteredOwner>Generic Owner</RegisteredOwner> </component> </settings> </unattend> '@ if ($LogonCount -gt 0) { Write-Warning -Message '-AutoLogon places the Administrator password in plain txt' } } Process { if ($psCmdlet.ShouldProcess("$path", 'Create new Unattended.xml')) { if ($FirstBootScriptPath) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding PowerShell script to First boot command" $FirstBootExecuteCommand = @(@{ Description = 'PowerShell First boot script' order = 1 path = "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"$FirstBootScriptPath`"" }) } if ($FirstLogonScriptPath) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding PowerShell script to First Logon command" $FirstLogonExecuteCommand = @(@{ Description = 'PowerShell First logon script' order = 1 CommandLine = "%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy Bypass -File `"$FirstBootScriptPath`"" }) } if ($enableAdministrator) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Enabling Administrator via First boot command" if ($FirstBootExecuteCommand) { $FirstBootExecuteCommand = $FirstBootExecuteCommand + @{ Description = 'Enable Administrator' order = 0 path = 'net user administrator /active:yes' } } else { $FirstBootExecuteCommand = @{ Description = 'Enable Administrator' order = 0 path = 'net user administrator /active:yes' } } } else { if ((-not ($UserAccount)) -or (-not($EnableAdministrator)) -or ( (-not ($domain)) -and (-not ($JoinAccount)) -and (-not ($OU)) ) ) { Write-Warning -Message "$Path only usable on a server SKU, for a client OS, use either -EnableAdministrator or -UserAccount, or (-Domain and -JoinAccount and -OU)" } } [xml] $unattendXml = $templateUnattendXml foreach ($setting in $unattendXml.Unattend.Settings) { foreach ($component in $setting.Component) { if ($setting.'Pass' -eq 'specialize' -and $component.'Name' -eq 'Microsoft-Windows-UnattendedJoin' ) { if (($JoinAccount) -or ($domain) -or ($OU)) { if (($JoinAccount) -and ($domain) -and ($OU)) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Unattend Domain Join for $($component.'processorArchitecture') Architecture" $identificationElement = $component.AppendChild($unattendXml.CreateElement('Identification', 'urn:schemas-microsoft-com:unattend')) $IdCredentialElement = $identificationElement.AppendChild($unattendXml.CreateElement('Credentials', 'urn:schemas-microsoft-com:unattend')) $IdCredDomainElement = $IdCredentialElement.AppendChild($unattendXml.CreateElement('Domain', 'urn:schemas-microsoft-com:unattend')) $Null = $IdCredDomainElement.AppendChild($unattendXml.CreateTextNode($JoinAccount.GetNetworkCredential().Domain)) $IdCredPasswordElement = $IdCredentialElement.AppendChild($unattendXml.CreateElement('Password', 'urn:schemas-microsoft-com:unattend')) $Null = $IdCredPasswordElement.AppendChild($unattendXml.CreateTextNode($JoinAccount.GetNetworkCredential().Password)) $IdCredUserNameElement = $IdCredentialElement.AppendChild($unattendXml.CreateElement('Username', 'urn:schemas-microsoft-com:unattend')) $Null = $IdCredUserNameElement.AppendChild($unattendXml.CreateTextNode($JoinAccount.GetNetworkCredential().UserName)) $IdJoinDomainElement = $identificationElement.AppendChild($unattendXml.CreateElement('JoinDomain', 'urn:schemas-microsoft-com:unattend')) $Null = $IdJoinDomainElement.AppendChild($unattendXml.CreateTextNode($domain)) $IdMachineOUElement = $identificationElement.AppendChild($unattendXml.CreateElement('MachineObjectOU', 'urn:schemas-microsoft-com:unattend')) $Null = $IdMachineOUElement.AppendChild($unattendXml.CreateTextNode($OU)) $IdUnsecureJoinElement = $identificationElement.AppendChild($unattendXml.CreateElement('UnsecureJoin', 'urn:schemas-microsoft-com:unattend')) $Null = $IdUnsecureJoinElement.AppendChild($unattendXml.CreateTextNode('False')) } else { Write-Warning 'Domain join requires -JoinAccount, -Domain, and -OU : one or more is missing, skipping section' } } } if ($setting.'Pass' -eq 'specialize' -and $component.'Name' -eq 'Microsoft-Windows-Deployment' ) { if (($null -ne $FirstBootExecuteCommand -or $FirstBootExecuteCommand.Length -gt 0) -and $component.'processorArchitecture' -eq 'x86') { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding first boot command(s)" $commandOrder = 1 $runSynchronousElement = $component.AppendChild($unattendXml.CreateElement('RunSynchronous', 'urn:schemas-microsoft-com:unattend')) foreach ($synchronousCommand in ($FirstBootExecuteCommand | Sort-Object -Property { $_.order })) { $syncCommandElement = $runSynchronousElement.AppendChild($unattendXml.CreateElement('RunSynchronousCommand', 'urn:schemas-microsoft-com:unattend')) $null = $syncCommandElement.SetAttribute('action', 'http://schemas.microsoft.com/WMIConfig/2002/State', 'add') $syncCommandDescriptionElement = $syncCommandElement.AppendChild($unattendXml.CreateElement('Description', 'urn:schemas-microsoft-com:unattend')) $Null = $syncCommandDescriptionElement.AppendChild($unattendXml.CreateTextNode($synchronousCommand['Description'])) $syncCommandOrderElement = $syncCommandElement.AppendChild($unattendXml.CreateElement('Order', 'urn:schemas-microsoft-com:unattend')) $Null = $syncCommandOrderElement.AppendChild($unattendXml.CreateTextNode($commandOrder)) $syncCommandPathElement = $syncCommandElement.AppendChild($unattendXml.CreateElement('Path', 'urn:schemas-microsoft-com:unattend')) $Null = $syncCommandPathElement.AppendChild($unattendXml.CreateTextNode($synchronousCommand['Path'])) $commandOrder++ } } } if (($setting.'Pass' -eq 'specialize') -and ($component.'Name' -eq 'Microsoft-Windows-Shell-Setup')) { if ($ComputerName) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding custom computername for $($component.'processorArchitecture') Architecture" $computerNameElement = $component.AppendChild($unattendXml.CreateElement('ComputerName', 'urn:schemas-microsoft-com:unattend')) $Null = $computerNameElement.AppendChild($unattendXml.CreateTextNode($ComputerName)) } if ($ProductKey) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Product key for $($component.'processorArchitecture') Architecture" $productKeyElement = $component.AppendChild($unattendXml.CreateElement('ProductKey', 'urn:schemas-microsoft-com:unattend')) $Null = $productKeyElement.AppendChild($unattendXml.CreateTextNode($ProductKey.ToUpper())) } } if (($setting.'Pass' -eq 'oobeSystem') -and ($component.'Name' -eq 'Microsoft-Windows-International-Core')) { if ($InputLocale) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Input Locale for $($component.'processorArchitecture') Architecture" $component.InputLocale = $InputLocale } if ($SystemLocale) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding System Locale for $($component.'processorArchitecture') Architecture" $component.SystemLocale = $SystemLocale } if ($UILanguage) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding UI Language for $($component.'processorArchitecture') Architecture" $component.UILanguage = $UILanguage } if ($UserLocale) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding User Locale for $($component.'processorArchitecture') Architecture" $component.UserLocale = $UserLocale } } if (($setting.'Pass' -eq 'oobeSystem') -and ($component.'Name' -eq 'Microsoft-Windows-Shell-Setup')) { if ($TimeZone) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Time Zone for $($component.'processorArchitecture') Architecture" $component.TimeZone = $TimeZone } Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Administrator Passwords for $($component.'processorArchitecture') Architecture" $concatenatedPassword = '{0}AdministratorPassword' -f $AdminCredential.GetNetworkCredential().password $component.UserAccounts.AdministratorPassword.Value = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($concatenatedPassword)) if ($RegisteredOrganization) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Registered Organization for $($component.'processorArchitecture') Architecture" $component.RegisteredOrganization = $RegisteredOrganization } if ($RegisteredOwner) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Registered Owner for $($component.'processorArchitecture') Architecture" $component.RegisteredOwner = $RegisteredOwner } if ($UserAccount) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding User Account(s) for $($component.'processorArchitecture') Architecture" $UserAccountsElement = $component.UserAccounts $LocalAccountsElement = $UserAccountsElement.AppendChild($unattendXml.CreateElement('LocalAccounts', 'urn:schemas-microsoft-com:unattend')) foreach ($Account in $UserAccount) { $LocalAccountElement = $LocalAccountsElement.AppendChild($unattendXml.CreateElement('LocalAccount', 'urn:schemas-microsoft-com:unattend')) $LocalAccountPasswordElement = $LocalAccountElement.AppendChild($unattendXml.CreateElement('Password', 'urn:schemas-microsoft-com:unattend')) $LocalAccountPasswordValueElement = $LocalAccountPasswordElement.AppendChild($unattendXml.CreateElement('Value', 'urn:schemas-microsoft-com:unattend')) $LocalAccountPasswordPlainTextElement = $LocalAccountPasswordElement.AppendChild($unattendXml.CreateElement('PlainText', 'urn:schemas-microsoft-com:unattend')) $LocalAccountDisplayNameElement = $LocalAccountElement.AppendChild($unattendXml.CreateElement('DisplayName', 'urn:schemas-microsoft-com:unattend')) $LocalAccountGroupElement = $LocalAccountElement.AppendChild($unattendXml.CreateElement('Group', 'urn:schemas-microsoft-com:unattend')) $LocalAccountNameElement = $LocalAccountElement.AppendChild($unattendXml.CreateElement('Name', 'urn:schemas-microsoft-com:unattend')) $null = $LocalAccountElement.SetAttribute('action', 'http://schemas.microsoft.com/WMIConfig/2002/State', 'add') $concatenatedPassword = '{0}Password' -f $Account.GetNetworkCredential().password $null = $LocalAccountPasswordValueElement.AppendChild($unattendXml.CreateTextNode([System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($concatenatedPassword)))) $null = $LocalAccountPasswordPlainTextElement.AppendChild($unattendXml.CreateTextNode('false')) $null = $LocalAccountDisplayNameElement.AppendChild($unattendXml.CreateTextNode($Account.UserName)) $null = $LocalAccountGroupElement.AppendChild($unattendXml.CreateTextNode('Administrators')) $null = $LocalAccountNameElement.AppendChild($unattendXml.CreateTextNode($Account.UserName)) } } if ($LogonCount) { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding AutoLogon for $($component.'processorArchitecture') Architecture" $autoLogonElement = $component.AppendChild($unattendXml.CreateElement('AutoLogon', 'urn:schemas-microsoft-com:unattend')) $autoLogonPasswordElement = $autoLogonElement.AppendChild($unattendXml.CreateElement('Password', 'urn:schemas-microsoft-com:unattend')) $autoLogonPasswordValueElement = $autoLogonPasswordElement.AppendChild($unattendXml.CreateElement('Value', 'urn:schemas-microsoft-com:unattend')) $autoLogonCountElement = $autoLogonElement.AppendChild($unattendXml.CreateElement('LogonCount', 'urn:schemas-microsoft-com:unattend')) $autoLogonUsernameElement = $autoLogonElement.AppendChild($unattendXml.CreateElement('Username', 'urn:schemas-microsoft-com:unattend')) $autoLogonEnabledElement = $autoLogonElement.AppendChild($unattendXml.CreateElement('Enabled', 'urn:schemas-microsoft-com:unattend')) $null = $autoLogonPasswordValueElement.AppendChild($unattendXml.CreateTextNode($AdminCredential.GetNetworkCredential().password)) $null = $autoLogonCountElement.AppendChild($unattendXml.CreateTextNode($LogonCount)) $null = $autoLogonUsernameElement.AppendChild($unattendXml.CreateTextNode('administrator')) $null = $autoLogonEnabledElement.AppendChild($unattendXml.CreateTextNode('true')) } if (($Null -ne $FirstLogonExecuteCommand -or $FirstBootExecuteCommand.Length -gt 0) -and $component.'processorArchitecture' -eq 'x86') { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding First Logon Commands" $commandOrder = 1 $FirstLogonCommandsElement = $component.AppendChild($unattendXml.CreateElement('FirstLogonCommands', 'urn:schemas-microsoft-com:unattend')) foreach ($command in ($FirstLogonExecuteCommand | Sort-Object -Property { $_.order })) { $CommandElement = $FirstLogonCommandsElement.AppendChild($unattendXml.CreateElement('SynchronousCommand', 'urn:schemas-microsoft-com:unattend')) $CommandDescriptionElement = $CommandElement.AppendChild($unattendXml.CreateElement('Description', 'urn:schemas-microsoft-com:unattend')) $CommandOrderElement = $CommandElement.AppendChild($unattendXml.CreateElement('Order', 'urn:schemas-microsoft-com:unattend')) $CommandCommandLineElement = $CommandElement.AppendChild($unattendXml.CreateElement('CommandLine', 'urn:schemas-microsoft-com:unattend')) $CommandRequireInputElement = $CommandElement.AppendChild($unattendXml.CreateElement('RequiresUserInput', 'urn:schemas-microsoft-com:unattend')) $null = $CommandElement.SetAttribute('action', 'http://schemas.microsoft.com/WMIConfig/2002/State', 'add') $null = $CommandDescriptionElement.AppendChild($unattendXml.CreateTextNode($command['Description'])) $null = $CommandOrderElement.AppendChild($unattendXml.CreateTextNode($commandOrder)) $null = $CommandCommandLineElement.AppendChild($unattendXml.CreateTextNode($command['CommandLine'])) $null = $CommandRequireInputElement.AppendChild($unattendXml.CreateTextNode('false')) $commandOrder++ } } if (($null -ne $EveryLogonExecuteCommand -or $FirstBootExecuteCommand.Length -gt 0) -and $component.'processorArchitecture' -eq 'x86') { Write-Verbose -Message "[$($MyInvocation.MyCommand)] Adding Every-Logon Commands" $commandOrder = 1 $FirstLogonCommandsElement = $component.AppendChild($unattendXml.CreateElement('LogonCommands', 'urn:schemas-microsoft-com:unattend')) foreach ($command in ($EveryLogonExecuteCommand | Sort-Object -Property { $_.order })) { $CommandElement = $FirstLogonCommandsElement.AppendChild($unattendXml.CreateElement('AsynchronousCommand', 'urn:schemas-microsoft-com:unattend')) $CommandDescriptionElement = $CommandElement.AppendChild($unattendXml.CreateElement('Description', 'urn:schemas-microsoft-com:unattend')) $CommandOrderElement = $CommandElement.AppendChild($unattendXml.CreateElement('Order', 'urn:schemas-microsoft-com:unattend')) $CommandCommandLineElement = $CommandElement.AppendChild($unattendXml.CreateElement('CommandLine', 'urn:schemas-microsoft-com:unattend')) $CommandRequireInputElement = $CommandElement.AppendChild($unattendXml.CreateElement('RequiresUserInput', 'urn:schemas-microsoft-com:unattend')) $null = $CommandElement.SetAttribute('action', 'http://schemas.microsoft.com/WMIConfig/2002/State', 'add') $null = $CommandDescriptionElement.AppendChild($unattendXml.CreateTextNode($command['Description'])) $null = $CommandOrderElement.AppendChild($unattendXml.CreateTextNode($commandOrder)) $null = $CommandCommandLineElement.AppendChild($unattendXml.CreateTextNode($command['CommandLine'])) $null = $CommandRequireInputElement.AppendChild($unattendXml.CreateTextNode('false')) $commandOrder++ } } } } #end foreach setting.Component } #end foreach unattendXml.Unattend.Settings Write-Verbose -Message "[$($MyInvocation.MyCommand)] Saving file" $unattendXml.Save($Path) Get-ChildItem $Path # } # catch # { # throw $_.Exception.Message # } } } } function Get-UnattendChunk { param ( [string] $pass, [string] $component, [string] $arch, [xml] $unattend ) # Helper function that returns one component chunk from the Unattend XML data structure return $unattend.unattend.settings | Where-Object -Property pass -EQ -Value $pass | Select-Object -ExpandProperty component | Where-Object -Property name -EQ -Value $component | Where-Object -Property processorArchitecture -EQ -Value $arch } |