W Powershell 2.0 mamy możliwość zdalnego wykonywania poleceń. Powershell domyślnie też umożliwia odwołania z kodu C# do comandletów Powershellowych. Jednak połączenie remotingu Powershella oraz C# nie jest prostym zadaniem – brakuje po prostu przykładów. Nawet google jest w tym wypadku bezradny.
Po wnikliwej lekturze SDK do Powershell-a, możemy znaleźć klasę RunspaceConnectionInfo, ale niestety ta klasa ma prywatny konstruktor. Na szczęście jest klasa WSManConnection Info.
Jak więc zacząć? Zacznijmy od wymaganych klas:
using System.Collections.Generic;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
Przy czym odwołanie do System.Management.Automation; wymaga referencji do klasy System.Management.Automation.dll. Domyślnie klasa nie jest wrzucana do GAC, więc ręcznie musimy przeszukać katalog %WinDir% i ją sobie wyciągnąć.
Remote powershell ma ogrniczenie co do ilości sesji, więc musimy gdzieś przechowywać informacje o sesji. Tu na przykład przechowuję informację o tworzeniu sesji w globalnych zmiennych aplikacji:
/// <summary>
/// Opens remote runspace
/// </summary>
/// <param name="uri">Uri to connect to, for example https://pod51002psh.outlook.com/powershell/"</param>
/// <param name="schema">Schema of connection, for example http://schemas.microsoft.com/powershell/Microsoft.Exchange</param>
/// <param name="username">Username</param>
/// <param name="password">Secure pasword</param>
/// <returns></returns>
public void openRunspace(string uri, string schema, string username, string livePass)
{
System.Security.SecureString password = new System.Security.SecureString();
foreach (char c in livePass.ToCharArray())
{
password.AppendChar(c);
}
PSCredential psc = new PSCredential(username, password);
WSManConnectionInfo rri = new WSManConnectionInfo(new Uri(uri), schema, psc);
rri.AuthenticationMechanism = AuthenticationMechanism.Basic;
Runspace runspace = RunspaceFactory.CreateRunspace(rri);
runspace.Open();
Application["runspace"] = runspace;
}
A inicjalizacja takiej funkcji by wyglądała np tak:
protected void Application_Start(object sender, EventArgs e)
{
string psremoteserver = "https://pod51002psh.outlook.com/powershell/";
string liveLogin = "liveadmin@domain.fqdn";
string livePass = "Pass";
string schema = "http://schemas.microsoft.com/powershell/Microsoft.Exchange";
if (Application["runspace"] == null)
openRunspace(psremoteserver, schema, liveLogin, livePass);
}
Nie zapomnijmy o zamykaniu sesji:
protected void Application_End(object sender, EventArgs e)
{
if (Application["runspace"] != null)
{
Runspace rs = (Runspace)Application["runspace"];
rs.Close();
}
}
protected void Application_Error(object sender, EventArgs e)
{
if (Application["runspace"] != null)
{
Runspace rs = (Runspace)Application["runspace"];
rs.Close();
}
}
Po stworzeniu połączenia nie pozostaje nam nic innego jak go użyć. Np tak:
/// <summary>
/// Opens remote runspace
/// </summary>
/// <param name="uri">Uri to connect to, for example https://pod51002psh.outlook.com/powershell/"</param>
/// <param name="schema">Schema of connection, for example http://schemas.microsoft.com/powershell/Microsoft.Exchange</param>
/// <param name="username">Username</param>
/// <param name="password">Secure pasword</param>
/// <returns></returns>
public void openRunspace(string uri, string schema, string username, string livePass)
{
System.Security.SecureString password = new System.Security.SecureString();
foreach (char c in livePass.ToCharArray())
{
password.AppendChar(c);
}
PSCredential psc = new PSCredential(username, password);
WSManConnectionInfo rri = new WSManConnectionInfo(new Uri(uri), schema, psc);
rri.AuthenticationMechanism = AuthenticationMechanism.Basic;
Runspace runspace = RunspaceFactory.CreateRunspace(rri);
runspace.Open();
Application["runspace"] = runspace;
}
private Runspace getRunSpace()
{
Runspace runspace = (Runspace)Application["runspace"];
if (runspace.RunspaceStateInfo.State != RunspaceState.Opened)
runspace.Open();
return runspace;
}
/// <summary>
/// Invokes any powershell script
/// </summary>
/// <param name="scriptText">script to invoke</param>
/// <returns></returns>
public Collection<PSObject> RunScript(string scriptText)
{
Runspace runspace = getRunSpace();
// create a pipeline and feed it the script text
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
// execute the script
Collection<PSObject> results = new Collection<PSObject>();
results = pipeline.Invoke();
return results;
}
/// <summary>
/// Invoke simple command
/// Ex.
/// Collection<PSObject> col = RunCommand("Get-Mailbox");
/// </summary>
/// <param name="command">Command Name</param>
/// <returns></returns>
public Collection<PSObject> RunCommand(string command)
{
Command myCommand = new Command(command);
Pipeline pipeLine = getRunSpace().CreatePipeline();
pipeLine.Commands.Add(myCommand);
return pipeLine.Invoke();
}
/// <summary>
/// Invoke command with single paramtere
/// </summary>
/// <param name="command">Command</param>
/// <param name="param">Command parameter</param>
/// <returns></returns>
public Collection<PSObject> RunCommand(string command, string param)
{
Command myCommand = new Command(command);
myCommand.Parameters.Add(param);
Pipeline pipeLine = getRunSpace().CreatePipeline();
pipeLine.Commands.Add(myCommand);
return pipeLine.Invoke();
}
/// <summary>
/// Invoke command with single named parameter
/// Ex.
/// Collection<PSObject> col = RunCommand("Get-Mailbox","Identity","usr@iscg.eu");
/// </summary>
/// <param name="command">Command to invoke</param>
/// <param name="param">Parameter Name</param>
/// <param name="paramValue">Parameter value</param>
/// <returns></returns>
public Collection<PSObject> RunCommand(string command, string param, object paramValue)
{
Command myCommand = new Command(command);
myCommand.Parameters.Add(new CommandParameter(param, paramValue));
Pipeline pipeLine = getRunSpace().CreatePipeline();
pipeLine.Commands.Add(myCommand);
return pipeLine.Invoke();
}
/// <summary>
/// Invokes command with collection of paramters
/// Ex.
/// Collection<CommandParameter> cpc = new Collection<CommandParameter>();
/// cpc.Add(new CommandParameter("Identity", "user@iscg.eu"));
/// Collection<PSObject> col = RunCommand("Get-Mailbox",cpc);
/// </summary>
/// <param name="command"></param>
/// <param name="param"></param>
/// <returns></returns>
public Collection<PSObject> RunCommand(string command, Collection<CommandParameter> param)
{
Command myCommand = new Command(command);
foreach (CommandParameter cp in param)
myCommand.Parameters.Add(cp);
Pipeline pipeLine = getRunSpace().CreatePipeline();
pipeLine.Commands.Add(myCommand);
return pipeLine.Invoke();
}
public string translatePSObject(Collection<PSObject> input)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in (input))
{
stringBuilder.AppendLine(obj.ToString() + obj.GetType() + obj.ImmediateBaseObject.GetType());
}
return stringBuilder.ToString();
}