Wednesday, July 27, 2011

How to create differencing disk VM from template on SCVMM 2012 via powershell

This quick post will provide an example of how to create a Virtual Machine (VM) based off an VMM template that utilizes differencing disks. 

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
  1. Looks up the specified Hardware profile
  2. Looks up the specified template (this is used to get the parent VHD location/name)
  3. Looks up info on the specified target HyperV host (so we can find the placement drive)
  4. Checks to see if the ParentVHD already exists on the HyperV hosts Placement Drive
  5. If the VHD does not exist it copies it to the HyperV host
  6. Creates Diff Disk via WMI on hyperV host pointing to parent VHD
  7. Creates the VM via the New-SCVirtualMachine cmdlet using the UseLocalVirtualHardDisk command. (this tells VMM to allow Diff disks).
It can be downloaded from http://dl.dropbox.com/u/3275573/Create_Diff_VM_From_Template.ps1
    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"
    

    2 comments:

    davtup said...

    Hi Mike,
    could 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?

    Rahul said...

    Davtup,
    If 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