Pierre Roman and I have been hard at work preparing for the upcoming round of CANITPRO Camps being made available in a couple months time to the general public.  For those of you who have attended a CANITPRO Camp in the past, you may be aware of the 20 or so computers provided for utilization to complete labs during the camp.  Pierre and I meticulously setup each machine before hand to ensure the labs run as they are supposed to.  One of the preparation steps involves the creation of the VMs to be utilized I the labs.  While there are many methods to complete this, I recently came across a great PowerShell script written by a PowerShell MVP by the name of Jeffrey Hicks who has written a script to automate the process in creating a VM from an ISO.

The script requires a existing PowerShell script which can be downloaded for free and is entitled Convert-WindowsImage.ps1. The script creates a bootable VHD or VHDX file from an ISO installation image and requires PowerShell 3.0 to operate. The additional script that Jeffery provides extracts the server version from the ISO file, creates a VHD or VHDX file based on the installation media and inserts it into an unattend.xml file.  In this example, Jeffery has downloaded the Windows Server 2012 ISO.

Said script is as follows:
 

#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:\VHD\unattend.xml",

[ValidateNotNullorEmpty()]
[string]$VHDPath = "F:\VHD\Win2012DatacenterEvalCore.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:\scripts\Convert-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 preceding script takes values for the virtual machine name and the path to the ISO file. Currently the default value is based on an ISO file normally used by Jeffery. Please remember to specify which edition you wish to convert as Windows Server installation files can contain multiple images. Also required is the specification of the name and path for the VHD or VHDX file, including the extension as the script requires the extension to know what kind of disk file to create.  Also be sure to specify the path to the unattend.xml file which will be inserted into the new VHD. The script provided by Microsoft will then automatically mount the new VHD, copy the unattend.xml file and automatically dismount. Finally basic settings for the new virtual machine, such as the number of processors utilized and the amount of memory required, will need to be specified.

The following script calls the Convert-WindowsImage.ps1 script to create the VHD or VHDX file:
 

Try {
  #call the convert script splatting the parameter hashtable
  c:\scripts\Convert-WindowsImage.ps1 @convertParams
}

 
This can take up to 10 minutes to process depending on the disk size to be created and where the creation is to occur. The disk files are automatically configured to be dynamically expanding and the script also supports –WhatIf and  –Verbose so you can test out and verify your values.
 
 
Once the disk file is obtained, creating the virtual machine is simple using New-VM with parameters gathered from the following script:



New-VM -Name $Name -VHDPath $VHDPath -MemoryStartupBytes $Memory -SwitchName $Switch | Set-VM -DynamicMemory -ProcessorCount $ProcessorCount -Passthru
 

Additional configuration changes can be added with the end result being a working virtual machine with much of the OS configured via utilizing the unattend.xml file.
 
To add, with the recent release of Windows Server 2012 R2 Preview Jeffery has been kind enough to create a script to quickly build a new virtual machine utilizing the preview. Be sure to download the ISO copy of the Windows Server 2012 Preview followed by running the following script:
 

PS C:\scripts> .\New-VMfromISO.ps1 -ISOPath  G:\iso\Windows2012R2-Datacenter-Preview.iso -Edition ServerDataCenterCore  -VHDPath g:\vhds\Win2012R2Core.vhdx  -Name Win2012R2-Core-Baseline