This HOWTO covers how use the free 180 trial version of Windows Server 2012 R2 to build a reusable template for lab and development purposes.
The objective of the steps below are as follows:
- Ensure that whenever we need to deploy a new Windows Server for testing, it will always have the latest updates and customizations
- Each deployment will be sysprepped ensuring there are no SID conflicts when using multiple machines
- Allow for the latest updates and new customizations to be be added in the future without impacting already deployed test machines
- New Server 2012 R2 VMs including fresh domain controllers and domain joined member servers must be spun up as quickly as possible with as little user intervention as possible
- The process must allow for an unlimited number of Syspreps to take place
- This entire guide must be repeated every 6 months as we are using the free trial version of Windows Server 2012 R2
- Any machines deployed during the 6 month trial period will reset and start from their own 180 day counter independent of the source
- The implement will leverage the linked clone snapshot feature found in VMware Workstation
Overview
The idea behind this build is to provide the fastest possible way to build new Windows 2012 R2 Active Directory lab environments.
I’m aware there are other solutions available but I wanted to see what I could accomplish using nothing more than the 180 day trial ISO, VMware Workstation and PowerShell.
The configuration may seem a little complex but once these steps are in place, it becomes hilariously easy and fast to deploy new test servers at home for testing.
The idea is to build out a new VM using the trial, snapshot it, sysprep it and then use PowerShell scripts to automatically build new Domain controllers and join to the domain using Desired State Configuration.
New Server Template Build
- Download the Windows Server 2012 R2 180 day trial ISO from Microsoft
- Create a new thin-provisioned VM called TMPL2012R2 and install Windows from the ISO using this template
- Once Windows is installed, it should automatically activate giving you 180 days or 6 months of usage
- Proceed to install VMware Tools and all Windows Updates and make any other configuration customizations you’d like. These can include:
* Copying over common software tools (the Sysinternals tools are highly recommended for example)
* Enable RDP
* Disable IE Protected Mode - Install the Windows Management Framework 5 Production Preview (the latest available as of this writing) which provides PowerShell 5
- From the PowerShell prompt type Set-ExecutionPolicy Unrestricted
- From the PowerShell prompt, run the following commands:
* Install-Module xActiveDirectory, xComputerManagement, xNetworking
Prepare for Deployment
- Now that your server is built exactly the way you want, copy and paste the text below into a file called unattend.xml and save it to c:\windows\system32\sysprep
- Be sure to update the highlighted text in the file to include the default administrator password you want to use. This is intended for lab environments only so the password is stored in plain text
<?xml version=”1.0″ encoding=”utf-8″?>
< unattend xmlns=”urn:schemas-microsoft-com:unattend”>
< 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>
< UILanguageFallback>en-US</UILanguageFallback>
< 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>
< NetworkLocation>Work</NetworkLocation>
< ProtectYourPC>3</ProtectYourPC>
</OOBE>
< UserAccounts>
<AdministratorPassword>
<Value>[insertpasswordhere]</Value>
</AdministratorPassword>
</UserAccounts>
< TimeZone>Mountain Standard Time</TimeZone>
</component>
< /settings>
< 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”>
< CopyProfile>true</CopyProfile>
</component>
< /settings>
< /unattend>
- Save the following 3 files to the desktop of the template machine
RunSysPrep.bat
echo If you don’t want to sysprep and shutdown now, close this window!
pause
@echo off
cd c:\windows\system32\sysprep
sysprep /oobe /generalize /unattend:c:\windows\system32\sysprep\unattend.xml
BuildNewDC.ps1
# Script uses DSC (Desired State Configuration to rename PC, assign network details, promote as DC and make new domain admin user
# Home; DSC; Active Directory; Domain Controller; Networking
<#
REQUIREMENTS:
Windows PowerShell 5 via Windows Management Framework 5 Production Preview: https://www.microsoft.com/en-us/download/details.aspx?id=48729
DSC Resources: (Install-Module [modulename])
xActiveDirectory
xComputer Management
xNetworking
#>
# The Local Configuration Manager (LCM) must be configured in advance to know to reboot as soon as one is required
# This cannot be set directly but rather the setting must be saved into a MOF (Management Object Format) plain text configuration file
# This file must then be read by the Set-DSCLocalConfigurationManager Command
configuration EnableRestarts
{
LocalConfigurationManager
{
RebootNodeIfNeeded = $true
}
}
# We use the temp file here since we don’t actually need to keep this configuration file after execution
EnableRestarts -OutputPath $Env:Temp\ | out-null
Set-DscLocalConfigurationManager -Path $Env:Temp | out-null
Remove-Item C:\Users\ADMINI~1\AppData\Local\Temp\1\localhost.meta.mof
# Request input that is required from the user
do { $ComputerName = Read-Host ‘Enter new Computer Name’ } while($ComputerName -eq “”)
do { $DomainName = Read-Host ‘Enter name of new domain/forest’ } while($DomainName -eq “”)
do { $DCIP = Read-Host ‘Enter the IP address of the new domain controller’ } while($DCIP -eq “”)
do { $DefaultGateway = Read-Host ‘Enter the Default Gateway to use’ } while($DefaultGateway -eq “”)
# Configure all of the settings we want to apply for this configuration
$ConfigData = @{
AllNodes = @(
@{
NodeName = ‘localhost’
MachineName = $ComputerName
DomainName = $DomainName
LabPassword = ‘[insertpasswordhere]‘
ADAdminUser = ‘[insertusernamehere]‘
IPAddress = $DCIP
InterfaceAlias = ‘Ethernet0′
DefaultGateway = $DefaultGateway
SubnetMask = ’24’
AddressFamily = ‘IPv4’
DNSAddress = $DCIP, ‘8.8.8.8’
PSDscAllowPlainTextPassword = $true
}
)
}
Configuration BuildTest01 {
Import-DscResource -Module xActiveDirectory, xComputerManagement, xNetworking
Node $AllNodes.NodeName
{
LocalConfigurationManager
{
ActionAfterReboot = ‘ContinueConfiguration’
ConfigurationMode = ‘ApplyOnly’
RebootNodeIfNeeded = $true
}
# All of the resources that require a password expect a PSCredential object — even those that only want a password
# For lab purposes we will use the same password everywhere
$password = ConvertTo-SecureString $Node.LabPassword -AsPlainText -Force
$username = “$DomainName\administrator”
$LabCred = New-Object System.Management.Automation.PSCredential($username,$password)
# With DSC, this is literally all you need to do to change the name of a computer. Because the LCM is set to reboot when needed above, that’s even taken care of
xComputer SetName {
Name = $Node.MachineName
}
# This is all that is needed to configure network details
xIPAddress SetIP {
IPAddress = $Node.IPAddress
InterfaceAlias = $Node.InterfaceAlias
DefaultGateway = $Node.DefaultGateway
SubnetMask = $Node.SubnetMask
AddressFamily = $Node.AddressFamily
}xDNSServerAddress SetDNS {
Address = $Node.DNSAddress
InterfaceAlias = $Node.InterfaceAlias
AddressFamily = $Node.AddressFamily
}
# This requires that Active Directory Domain Services role be present on the machine. If it isn’t, go install it. (All that logic is taken care of behind the scenes)
WindowsFeature ADDSInstall {
Ensure = ‘Present’
Name = ‘AD-Domain-Services’
}
# Make sure the Active Directory Management tools are installed
WindowsFeature ADDSTools
{
Ensure = “Present”
Name = “RSAT-ADDS”
}
# Build a domain controller. This is all that is required. The rest is taken care of automatically!
xADDomain FirstDC {
DomainName = $Node.DomainName
DomainAdministratorCredential = $LabCred
SafemodeAdministratorPassword = $LabCred
DependsOn = ‘[xComputer]SetName’, ‘[xIPAddress]SetIP’, ‘[WindowsFeature]ADDSInstall’
}# Assign a custom admin user so we don’t use the default ‘administrator’ account
xADUser FirstUser
{
DomainAdministratorCredential = $LabCred
DomainName = $Node.DomainName
UserName = $Node.ADAdminUser
Password = $LabCred
Ensure = ‘Present’
}
# There is no built in resource I could find to change user group membership in AD so we use the “script” resource to run a traditional command
Script NewADAdminUser
{
SetScript = { Add-ADGroupMember -Identity “Domain Admins” -Members $Using:Node.ADAdminUser }
TestScript = { $false }
GetScript = { }
}}
}
# We now need to build a MOF (Managed Object File) based on the configuration defined above and based on the custom configuration parameters we defined
# This will place the MOF in a folder called “BuildTest01” under the current operating folder
BuildTest01 -ConfigurationData $ConfigData
# We now enforce the configuration using the command syntax below
Start-DscConfiguration -Wait -Force -Path .\BuildTest01 –Verbose
AddComputerToDomain.ps1
# Asks the user for a computer name and IP address and sets those on the current machine, and optionally joins to the domain
# VM; New Machine; IP Address; Computername; Hostname; Domain Join; Home; VMware Workstation; Template;
# Force execution of code to run as administrator (requires prompting the user to run as administrator)
param([switch]$Elevated)
function Test-Admin {
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated)
{
# tried to elevate, did not work, aborting
}
else {
Start-Process powershell.exe -Verb RunAs -ArgumentList (‘-noprofile -noexit -file “{0}” -elevated’ -f ($myinvocation.MyCommand.Definition))
}
exit
}
do { $ComputerName = Read-Host ‘Enter new Computer Name’ } while ($ComputerName -eq “”)
do { $IPAddress = Read-Host ‘Enter IP address to assign’ } while ($IPAddress -eq “”)
do { $DNSServer = Read-Host ‘Enter DNS Server IP to use’ } while ($DNSServer -eq “”)
do { $DomainName = Read-Host ‘Enter domain name to connect to’ } while ($DomainName -eq “”)
do { $DefaultGateway = Read-Host ‘Enter the default gateway to use’ } while ($DefaultGateway -eq “”)
$password = “[insertpasswordhere]” | ConvertTo-SecureString -asPlainText -Force
$username = “$DomainName\[insertusernamehere]“
#if($ComputerName -ne “”) { Rename-Computer $ComputerName -verbose}
if($IPAddress -ne “”)
{
# Cannot edit existing IP configuration with Powershell — must remove and then add new
# This section removes the existing IP configuration on the default network interface
Remove-NetIPAddress -InterfaceAlias Ethernet0 -confirm:$false -ErrorAction SilentlyContinue
remove-netroute -interfaceAlias Ethernet0 -confirm:$false -ErrorAction SilentlyContinue
#Add New IP information
New-NetIPAddress -InterfaceAlias Ethernet0 -IPAddress $IPAddress -PrefixLength “24” -DefaultGateway $DefaultGateway
Set-DnsClientServerAddress -InterfaceAlias Ethernet0 -ServerAddresses $DNSServer
}
Write-Host “Waiting 5 seconds for network configuration to update…”
sleep 5
do { $DomainJoin = Read-Host “Join the $DomainName domain? (y/n)” } until (“y”,”n” -ccontains $DomainJoin)
if($DomainJoin -eq “y”)
{
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
Add-Computer -DomainName $DomainName -NewName $ComputerName -Credential $credential
}
$RebootNow = Read-Host “Reboot computer? (y/n)”
if($RebootNow -eq “y”) { Restart-Computer }
Completing the Setup
- At this point you should have a fully patched and customized Windows 2012 R2 VM with three files (one batch file and two PowerShell scripts) on your desktop
- Power off the VM
- From VMware Workstation, make a snapshot of the VM. Call the Snapshot B1 (Short for Base Snapshot 1)
- Power up the VM and login. Double click on RunSysprep.bat. The system will power off automatically when complete
- From VMware Workstation, snapshot the VM again, this time calling the snapshot L1 (Short for Linked Clone Base 1)
- Right click on the VM, choose Settings / Options / Advanced and check the box “Enable Template Mode (to be used for cloning)“
Deploying a New VM
- Right click on on TMPL2012R2 and choose Clone
- Create a linked clone
- Power on the VM. It should automatically sysprep and immediately wait at the login screen. The local administrator password should be what you set in the unattend.xml file
- Log into the VM and run the BuildNewDC.ps1 script. It will ask you for the computer name, forest name to assign and networking details
- Wait 5-10 minutes depending on the speed of your computer. You should now be able to login as the custom user you assigned which is already made a Domain Admin with an Active Directory domain fully deployed
- Repeat the steps above to build a member server only this time running the AddComputerToDomain.ps1 script
Updating the Template
- Next month when you want to apply new Windows updates, right click on the TMPL2012R2 VM and choose Snapshot Manager. Select your base snapshot B1 and choose Goto Snapshot
Note: This will not impact your existing VMs you have deployed - Remember this snapshot was taken prior to running sysprep. Apply your Windows updates or any other changes you would like to make and power off the VM
- Snapshot the VM and call the new snapshot B2 or Base 2
- Power on the VM and run the RunSysprep.bat icon on the desktop. The machine will power off automatically
- Snapshot the VM again this time calling it L2 (or Linked 2)
- When you need a new VM, create a linked clone base off the new latest snapshot
Rinse and repeat as many times as necessary. Note that additional snapshots will have a performance impact but for lab purposes, the impact should be practically undetectable.
With these steps deployed, I can build, from scratch a fully patched Windows 2012 R2 domain and 2 member servers in under 10 minutes using less than 10 mouse clicks per server.
This process has drastically improved my IT studying experience as it is now trivially easy to bring up and tear down new servers as required.
2 comments
Would there be a way for you to publish the scripts somewhere without formatting, or attach them to be downloaded? I tried to copy/paste the BuildNewDC.ps1 into my ISE and it’s not happy.
I like the idea that you have going here and would like to attempt it in my homelab as well.
Thank you!
Hi Nick,
Well that’s odd. It looks like a new WordPress template messed up the formatting on that post (and probably others 🙁 It’s not supposed to look like that obviously. I’ll have to look into that.
In the mean time here is a link to the script that I use.
http://pleasework.robbievance.net/PowerShellScripts/buildnewdc.txt
At least I think that’s the one I use. I use Subversion as my software repo for my PowerShell scripts and this is my most recent version. But admittedly I haven’t updated the template I actually use since it “just works” so as with any code you download off the Internet, pay attention to what your running. The good news is it sounds like you’re doing this specifically in a lab environment so that gives you some leeway.
If you manage to get DSC working for you, I’d love to hear about it!