Automatically Create Hyper-V Virtual Machines from VHD

Automatically Create Hyper-V Virtual Machines from VHD

  • Comments 1
  • Likes

Summary: Microsoft Scripting Guy, Ed Wilson, shows how to automatically create Hyper-V virtual machines from VHD by using Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. As the Scripting Wife and I get ready for our European Scripting Tour, I need to make sure my Windows 8 laptop is in top-working order. Good thing I checked. I have a really small disk drive on my laptop, but luckily, the Scripting Wife and friends stopped at a really cool computer store on the way back from the Windows PowerShell Saturday event in Atlanta and brought me a nice USB 3.0 key fob. USB 3.0 is decent enough to make for expanded storage without much of a performance impact on my laptop. Sweet.

I exported all of my virtual machines to the USB 3.0 disk to permit room on my laptop. There was just one problem—I formatted the key with EXFAT (not out of any personal identification that I know of), and I exported all of the virtual machines to the key fob. When I checked today, dude, nothing worked. I got a bogus file system unable to do something type of error, and none of the virtual machines worked.

A little checking with BING reveals that Hyper-V does not support EXFAT file systems. Bummer. So, I copied the 50-gig worth of virtual machines back to my laptop, formatted the USB fob with NTFS, copied the virtual machines back to the fob, and then nothing. Bummer. So, I decided to simply copy the VHDs to their own folder and use Windows PowerShell to recreate all of the machines.

Using the Hyper-V module to create virtual machines

The first thing I do is import the Hyper-V module, and I create three variables. The first is the path to the VHD files; the second is the path to where I wish to store the completed virtual machines; and lastly, I have a variable that holds the internal virtual switch.

The first two variables are straightforward string assignments, but the last variable, the $InternalSwitch variable, uses the Get-VMSwitch cmdlet to find the internal virtual switch. I only have one internal switch, so this works well for me. Here is the first portion of the code.

Import-Module hyper-V

$VHDPath = "E:\vhd"

$VMPath = "E:\VM"

$InternalSwitch = Get-VMSwitch -SwitchType Internal

Now, I need to use the Get-ChildItem cmdlet to retrieve all of the VHDs in the VHD folder. Because I name my VHDs with the same name as the virtual machine themselves, this technique works really well for me. For example, the DC1 virtual machine has a VHD named DC1.VHDX. This is not mandatory, but it simplifies my script.

After enumerating all of the VHDX files, I use the Foreach-Object cmdlet to create a new virtual machine for each of the disks. I use the BaseName property, for example, DC1, without the VHDX file extension or the path to the folder) as the virtual machine name. I then specify the memory in MB (this is required), the switch name, the path to store the virtual machine, and the path to the VHD file itself. Here is the complete command.

Get-ChildItem $VHDPath |

ForEach-Object {

  new-vm -Name $_.BaseName -MemoryStartupBytes 512MB `

  -SwitchName $InternalSwitch.name -VHDPath $_.FullName -Path $VMPath}

From here I want to enable dynamic memory for each of the virtual machines. To do this, I use the Get-VM cmdlet to collect all of the virtual machines on the system, and I pipe it to the Set-VMMemory cmdlet. I need to set the DynamicMemoryEnabled parameter to $true, and then specify the minimum and maximum memory. Remember to use the MB at the end of the numbers or an error generates. I also set a buffer value of 20%. Here is the command.

Get-VM | Set-VMMemory -DynamicMemoryEnabled $true `

 -MinimumBytes 512MB -MaximumBytes 2048MB -Buffer 20

The complete script is shown here.

CreateVMfromVHD.ps1

 Import-Module hyper-V

$VHDPath = "E:\vhd"

$VMPath = "E:\VM"

$InternalSwitch = Get-VMSwitch -SwitchType Internal

Get-ChildItem $VHDPath |

ForEach-Object {

  new-vm -Name $_.BaseName -MemoryStartupBytes 512MB `

  -SwitchName $InternalSwitch.name -VHDPath $_.FullName -Path $VMPath}

Get-VM | Set-VMMemory -DynamicMemoryEnabled $true `

 -MinimumBytes 512MB -MaximumBytes 2048MB -Buffer 20

Enabling VM Integration services

To enable VM Integration services, I use the Enable-VMIntegrationService cmdlet. Unfortunately, each integration service must be specifically enumerated—there is no –all parameter. But, a little creative scripting makes this a piece of cake. I use the Get-VM cmdlet to retrieve all of my virtual machines and store them in a $vm variable. Next, I use the Foreach statement to walk through the collection. This portion of the code is shown here.

$vm = get-vm

Foreach($v in $vm)

 {

To retrieve the status of all of the integration services on the virtual machines, I use the Get-VMIntegrationService cmdlet. I pipe the results of this cmdlet to the Foreach-Object cmdlet, and then use the if statement to see if each of the integration services is enabled or not. If the service is not enabled, I use the Enable-VMIntegrationService cmdlet to turn it on. Here is that portion of the code.

Get-VMIntegrationService -VM $v |

    Foreach-object {

      if(!($_.enabled))

        {Enable-VMIntegrationService -Name $_.name -VM $v}} }

The complete script is shown here.

EnableAllVMIntegrationServices.ps1

$vm = get-vm

Foreach($v in $vm)

 {

  Get-VMIntegrationService -VM $v |

    Foreach-object {

      if(!($_.enabled))

        {Enable-VMIntegrationService -Name $_.name -VM $v}} }

 Sweet! Two simple scripts, and I recovered from a potentially time-consuming disaster. As seen in the following image, the virtual machines start up and are working fine.

Image of Hyper-V Manager

It could have been a REAL problem next week, while in Europe, when I needed to make a Windows PowerShell presentation.

Join me tomorrow when I will have guest blogger and Microsoft PFE Chris Wu talk about using Windows PowerShell to work with libraries. It is good stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I love the article, Ed!

    One caveat I want to bring to everyone's attention...

    Ed's script makes an important assumption in his 4th line of sample code - that there is only ONE VMswitch of type 'Internal'.  If true, all the code that follows will work as shown.  

    However, if you create a second VMswitch that is 'Internal' then you must modify the code that follows to select the specific switch you want.  For example, if you know that it was the first internal switch that you created, then you can use the syntax $internalswitch[0].name in the -switchname parameter for the new-vm command.

    Happy scripting!

    Dave