Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / PowerShell

SCRIPT Use PowerShell to Provision a New Virtual Machine (Hyper V)

0.00/5 (No votes)
8 Dec 2016Ms-PL4 min read 11.8K  
SCRIPT Use PowerShell to Provision a New Virtual Machine (Hyper V)

When you want to perform repetitive tasks, automation is the way to go. For Windows Server technologies related automation, you need to look no further than PowerShell. In this article, we will write a full fledged PowerShell script to automate the provisioning of a new VM using a pre-created hard disk.

When I was doing some experimenting with VMs in a Hyper V environment, creating my own “Data Center in a Box” and so forth, I needed to create over 20 VMs repetitively with almost the same settings. To make my task easy, this is the script I wrote for myself. Note that this is by no means an “industrial strength” script and has next to no error handling or recovery. Yes, there are definitely better written and more robust scripts out there.

The Scenario

I have a pre-created base system, with the operating system just installed. I have logged on once into this base system and performed some basic settings: I have installed a network adapter, setup the time zone, selected not to participate in the Customer Program, enabled Remote Desktop, activated the machine, and brought the system up to date with respect to patches via online Windows Update. I have not set the computer name or joined it to any workgroup or domain (those are still at default settings).

Now, I need to duplicate this system and create a new system out of it. After bringing up this new system, I need to set up its add my internal DNS servers to its network adapter, set up a new computer name and join it to an Active Directory domain. I do this in two parts. The first part starting immediately below, I run from my host machine. During my above prep-work, I also placed a PowerShell script on the Administrator user’s desktop of my base system to perform the second part — doing so ensures the script is available by default in every differenced system I create.

Scripting…

For all VHD based commands, assume that I am in the path that has my VHD file(s). I am going to cheat a bit and use a differencing disk to create my new VM off the existing disk. For this, I will need to issue the New-HVD command as follows (this command creates a differencing disk named “system01.vhd” of 80GB maximum size):

PS:\> New-VHD -ParentPath "BaseSystemVHD.vhd"  -Path "System01.vhd" -SizeBytes 80GB -Differencing

Next, I create the new VM. I am using Windows Server 2012 R2 as my host operating system, which gives me the new “Generation 2” virtual machine ability. This has some extra features I plan to use later, so I issue this command to create my VM:

PS:\> New-VM -Generation 2 -Name "System01" -MemoryStartupBytes 4096MB -VHDPath "System01.vhd" 
-BootDevice VHD -SwitchName "InternetAccessSwitch"
PS:\> Set-VM -Name "System01" -DynamicMemory

The above command provisions my new VM (does not start it up just yet) with the name “System01”, initial boot time memory of 4GB. It also sets the boot up device to the VHD (as against the CDROM, etc.) and sets it to use a switch called “InternetAccessSwitch” (this is defined in my Virtual Switch Manager as a switch that provides access to the Internet connection on that system). The machine is set up to use Dynamic memory using the second (Set-VM) command. Now, time to start it up…

PS:\> Start-VM -Name "System01"

There is no actual way to determine if the VM booted up properly. You can pass an -AsJob parameter to the Start-VM command and then do a “Wait-Job -Job [JobHandle returned by Start-VM]” to wait, but this will only tell you that the VM was started by Hyper V, not that it was booted up and ready for use. I have not yet figured out a good way to do this. So I have a pause command in my script, where I go and watch the VM booting up. Once it has booted to the logon screen, I go back and continue the script.

The first thing I want to know is the name that the system has assigned itself. I can do this easily from PowerShell. What I need to do is, find the Network Adapter on the new VM, determine its IP address and reverse look it up for the hostname. You will need to change the RegEx in the below command if you are using IPv6 (I am using IPv4):

PS:\> $NewVMIPAddress = Get-VMNetworkAdapter "System01" | where switchname 
-eq "InternetAccessSwitch" | select-expandproperty "IPAddresses" | 
where {$_-match"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$"}
PS:\> [System.Net.Dns]::GetHostEntry($NewVMIPAddress).HostName

Now I log on to this system from MSTSC with the determined hostname. I now use the PowerShell script I had copied earlier to continue and setup the final part of the configuration:

PS:\> $WMI = Get-WmiObject win32_networkadapterconfiguration -Filter "ipenabled = 'true'"
PS:\> $DNSServers = "192.168.1.1", "208.67.222.222", "208.67.220.220"
PS:\> $WMI.SetDNSServerSearchOrder()
PS:\> $WMI.SetDNSServerSearchOrder($DNSServers)

In the above snippet, I fetch the WMI reference to an enabled network adapter. Then I clear the existing DNS server setup on the network adapter (what you would normally setup on the DNS tab of the TCP/IP properties) and then set it up with my custom DNS servers. Note that the first one is meant to mean my own internal DNS server (bound to my Active Directory) and the other two are OpenDNS DNS servers (http://www.opendns.com/business-solutions/premium-dns/benefits).

Now, we set up the new computer name and join it to the Active Directory domain. I give the new name “MyNewDifferencedVM” and join it to a fictitious domain called “foo.bar” in the example below. Replace them with what you need. Note that I don’t provide a password, the system will prompt me automatically when it runs the Add-Computer command.

PS:\> Rename-Computer -NewName "MyNewDifferencedVM"
PS:\> Add-Computer -DomainName "foo.bar -Credential "FOO\Administrator" 

Now of course, we need to restart the VM…

PS:\> Restart-Computer -Force

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)