PDC Teaser – Attaching a VHD to A Virtual Machine


PDC is just around the corner now – I am giving the dry run of my talk tomorrow morning, so of course I am working on my demo’s today…  Just to give you a bit of a teaser here’s one of the scripts that I am using in one of my demo’s…

This script will take a VHD and attach it to the second port of the first IDE controller, just change the address numbers and you can change where it get’s attached.


Note that I am using the ProcessWMIJob function from Hyper-V WMI: Rich Error Messages for Non-Zero ReturnValue (no more 32773, 32768, 32700…)

$vhdToMount = "c:\test.vhd"
$HyperVGuest = "ScriptTest"

$VMManagementService = Get-WmiObject -class "Msvm_VirtualSystemManagementService" -namespace "root\virtualization"
$Vm = Get-WmiObject -Namespace "root\virtualization" -Query "Select * From Msvm_ComputerSystem Where ElementName=’$HyperVGuest’"
$VMSettingData = Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$Vm} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState"
$VmScsiController = (Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$VMSettingData} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_VirtualSystemSettingDataComponent" | `
                       
where-object {$_.ResourceSubType -eq "Microsoft Emulated IDE Controller" -and $_.Address -eq 0})
                        
$DiskAllocationSetting = Get-WmiObject -Namespace "root\virtualization" -Query "SELECT * FROM Msvm_AllocationCapabilities WHERE ResourceSubType = ‘Microsoft Synthetic Disk Drive’"
$DefaultDiskDrive = (Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$DiskAllocationSetting} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_SettingsDefineCapabilities" | `
                       
where-object {$_.InstanceID -like "*Default"})
$DefaultDiskDrive.Parent = $VmScsiController.__Path
$DefaultDiskDrive.Address = 1
$NewDiskDrive = ($VMManagementService.AddVirtualSystemResources($Vm.__Path, $DefaultDiskDrive.PSBase.GetText(1)) | ProcessWMIJob $VMManagementService "AddVirtualSystemResources").NewResources

$DiskAllocationSetting = Get-WmiObject -Namespace "root\virtualization" -Query "SELECT * FROM Msvm_AllocationCapabilities WHERE ResourceSubType = ‘Microsoft Virtual Hard Disk’"
$DefaultHardDisk = (Get-WmiObject -Namespace "root\virtualization" -Query "Associators of {$DiskAllocationSetting} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_SettingsDefineCapabilities" | `
                       
where-object {$_.InstanceID -like "*Default"})
                        
$DefaultHardDisk.Parent = $NewDiskDrive
$DefaultHardDisk.Connection = $vhdToMount

$VMManagementService.AddVirtualSystemResources($Vm.__Path, $DefaultHardDisk.PSBase.GetText(1)) | ProcessWMIJob $VMManagementService "AddVirtualSystemResources"

 

Taylor Brown
Hyper-V Integration Test Lead
http://blogs.msdn.com/taylorb

clip_image001

Comments (6)

  1. Mark Turner says:

    Cool, now how about a C# sample?

    ===========================================================

    Take a look at http://msdn.microsoft.com/en-us/library/cc160705(VS.85).aspx

    -taylor

  2. Mark Turner says:

    Yeh, I’ve tried that sample, but having troubles translating that to virtual hard drives.  They seem a bit more complicated???  On a side note the referenced sample works great, just not sure how to specify the “network” to attach the nic.

     ==========================================================
    I start my “vacation” next week – I am planning on trying to get cought up on blog posts and I’ll add this to the list.

    -Taylor
    p.s. yes that is my idea of a vacation.

  3. Siva says:

    Could you please provide the C# version of above code? We are having problems in executing the MSDN code. Thanks.

  4. Mark Turner says:

    Interesting way to vacation, guess you’re a computer geek like the rest of us 🙁

    OK, I figured out the same in C#…guess the secret was to understand how the WMI objects are all related.

    I did this by creating a VirtualMachineList utility that attempts to dump WMI objects that are

    related to an existing virtual machine, that helped tons.  

    Anyway, following is a sample of how to attach a virtual hard drive (.VHD) to a virtual machine’s SCSI controller in C# …

    This sample uses utilities found at http://msdn.microsoft.com/en-us/library/cc723869(VS.85).aspx

    using System;

    using System.Management;

    using System.Collections;

    using System.Text;

    Interesting way to vacation, guess you’re a computer geek like the rest of us 🙁

    OK, I figured out the same in C#…guess the secret was to understand how the WMI objects are all related.

    I did this by creating a VirtualMachineList utility that attempts to dump WMI objects that are

    related to an existing virtual machine, that helped tons.  

    Anyway, following is a sample of how to attach a virtual hard drive (.VHD) to a virtual machine’s SCSI controller in C#…

    This sample uses utilities found at http://msdn.microsoft.com/en-us/library/cc723869(VS.85).aspx

    using System;

    using System.Management;

    using System.Collections;

    using System.Text;

    //********************************************************************************************************************

    static UInt32 AddVirtualSystemResource(ManagementScope scope, ManagementObject virtualMachine, ManagementObject resourceToAdd, ref ManagementObject resourceAdded)

    {

      UInt32 result = 0;

      ManagementObject[] resourcesToAddList = new ManagementObject[1];

      resourcesToAddList[0] = resourceToAdd;

      string[] resourcesAddedList = null;

      result = AddVirtualSystemResources(scope, virtualMachine, resourcesToAddList, ref resourcesAddedList);

      if ((result == 0) && (resourcesAddedList != null))

      {

          resourceAdded = new ManagementObject(scope, new ManagementPath(resourcesAddedList[0]), null);

      }

      return result;

    }

    //********************************************************************************************************************

    static UInt32 AddVirtualSystemResources(ManagementScope scope, ManagementObject virtualMachine, ManagementObject[] resourcesToAdd, ref string[] resourcesAdded)

    {

      UInt32 result = 0;

      ManagementObject virtualSystemService = Utility.GetServiceObject(scope, "Msvm_VirtualSystemManagementService");

      ManagementBaseObject inParams = virtualSystemService.GetMethodParameters("AddVirtualSystemResources");

      int idx = resourcesToAdd.GetLength(0);

      string[] resourcesToAddString = new string[idx];

      g_dumpData.DumpString("——————————————————————————");

      g_dumpData.DumpString("AddedVirtualSystemResources():");

      idx = 0;

      foreach (ManagementObject resource in resourcesToAdd)

      {

          resourcesToAddString[idx++] = resource.GetText(TextFormat.CimDtd20);

          g_dumpData.DumpString("Type=" + resource["ResourceType"] + ", Subtype=" + resource["ResourceSubtype"]);

          if (resource["Connection"] != null)

          {

              foreach (string connection in (string[])resource["Connection"])

              {

                  g_dumpData.DumpString("Connection=" + connection);

              }

          }

      }

      inParams["ResourcesettingData"] = resourcesToAddString;

      inParams["TargetSystem"] = virtualMachine.Path.Path;

      resourcesAdded = null;

      ManagementBaseObject outParams = virtualSystemService.InvokeMethod("AddVirtualSystemResources", inParams, null);

      resourcesAdded = null;

      if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)

      {

          if (Utility.JobCompleted(outParams, scope))

          {

              result = 0;

              resourcesAdded = (string[])outParams["NewResources"];

              //syntheticNic = new ManagementObject(addedResources[0]);

              g_dumpData.DumpString("Resource(s) were added successfully.");

          }

          else

          {

              result = (UInt32)outParams["ReturnValue"];

              g_dumpData.DumpString("Failed to add resource(s)");

          }

      }

      else if ((UInt32)outParams["ReturnValue"] == ReturnCode.Completed)

      {

          result = 0;

          resourcesAdded = (string[])outParams["NewResources"];

          //syntheticNic = new ManagementObject(addedResources[0]);

          g_dumpData.DumpString("Resource(s) were added successfully.");

      }

      else

      {

          result = (UInt32)outParams["ReturnValue"];

          g_dumpData.DumpString("Add virtual system resource(s) failed with error:" + outParams["ReturnValue"]);

      }

      inParams.Dispose();

      outParams.Dispose();

      return result;

    }

    //********************************************************************************************************************

    static UInt32 AddVirtualHarddrive(ManagementScope scope, ManagementObject virtualMachine, string newVHDParams)

    {

      UInt32 result = 0;

      string vhdToMount = @"E:test.vhd";

      ManagementObject virtualSystemService = Utility.GetServiceObject(scope, "Msvm_VirtualSystemManagementService");

      ManagementObject vmSettingsData = Utility.GetVirtualSystemSettingData(virtualMachine);

      // Locate the SCSI controller on the vm

      ManagementObject vmDiskController = Utility.GetResourceAllocationsettingData(virtualMachine, ResourceType.ParallelSCSIHBA, ResourceSubType.ParallelSCSIHBA, null);

      if (vmDiskController == null)

      {

          // SCSI controller does not exits on the vm, create it

          ManagementObject vmDiskControllerDefault = Utility.GetResourceAllocationsettingDataDefault(scope, ResourceType.ParallelSCSIHBA, ResourceSubType.ParallelSCSIHBA, null);

          result = AddVirtualSystemResource(scope, virtualMachine, vmDiskControllerDefault, ref vmDiskController);

      }

      // Create the Synthetic disk drive on the SCSI controller

      int SCSIfreeLocation = -1;

      LocateFreeSCSILocation(scope, vmDiskController.Path.ToString(), ref SCSIfreeLocation);

      ManagementObject syntheticDiskDriveDefault = Utility.GetResourceAllocationsettingDataDefault(scope, ResourceType.Disk, ResourceSubType.DiskSynthetic, null);

      syntheticDiskDriveDefault["Parent"] = vmDiskController.Path;

      syntheticDiskDriveDefault["Address"] = SCSIfreeLocation;

      syntheticDiskDriveDefault["Limit"] = 1; // Not sure what this does???

      ManagementObject newDiskDrive = null;

      result = AddVirtualSystemResource(scope, virtualMachine, syntheticDiskDriveDefault, ref newDiskDrive);

      // Now create a new virtual hard disk, associate it with the new synthetic disk drive and attach the virtual hard drive to the virtual machine

      ManagementObject vhdDefault = Utility.GetResourceAllocationsettingDataDefault(scope, ResourceType.StorageExtent, ResourceSubType.VHD, null);

      vhdDefault["Parent"] = newDiskDrive;

      string[] connection = new string[1];

      connection[0] = vhdToMount;

      vhdDefault["Connection"] = connection;

      ManagementObject newVHD = null;

      result = AddVirtualSystemResource(scope, virtualMachine, vhdDefault, ref newVHD);

      return result; // maybe return newVHD

    }

    //********************************************************************************************************************

  5. Don V. says:

    Mark,

         in your C# example you have LocateFreeSCSILocation(scope, vmDiskController.Path.ToString(), ref SCSIfreeLocation);

    but "LocateFreeSCSILocation" doesn’t exist anywhere else in the context?

    -Don

  6. Amit says:

    Hi

    Thanks works great..Can you provide a similar code to remove an attached VHD.

    Amit