Prepare VM: Create VM programmatically, Hyper-V API, C# version



Create VM (Hyper-V) via code – .NET version Create VM programmatically


[Sergei Meleshchuk. http://blogs.msdn.com/sergeim/] 


From code, you can create many VMs per second. Those will be “bare-metal” VMs of course – you still will need to load image or just install OS on them. My example does not work on remote hosts, but you can modify the code in few minutes to do same on remote boxes.


PowerShell vs. C#


It looks everyone likes PowerShell, and for Hyper-V management in particular.


Sometimes I feel more traditional languages (like C#) give more control/error handling. Another C# benefit is what I think is unsurpassed tracing and debugging facilities of Visual Studio (especially if to forget WinDbg for a second). Sometimes you can learn API by just stepping thru the code.


Credits


Here below is the PS1 à C# port of some code written originally by James O’Neill. I might have changed something a little, no offence taken.


About this document


I briefly 1) outline needed steps, 2) comment code fragments and 3) provide full code. Add System.Management.dll to references, and start the VS elevated (that is, right-click, select ‘run as administrator).


Steps needed


The steps are:


          Get parameters from command line


          Obtain the “MsVM_VirtualSystemManagementService” object, which will do all the work for us. I will call it VSMS for brevity.


          Step 1.
Ask VSMS to create a sort of empty VM definition. I think about this thing as of a template.


          Step 2.
Obtain set of VM settings; fill in those settings


          Step 3.
Modify the newly created ComputerSystem (sort of template) with the settings we just prepared.


Comments on steps


First, some abbreviations I use:


    using MO = ManagementObject;


    using MBO = ManagementBaseObject;


    using MOS = ManagementObjectCollection;


 


Step 1. Create VM definition.


Trick here is a late-bound call on VSMS; see how managed WMI calls are coded (search for WMI InvokeMethod). The code is:


// Create VM with empty settings


 


MBO definition = sysMan.InvokeMethod(


    Constants.DefineVirtualSystem,


    sysMan.GetMethodParameters(Constants.DefineVirtualSystem),  // empty set


    null);


uint retCode = (uint)definition[“returnvalue”];


if (retCode != Constants.ERROR_SUCCESS)


    throw new InvalidOperationException(“DefineVirtualSystem failed”);


 


Next we get the WMI’s ManagementObject, which represents the instance of MSVM_ComputerSystem WMI class:


string vmPath = definition[“DefinedSystem”] as string;


MO computerSystemTemplate = new MO(vmPath);


 


At this point, we are half-done.


Step 2. Fill in the VM settings


First, get the generated VM name, which is actually a GUID (the display name is called ‘elementname’ in Hyper-V API world). Then we locate the ‘settings’ object:


string vmName = (string)computerSystemTemplate[“name”];


// this is GUID; will need to locate settings for this VM


 


MO settings = GetMsvm_VirtualSystemSettingData(vmName);


 


Now fill in the settings object:


// Now, set settings of this MSVM_ComputerSystem as we like


 


settings[“elementname”] = displayName;


settings[“notes”] = notes;


settings[“BIOSGUID”] = new Guid();


settings[“BIOSSerialNumber”] = “1234567890”;


settings[“BIOSNumLock”] = “true”;


// settings[“…”] = …;


// … set whatever you like; see list at


//     http://msdn.microsoft.com/en-us/library/cc136944(VS.85).aspx


settings.Put();


 


Step 3. Finalize.


Use VSMS (management service) again to propagate settings to VM object.


// Now, set the settings which were build above to newly created ComputerSystem


 


MBO inParams = sysMan.GetMethodParameters(Constants.ModifyVirtualSystem);


string settingsText = settings.GetText(TextFormat.WmiDtd20);


inParams[“ComputerSystem”] = computerSystemTemplate;


inParams[“SystemSettingData”] = settingsText;


MBO resultToCheck = sysMan.InvokeMethod(


    Constants.ModifyVirtualSystem,


    inParams,


    null);


 


// Almost done – now apply the settings to newly created ComputerSystem


 


MO settingsAsSet = (MO)resultToCheck[“ModifiedSettingData”];


 


// Optionally print settingsAsSet here


 


Log(“Created: VM with name ‘{0}’ and GUID name ‘{1}'”, displayName, vmName);


 CreateVm


 


 


Helper functions


Useful functions (to be coded once) are:


#region Wmi Helpers


private MO GetWmiObject(string classname, string where)


{


    MOS resultset = GetWmiObjects(classname, where);


    if (resultset.Count != 1)


        throw new InvalidOperationException(


            string.Format(


                “Cannot locate {0} where {1}”,


                classname,


                where));


    MOS.ManagementObjectEnumerator en = resultset.GetEnumerator();


    en.MoveNext();


    MO result = en.Current as MO;


    if (result == null)


        throw new InvalidOperationException(“Failure retrieving “ + classname +


    return result;


}


 


private MOS GetWmiObjects(string classname, string where)


{


    string query;


    ManagementScope scope = new ManagementScope(@”root\virtualization”, null);


    if (where != null)


    {


        query = string.Format(


           “select * from {0} where {1}”,


           classname,


           where);


    }


    else


    {


        query = string.Format(


            CultureInfo.InvariantCulture,


            “select * from {0}”,


            classname);


    }


    ManagementObjectSearcher searcher = new ManagementObjectSearcher(


        scope,


        new ObjectQuery(query));


 


    ManagementObjectCollection resultset = searcher.Get();


    return resultset;


}


 


#endregion Wmi helpers


 


Full code


// This is port to C# from a Powershell script written by James O’Neill


using System;


using System.Globalization;


using System.Management;


namespace Hyperv.Misc


{


    using MO = ManagementObject;


    using MBO = ManagementBaseObject;


    using MOS = ManagementObjectCollection;


    class MainCreateVm


    {


        static void Main(string[] args)


        {


            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(Oops);


            new MainCreateVm().CreateVm(args);


        }


        void CreateVm(string[] args)


        {


            if (args.Length < 1)


            {


                Console.ForegroundColor = ConsoleColor.Red;


                Log(“Usage:   createvm <vmname> [<notes>]”);


                Console.ForegroundColor = ConsoleColor.White;


                Log(“Example: createvm vm1”);


                Console.ResetColor();


                Environment.Exit((int)Constants.ERROR_INV_ARGUMENTS);


            }


            string displayName = args[0];


            string notes;


            if (args.Length > 1)


                notes = args[1];


            else


                notes = “Created “ + DateTime.Now;


            MO sysMan = GetMsVM_VirtualSystemManagementService();


 


            // Create VM with empty settings


 


            MBO definition = sysMan.InvokeMethod(


                Constants.DefineVirtualSystem,


                sysMan.GetMethodParameters(Constants.DefineVirtualSystem),  // empty set


                null);


            uint retCode = (uint)definition[“returnvalue”];


            if (retCode != Constants.ERROR_SUCCESS)


                throw new InvalidOperationException(“DefineVirtualSystem failed”);


 


            // Obtain WMI root\virtualization:ComputerSystem object.


            // we will need “Name” of it, which is GUID


 


            string vmPath = definition[“DefinedSystem”] as string;


            MO computerSystemTemplate = new MO(vmPath);


            string vmName = (string)computerSystemTemplate[“name”];


            // this is GUID; will need to locate settings for this VM


 


            MO settings = GetMsvm_VirtualSystemSettingData(vmName);


 


            // Now, set settings of this MSVM_ComputerSystem as we like


 


            settings[“elementname”] = displayName;


            settings[“notes”] = notes;


            settings[“BIOSGUID”] = new Guid();


            settings[“BIOSSerialNumber”] = “1234567890”;


            settings[“BIOSNumLock”] = “true”;


            // settings[“…”] = …;


            // … set whatever you like; see list at


            //     http://msdn.microsoft.com/en-us/library/cc136944(VS.85).aspx


            settings.Put();


 


            // Now, set the settings which were build above to newly created ComputerSystem


 


            MBO inParams = sysMan.GetMethodParameters(Constants.ModifyVirtualSystem);


            string settingsText = settings.GetText(TextFormat.WmiDtd20);


            inParams[“ComputerSystem”] = computerSystemTemplate;


            inParams[“SystemSettingData”] = settingsText;


            MBO resultToCheck = sysMan.InvokeMethod(


                Constants.ModifyVirtualSystem,


                inParams,


                null);


 


            // Almost done – now apply the settings to newly created ComputerSystem


 


            MO settingsAsSet = (MO)resultToCheck[“ModifiedSettingData”];


 


            // Optionally print settingsAsSet here


 


            Log(“Created: VM with name ‘{0}’ and GUID name ‘{1}'”, displayName, vmName);


        } // CreateVm


        private MO GetMsVM_VirtualSystemManagementService()


        {


            return GetWmiObject(“MsVM_VirtualSystemManagementService”, null);


        }


        private MO GetMsvm_VirtualSystemSettingData(string vmName)


        {


            return GetWmiObject(


                “Msvm_VirtualSystemSettingData”,


                string.Format(“systemname='{0}'”, vmName));


        }


        #region Wmi Helpers


        private MO GetWmiObject(string classname, string where)


        {


            MOS resultset = GetWmiObjects(classname, where);


            if (resultset.Count != 1)


                throw new InvalidOperationException(


                    string.Format(


                        “Cannot locate {0} where {1}”,


                        classname,


                        where));


            MOS.ManagementObjectEnumerator en = resultset.GetEnumerator();


            en.MoveNext();


            MO result = en.Current as MO;


            if (result == null)


                throw new InvalidOperationException(“Failure retrieving “ + classname + ” where “ + where);


            return result;


        }


 


        private MOS GetWmiObjects(string classname, string where)


        {


            string query;


            ManagementScope scope = new ManagementScope(@”root\virtualization”, null);


            if (where != null)


            {


                query = string.Format(


                   “select * from {0} where {1}”,


                   classname,


                   where);


            }


            else


            {


                query = string.Format(


                    CultureInfo.InvariantCulture,


                    “select * from {0}”,


                    classname);


            }


            ManagementObjectSearcher searcher = new ManagementObjectSearcher(


                scope,


                new ObjectQuery(query));


 


            ManagementObjectCollection resultset = searcher.Get();


            return resultset;


        }


 


        #endregion Wmi helpers


        private static void Log(string message, params object[] data)


        {


            Console.WriteLine(message, data);


        }


        private static void Oops(object sender, UnhandledExceptionEventArgs e)


        {


            Console.BackgroundColor = ConsoleColor.White;


            Console.ForegroundColor = ConsoleColor.Black;


            Exception ex = e.ExceptionObject as Exception;


            Log(ex.Message);


            Console.ResetColor();


            Log(ex.ToString());


        }


    } // class MainCreateVm


    class Constants


    {


        internal const string DefineVirtualSystem = “DefineVirtualSystem”;


        internal const string ModifyVirtualSystem = “ModifyVirtualSystem”;


        internal const uint ERROR_SUCCESS = 0;


        internal const uint ERROR_INV_ARGUMENTS = 87;


    }


}


 


 


 


 


 

Comments (3)

  1. Brian says:

    Is it possible to set the number of processors at the time the vm is created or does the processor resource settings need to be modified post create?

  2. Aysha says:

    Hey Sergei, I am working on a service which needs to be do tasks like getting all vhds attached to a vm, attaching a vhd and stuff like that. It will not be a one time script, it is like service is listening for requests and based on those requests getting vhds attached,attaching vhd etc. For me it seems like C# is the solution instead of powershell. Can you comment? Also if I use C# isthe functionality given by powershell for hyper-v same as the one for c#?