Summary: Use an Unattend.xml file and edit it directly with Windows PowerShell.
Hey, Scripting Guy!
I saw yesterday that by using a free script called Convert-WindowsImage.ps1, I could convert Windows from a WIM file and make a bootable VHD. Is there any way to get past the initial startup screens or maybe even define the computer name?
—KR
Hello KR,
Honorary Scripting Guy, Sean Kearney, here to continue making your life smoother and quicker with Windows PowerShell.
Note This is the second part of a five-part series. To catch up, read Set Up a Lab with Windows PowerShell and Free Microsoft Software: Part 1.
Yesterday, we showed you a really cool script made by a Microsoft partner called Convert-WindowsImage.ps1 to build a bootable VHD file from a WIM file.
If you were curious and jumped ahead by adding this VHD to a Hyper-V virtual machine, you saw, “Whoa! Cool! It’s working!” Except of course, you had to set up the keyboard, accept the license terms, and all of that fun stuff.
But you can bypass all of this with one magic file called Unattend.xml, which is usually about as fun to play with as a 4x4x4 Rubik’s cube with all the stickers switched around.
So I did some digging and playing about, and I found a nicely stripped-down file that we can work with. To make this useful, we’re going to build the following process:
- Copy one of the VHD files that we created to a new location (the default Hyper-V disk location)
- Mount the newly copied VHD file
- Edit the XML file directly with Windows PowerShell
- Save this new file and copy it directly to the mounted VHD file under “\Windows\system32\sysprep”
- Dismount the VHD file
We shall begin by leveraging the powers of an Unattend.xml file. This is a special file that is used in newer versions of Windows (starting with Windows Vista). When placed under the \Windows\System32\Sysprep folder, it gives the operating system commands for how certain things should be set up during the first-time boot and configuration.
For example, we might want to automatically predefine the Time Zone or perhaps the Administrator account—maybe even name of the machine. These are just small pieces of what Unattend.xml does.
I’m going to tell you right now: Use the Microsoft Deployment Toolkit to make your life easier. It builds Unattend.xml in addition to creating a whole pile more automation. But for my simple lab, my needs are very tight. All I want to do is:
- Name the server
- Bypass the end-user license agreement
- Set the international settings
- Assign the Administrator password
If you’re feeling really curious, here is a copy of the Master-Unattend-Template.xml file that I put together:
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<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>
<ProductKey>***ProductKey***</ProductKey>
<RegisteredOrganization>***RegOrg***</RegisteredOrganization>
<RegisteredOwner>***RegOwner***</RegisteredOwner>
<TimeZone>***TimeZone***</TimeZone>
</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">
<UserAccounts>
<AdministratorPassword>
<Value>***Password***</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<AutoLogon>
<Password>
<Value>***Password***</Value>
<PlainText>true</PlainText>
</Password>
<Username>administrator</Username>
<LogonCount>2</LogonCount>
<Enabled>true</Enabled>
</AutoLogon>
<RegisteredOrganization>***RegOrg***</RegisteredOrganization>
<RegisteredOwner>***RegOwner***</RegisteredOwner>
<OOBE>
<HideEULAPage>true</HideEULAPage>
<SkipMachineOOBE>true</SkipMachineOOBE>
</OOBE>
</component>
</settings>
<cpi:offlineImage cpi:source="" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
You’ll see several spots where I have substituted ***value*** instead of actual data. This is done for a few reasons:
- If I look at this after, I can see where the values are supposed to be.
- If you try to look at it with the Windows System Image Manager, and you have NULL values, it’s not very happy.
- Honestly, XML files hurt my eyes.
Don’t hurt your eyes by trying to memorize this. At the end of the series, this will all be available as a single download in the Script Center Repository. But I thought it would be fun to show a basic Unattend.xml file.
Let’s have some fun. We’re going to edit this with Windows PowerShell. Believe it or not, it’s very easy.
First we define the parameters that we’re going to pass to the XML file:
Param(
$VMName,
$Organization='Contoso Rocks',
$Owner='Contoso Rocks',
$TimeZone='Eastern Standard Time',
$AdminPassword='P@ssw0rd'
)
Next we create an XML object:
$UnattendFile=NEW-OBJECT XML
Then we load the XML file into the object:
$UnattendFile=Load(“C:\Unattend-Template.XML”)
After the file is an object in memory, we can access the various attributes directly and replace them with what we want. In our case, I would like to set up the Default Organization and Owner name, Machine Name, Time Zone, and Administrator Password.
I do this by accessing the attributes directly within XML and setting their values:
$Unattendfile.unattend.settings.component[0].ComputerName=$VMName
$Unattendfile.unattend.settings.component[0].RegisteredOrganization=$Organization
$Unattendfile.unattend.settings.component[0].RegisteredOwner=$Owner
$Unattendfile.unattend.settings.component[0].TimeZone=$TimeZone
$Unattendfile.unattend.settings.Component[1].RegisteredOrganization=$Organization
$Unattendfile.unattend.settings.Component[1].RegisteredOwner=$Owner
$UnattendFile.unattend.settings.component[1].UserAccounts.AdministratorPassword.Value=$AdminPassword
$UnattendFile.unattend.settings.component[1].autologon.password.value=$AdminPassword
Then we save the file. But because I might have multiple machines that I am going to deploy, I am going to name each .xml file by the virtual machine’s name:
$UnattendFile.Save(“C:\ISO\$VMName.xml”)
Normally, I would also supply the product key, but in this scenario the product is already baked into our trial software, and it would actually (as a former coworker, Kevin, loved to say) “hack up a hairball” if we tried to use it. So I’ll make my old friend Kevin happy and not blow up my virtual machine on first use.
Tomorrow we’re going to have some real fun. We’ll take this file, inject it into a VHD file, and set up the basic virtual machine—all without touching the GUI! This and more Windows PowerShell goodness in tomorrow’s spine tingling episode of the Hey! Scripting Guy Blog!
I invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then remember eat your Cmdlets each and every day with a taste dash of Creativity.
Sean Kearney, Windows PowerShell MVP and Honorary Scripting Guy