共用方式為


Working with SCVMM Powershell in C# - InvokePowerShell function

In may last post I used a function to allow C# to talk to SCVMM via powershell. This blog will demonstrate and show you how to create your own InvokePowerShell functions, and I'll show you a way you can use this function with external powershell scripts so you can modify the powershell in your application without having to recompile the C# code.

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:

private Collection<PSObject> InvokePowerShell(string psscript)

{

RunspaceConfiguration rconfig = RunspaceConfiguration.Create();PSSnapInException Pwarn = new PSSnapInException();

rconfig.AddPSSnapIn("Microsoft.SystemCenter.VirtualMachineManager", out Pwarn);

       Runspace runspace = RunspaceFactory.CreateRunspace(rconfig);

       runspace.Open();

       Pipeline pipeline = runspace.CreatePipeline();

       pipeline.Commands.AddScript(psscript);

       Collection<PSObject> results = new Collection<PSObject>();

       try

       {

       results = pipeline.Invoke();

       }

       catch(Exception ex)

  {
}
      
finally
       {
             runspace.Close();
             runspace.Dispose();
       }
}

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!

Comments