This is special because VMM does not support this natively. So you have to contact the target HyperV host (via WMI) to create the Diff disk. In addition to need to make sure that the HyperV host has the parent VHD already in place.
The Powershell cmdlet below does the following
- Looks up the specified Hardware profile
- Looks up the specified template (this is used to get the parent VHD location/name)
- Looks up info on the specified target HyperV host (so we can find the placement drive)
- Checks to see if the ParentVHD already exists on the HyperV hosts Placement Drive
- If the VHD does not exist it copies it to the HyperV host
- Creates Diff Disk via WMI on hyperV host pointing to parent VHD
- Creates the VM via the New-SCVirtualMachine cmdlet using the UseLocalVirtualHardDisk command. (this tells VMM to allow Diff disks).
param($VMName,$TemplateName = "Windows Server 2008 R2 SP1 Enterprise", $HardwareProfileName = "ServerHardware", $VMMHost = "SCVMM",$targethostname = $null)
$Err = 0
If ($VMName -eq $null)
{
Write-Host "`nERROR: Incorrect arguments" -foregroundcolor red -backgroundcolor black
Write-Host "`nREQUIRED ARGUMENTS:" -foregroundcolor cyan
Write-Host "`n -VMName `"Name of the VM to create`"" -foregroundcolor cyan
Write-Host "`n -TemplateName `"Name of the VMM Template`"" -foregroundcolor cyan
Write-Host "`n -HardwareProfileName `"Name of the VMM Hardware Profile to use with the template`"" -foregroundcolor cyan
Write-Host "`n -VMMHost `"VMM Computername`"" -foregroundcolor cyan
Write-Host "`n -targetHostname `"Computername of HyperV host to target`"" -foregroundcolor cyan
Exit $Err
}
function CreateDiffDiskOnHost
{
param([string]$hostComputerName, [string]$childPath, [string]$parentPath)
#get the image mgmt service instance for the host computer
$VHDService = get-wmiobject -class "Msvm_ImageManagementService" -namespace "root\virtualization" -computername $hostComputerName
#create a differencing disk from the base disk
$Result = $VHDService.CreateDifferencingVirtualHardDisk($childPath, $parentPath)
}
function TestFileLock {
## Attempts to open a file and trap the resulting error if the file is already open/locked
param ([string]$filePath )
$filelocked = $false
$fileInfo = New-Object System.IO.FileInfo $filePath
trap {
Set-Variable -name locked -value $true -scope 1
continue
}
$fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
if ($fileStream) {
$fileStream.Close()
}
$obj = New-Object Object
$obj | Add-Member Noteproperty FilePath -value $filePath
$obj | Add-Member Noteproperty IsLocked -value $filelocked
$obj
}
write-host "VMName is $VMName"
write-host "Template is $TemplateName"
write-host "Hardware Profile is $HardwareProfileName"
write-host "VMMHost is $VMMHost"
write-host "Target Host is $targethostname"
$guid = [guid]::NewGuid()
write-host "Lookup the base hardware Profile $HardwareProfileName"
$HardwareProfile = Get-SCHardwareProfile -VMMServer $VMMHost | where {$_.Name -eq $HardwareProfileName}
if ($HardwareProfile -eq $null)
{
write-host "Failed to lookup HardwareProfile $HardwareProfileName" -foregroundcolor red -backgroundcolor black
EXIT 1
}
write-host "Getting information for template $TemplateName"
$Template = Get-SCVMTemplate -VMMServer $VMMHost -All | where {$_.Name -eq $TemplateName}
if ($Template -eq $null)
{
write-host "Failed to lookup Template $TemplateName" -foregroundcolor red -backgroundcolor black
EXIT 1
}
#Get the VHD info for the template
$VHD = Get-SCVirtualharddisk | where {$_.Name -eq $template.VirtualDiskDrives[0].VirtualHardDisk}
write-host "Template VHD Path " $VHD.SharePath
if ($targethostname -eq $null)
{
write-host "Invalid targethostname" -foregroundcolor red -backgroundcolor black
exit 1
}
$vmhost = Get-SCVMHost $targethostname
$vmHostPath = [string]$vmhost.vmpaths
write-host "Target VMHost: " $vmhost.name
write-host "Target Drive: " $vmhost.VMPaths
$remotePath = "\\" + $vmhost.name + "\" + $vmHostPath.substring(0,1) + "$\" + [system.io.path]::GetFileName($VHD.SharePath)
"Checking to see if Parent VHD exists on HV Host Drive"
"RemotePath: $remotePath"
if (Test-Path $remotePath)
{
"Parent VHD already exists on HV Host"
}
else
{
"$RemotePath does not exist, going to Copy"
[System.IO.File]::Copy($VHD.SharePath,$remotePath);
}
"Creating Diff Disk"
$targetVHDPath = $vmHostPath + $VMName + ".vhd"
$ParentVHDPath = $vmHostPath + [system.io.path]::GetFileName($VHD.SharePath)
CreateDiffDiskOnHost "$vmhost" "$targetVHDPath" "$ParentVHDPath"
Start-Sleep -s 20
$targetVHDRemotePath = "\\" + $vmhost.name + "\" + $vmHostPath.substring(0,1) + "$\" + $VMName + ".vhd"
if (Test-Path $targetVHDRemotePath)
{
"Diff Disk Created at:$targetVHDRemotePath"
}
else
{
"$targetVHDRemotePath does not exist yet. Sleeping for a few"
Start-Sleep -s 30
if (Test-Path $targetVHDRemotePath)
{
"Disk was created."
}
else
{
write-host "Failed to find VHD $targetVHDRemotePath after sleep, exiting" -foregroundcolor red -backgroundcolor black
exit 1
}
}
#Set a random startup delay to reduce VM startup IOPs overload
$startDelay = Get-Random -minimum 1 -maximum 30
$lockstate = TestFileLock "$targetVHDRemotePath"
if ($lockstate.IsLocked)
{
"File locked, sleeping"
Start-Sleep -s 30
}
$mv = move-SCvirtualharddisk -Bus 0 -Lun 0 -IDE -path $targetVHDPath -jobgroup $guid
$description = $template.tag
New-SCVirtualMachine -Name $vmName -VMHost $vmHost -VMTemplate $template -UseLocalVirtualHardDisk -HardwareProfile $HardwareProfile -ComputerName $vmName -path $vmHostPath -delaystartseconds $startDelay -Description "$description" -mergeanswerfile $true -BlockDynamicOptimization $false -StartVM -JobGroup "$guid" -RunAsynchronously -StartAction "AlwaysAutoTurnOnVM" -StopAction "SaveVM"
Hi Mike,
ReplyDeletecould you please guide the way to import data from a .csv file in to your script for the first line:
param($VMName,$TemplateName = "Windows Server 2008 R2 SP1 Enterprise", $HardwareProfileName = "ServerHardware", $VMMHost = "SCVMM",$targethostname = $null)
How to import those information like VMName, Template... from csv file?
Davtup,
ReplyDeleteIf your CSV has fields like
VMName, Template etc and has associated value, do following
$csvlist = import-csv ""
foreach($entry in $csvlist)
{
$entry.VMName
$entry.Template
}
Try this,
-Rahul Tarambale