Working with SCVMM 2012 Powershell in C# - InvokePowerShell function
I have a blog post on Working with SCVMM Powershell in C# - InvokePowerShell funciton, that has been pretty popular and helpful to some. I want to write a subsequent blog post on doing the same thing but what has changed in SCVMM 2012.
SCVMM 2012 has replaced it's pssnapin (formerly Microsoft.SystemCenter.VirtualMachineManager) with a PowerShell module. Modules were introduced in PowerShell v2 and have added functionality over pssnapins. There is plenty on the web on PowerShell modules, and that is not the topic of this blog. In this blog I will show you how to load the SCVMM 2012 PowerShell module so you may make C# calls to SCVMM 2012 via embedded PowerShell.
In my applications, I use the InvokePowerShell function to call into SCVMM any time I need to work with any of the objects managed by SCVMM. My function will take a string parameter that contains the powershell I want to call. In my sample string you will see I add \r\n in my strings. This puts line breaks in the string so that when I debug, and I view my powershell in the Visual Studio debugger windows, the script is very easy to read (and check for errors!).
The function will return a PSObject. This PSObject will return any errors if occurred, or if successful any information that would be normally seen in the powershell window. I show some examples of how to use this function as well.
Before you can use this function, you will need to add a reference to the System.Management.Automation.dll. This is not so straight forwarded because this .dll is a little hard to find. Take a look at this article that will help you add this reference.
https://www.codeplex.com/wikipage?ProjectName=ShinyPower&title=Frequently%20Asked%20Questions
Here is link for reference on the System.Management.Automation namespace:
https://msdn.microsoft.com/en-us/library/system.management.automation(VS.85).aspx
OK.. let's take a look at the function:
RunspaceConfiguration rconfig = RunspaceConfiguration.Create();
PSSnapInException Pwarn = new PSSnapInException();
Collection<PSObject> results = new Collection<PSObject>();
Runspace runspace = RunspaceFactory.CreateRunspace();
string test = "Import-Module VirtualMachineManager\r\n";
runspace = RunspaceFactory.CreateRunspace(rconfig);
runspace.Open();
pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(test);
try
{
results = pipeline.Invoke();
}
catch (Exception ex)
{
return ex.Message;
}
OK so now we have a function to call our powershell scripts. You will have to check and handle your exceptions yourself. Do not leave the try-catch like this as you will miss uhandled exceptions.
There is one issue you have to be very careful with with this function. If this function is run in a process, and multiple calls to Get-VMMSerrver are called, a connection for each call will be held in the process until the process is taken down. The runspace Close and Dispose call do not clear these out. Avoid calling the generic Get-VMMServer cmdlet with this function. If you must use this cmdlet, make sure that it is only called once, and store a way to check with it with a static variable in your code.
Let's take a look at using the InvokePoweShell function. I'll use this function to take a SCVMM host out of placement. I will pass into this function a string for the host name, and a boolean value to set the host available and not available. The function will return true, if it succeeds and false if there is a problem:
public bool SetHostAvailabeForPlacement(string HostName, boolean val)
{
bool retval = true;
string psscript = "$MyHost = Get-VMHost -VMMServer localhost -ComputerName '" + HostName + "'\r\n Set-VMHost -VMHost $MyHost -AvailableForPlacement $" + val;
try
{
Collection<PSObject> results = InvokePowerShell(psscript);
}
catch
{
return false;
}
return retval;
}
You will notice that in my powershell script, I didn't use the Get-VMMServer function instead I just told the cmdlet which server to use. I also could have made this powershell a little cleaner by making the cmdlet call in one line:
string psscript = "$MyHost = Get-VMHost -VMMServer localhost -ComputerName '" + HostName + "' | Set-VMHost -AvailableForPlacement $" + val;
In this example I'm not looking for any data back so I don't check the results PSObject. See my other blog posts for examples of this.
The last thing I want to demonstrate in this blog is how to use powershell that can be called from C# where you don't need to recompile your code to change the powershell. What I do is read the contents of an external text file into a string. I then parse that string replacing place holders with the values that I need.
public void CallPSFromFile(string FileName)
{
StreamReader sr = new StreamReader(FileName);
string psscript = sr.ReadToEnd();
sr.Close();
string VMName = "MyTestVM";
psscript.Replace("__VMName__", VMName);
Collection<PSObject> results = InvokePowerShell(psscript);
}
This is a very simple function, and in the real world, you will want to make sure that your file read process doesn't error out. The StreamReader is the System.IO namespace, add a Using statement to add it to your code.
This function will read in the text from the text file and then replace any occurances of "__VMName__" with my value. I add the underscores to my replacement text to make them easy to find.
Here's a sample text file to remove a VM that can be used with the above sample:
$MyVMs = @(Get-VM -VMMServer VMMServer1.Contoso.com | where { $_.Name -Match "__VMName__" } )
$MyVMs | Remove-VM
As always, enjoy!