Save to My DOJO
Table of contents
Today I have another article that continues my fascination with Hyper-V, PowerShell and spinning up new virtual machines with minimal intervention.
During a recent conference, fellow PowerShell MVP Aleksandar Nikolic did a presentation on creating a Hyper-V test lab. One of his demonstrations used a tool I had forgotten about and sparked an idea for a PowerShell script. My script will create a new Hyper-V virtual machine based on an ISO file for a Windows Server. Most of Microsoft’s installation media can be downloaded as ISO images from MSDN or TechNet. Or you might download an evaluation installation ISO of a new operating system so that you can test it out, such as Windows Server 2012. This is a great reason to use a Hyper-V virtual machine.
My script relies on a PowerShell script which you can freely download from Microsoft called Convert-WindowsImage.ps1. The script was written by Microsoft and is designed to create a bootable VHD or VHDX file from an ISO installation image. You can download the script from http://gallery.technet.microsoft.com/scriptcenter/Convert-WindowsImageps1-0fe23a8f#content. It will require PowerShell 3.0. I’m not going to go into the details of the script except to explain that it will extract a server version from the ISO file, create a VHD or VHDX file based on the installation media and insert an unattend.xml file. With a little planning you could create a system for cranking out new virtual machines based on a variety of unattend.xml files. You could even generate or modify the XML files on the fly. But that is beyond what I want to demonstrate in this article.
First, let me show you my script.
#requires -version 3.0 <# Create a Hyper-V virtual machine from an ISO file and template This script requires the Convert-WindowsImage.ps1 script which you can download from Microsoft: http://gallery.technet.microsoft.com/scriptcenter/Convert-WindowsImageps1-0fe23a8f#content #> [cmdletbinding(SupportsShouldProcess)] Param( [Parameter (Position = 0,Mandatory, HelpMessage = "Enter the name of the new virtual machine")] [ValidateNotNullorEmpty()] [string]$Name, [ValidateScript({Test-Path $_ })] [string]$ISOPath = 'F:9200.16384.WIN8_RTM.120725-1247_X64FRE_SERVER_EVAL_EN-US-HRM_SSS_X64FREE_EN-US_DV5.ISO', [ValidateNotNullorEmpty()] [string]$Edition = "ServerDatacenterEvalCore", [ValidateScript({$_ -ge 10GB})] [int64]$Size = 20GB, [ValidateScript({Test-Path $_ })] [string]$Unattend = "F:VHDunattend.xml", [ValidateNotNullorEmpty()] [string]$VHDPath = "F:VHDWin2012DatacenterEvalCore.vhdx", [ValidateScript({$_ -ge 256MB})] [int64]$Memory = 1GB, [ValidateNotNullorEmpty()] [string]$Switch = "Work Network", [ValidateScript({$_ -ge 1})] [int]$ProcessorCount = 2 ) #region Convert ISO to VHD Write-Verbose "Converting ISO to VHD(X)" #parse the format from the VHDPath parameter value [regex]$rx = ".VHD$|.VHDX$" #regex pattern is case-sensitive if ($vhdpath.ToUpper() -match $rx) { #get the match and strip off the period $Format = $rx.Match($vhdpath.toUpper()).Value.Replace(".","") } else { Throw "The extension for VHDPath is invalid. It must be .VHD or .VHDX" Return } #define a hashtable of parameters and values for the Convert-WindowsImage $convertParams = @{ SourcePath = $ISOPath SizeBytes = $size UnattendPath = $Unattend Edition = $Edition VHDFormat = $Format VHDPath = $VHDPath ErrorAction = 'Stop' } Write-Verbose ($convertParams | Out-String) #define a variable with information to be displayed in WhatIf messages $Should = "VM $Name from $ISOPath to $VHDPath" #region -Whatif processing If ($pscmdlet.ShouldProcess($Should)) { Try { #call the convert script splatting the parameter hashtable c:scriptsConvert-WindowsImage.ps1 @convertParams } Catch { Write-Warning "Failed to convert $ISOPath to $VHDPath" Write-Warning $_.Exception.Message } } #should process #endregion #endregion #region Creating the virtual machine Write-Verbose "Creating virtual machine $Name" Write-Verbose "VHDPath = $VHDPath" Write-Verbose "MemoryStartup = $Memory" Write-Verbose "Switch = $Switch" Write-Verbose "ProcessorCount = $ProcessorCount" New-VM -Name $Name -VHDPath $VHDPath -MemoryStartupBytes $Memory -SwitchName $Switch | Set-VM -DynamicMemory -ProcessorCount $ProcessorCount -Passthru Write-Verbose "New VM from ISO complete" #endregion
The script takes values for the virtual machine name and the path to the ISO file. I’ve set a default value based on an ISO file that I use. Because Windows Server installation files can contain multiple images, you need to specify the edition you wish to convert. You also need to specify the name and path for the VHD or VHDX file, including the extension. The script needs the extension so it knows what kind of disk file to create. Specify the path to an unattend.xml file which will be inserted into the new VHD. The Microsoft script will automatically mount the new VHD, copy the unattend file and automatically dismount. Finally, you will also need to specify some basic settings for the new virtual machine such as the amount of memory, the number of processors and the Hyper-V switch.
[optin-monster-shortcode id=”lwzgfpb294pntqna”]
My script first calls the Convert-WindowsImage.ps1 script to create the VHD or VHDX file.
Try { #call the convert script splatting the parameter hashtable c:scriptsConvert-WindowsImage.ps1 @convertParams }
This might take up to 10 minutes depending on the size of the disk you want to create and where you are creating it. When I create a 15GB VHDX on an SSD drive it only took a few minutes. By the way, the disk files are automatically configured to be dynamically expanding. My script also supports –WhatIf and –Verbose so you can test out and verify your values.
Once I have a disk file, creating the virtual machine is very easy using New-VM with parameters gathered from the script.
New-VM -Name $Name -VHDPath $VHDPath -MemoryStartupBytes $Memory -SwitchName $Switch | Set-VM -DynamicMemory -ProcessorCount $ProcessorCount -Passthru
In Figure 1 you can see my script in action.
Figure 1
The new VM is piped to Set-VM to configure. You could add as many additional configuration changes as you want from PowerShell. The end result is a working virtual machine with as much of the OS configured as I want using the unattend.xml file. If it helps, unattend file that I use.
My script certainly works as a stand-alone tool, although you will most likely want to adjust some of the default parameter values. I would suggest you use the script as the basis for building your own provisioning system. There are so many variables in how someone might want to spin up and provision a new virtual machine that I haven’t tried to build a perfect solution. Instead I’ve written articles on the blog that explore the process from different angles with the assumption that you will be able to pull something together.
For PowerShell help, including using the Hyper-V cmdlets, I encourage you to use the forum at http://powershell.org.
Not a DOJO Member yet?
Join thousands of other IT pros and receive a weekly roundup email with the latest content & updates!
10 thoughts on "How to Create a Hyper-V VM from an ISO using PowerShell"
Since Windows Server 2012 R2 Preview just came out, I used this script to quickly build a new virtual machine.
PS C:scripts> .New-VMfromISO.ps1 -ISOPath G:isoWindows2012R2-Datacenter-Preview.iso -Edition ServerDataCenterCore -VHDPath g:vhdsWin2012R2Core.vhdx -Name Win2012R2-Core-Baseline
I renamed the ISO file to make it easier to type. Once I have the VM configured I’ll use it as a baseline to spin up more VMs using differencing disks.
Hi,
Great guide, I’m trying to run it for W2012R2 machine BUT it doesnt work . . . it tell me that the Micrososft Script cannot be loaded because is not digitally signed.
Ok was a problem on my server, now it runs… but it create GEN1 VM – is it possible to create GEN2 VM?
Hi,
Great article, I have created the same script (with modifications for my environment), I have added at the END of the script -GENERATION 2 to create a GENERATION 2 VM.
The problem IS that when I try to boot the VM it doesnt start (I have made an ANSWER FILE with THIS guide (http://www.ragnarharper.com/2013/09/unattended-installation-of-windows-server-2012-r2.html )
Any idea?
Another thing, I have see that the UNATTENDED DONT CREATE the partition in the VHDX but the other settings (serial,…) are in the VHDX… why?
Thanks for reading and I’m glad you found this useful.
A generation 2 virtual machine needs some special setup, especially if you are doing it from the console. You won’t be able to use the code in my post to create a Gen2 VM. The disk and VM have to be configured to support UEFI. But stay tuned as I am working on some new content on this topic.
Finally, I’m not sure I understand the comment about the “don’t create the partition” in the unattend file.
Everything seems to work but the VM isn’t bootable. I get this error in PS. Any idea how/what to fix? thanks.
VERBOSE: Partition Style : 0
VERBOSE: Disk Signature : 1423167926
VERBOSE: Partition Offset: 1048576
ERROR : Could not get the BootMgr object from the Virtual Disks BCDStore.
INFO : Log folder is C:UsersADMINI~1AppDataLocalTempConvert-WindowsImage4758eeab-519e-4d87-a8f0-d2265459142a
John, are you trying to create a generation 2 VM? My code won’t work for that. Did you change the path for Convert-WindowsImage? My script assumes it is in C:Scripts.
Hi there,
I cant seem to get this to work with the latest Convert-WindowsImageps1
https://gallery.technet.microsoft.com/scriptcenter/Convert-WindowsImageps1-0fe23a8f
Its fails to create the VHD
I’ll have to add this to the list of topics to revisit.