I have a .NET function which:
I have not been able to figure out how to just directly run a command on the Exchange server (via powershell) and then receive the response back - I have only been able to do it with this rather roundabout way.
How can I remotely run a Powershell command on Exchange and receive the response back in .NET?
This is my function:
protected string Receive_Exchange(string lscript)
{
//create credentials + encrypt them
string runasUsername = loginDetails.Username;
string runasPassword = loginDetails.Password;
SecureString ssRunasPassword = new SecureString();
foreach (char x in runasPassword)
{
ssRunasPassword.AppendChar(x);
}
// bind to PS credentials Object
PSCredential credentials =
new PSCredential(runasUsername, ssRunasPassword);
// prepare the connection to Remote server (Exchange 2010)
var connInfo = new WSManConnectionInfo(
new Uri("http://exchangeServer.domain.local/PowerShell"),
"http://schemas.microsoft.com/powershell/Microsoft.Exchange",
credentials);
object psSessionConnection;
//need .default in order to send it (not .basic)
connInfo.AuthenticationMechanism = AuthenticationMechanism.Default;
//Define/set executionPolicy
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ExecutionPolicy = ExecutionPolicy.RemoteSigned;
//open runspace
Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
runspace.Open();
PowerShell powershell = PowerShell.Create();
//send the remote connection request to exchange
var command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri("http://exchangeServer.domain.local/PowerShell"));
command.AddParameter("Authentication", "Kerberos");
powershell.Commands = command;
powershell.Runspace = runspace;
var result = powershell.Invoke();
psSessionConnection = result[0];
//start to build command to import exchange connection to localhost
var command1 = new PSCommand();
command1.AddCommand("Import-PSSession");
command1.AddParameter("Session", psSessionConnection);
powershell.Commands = command1;
powershell.Runspace = runspace;
//after importing connection, actually run the script
powershell.AddScript(lscript);
var result22 = powershell.Invoke();
runspace.Close();
string json = JsonConvert.SerializeObject(result22, Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
return json;
}
It is a behemoth and it is inefficient causing my entire app to slow down.
I love that it works but I hate how it works - please help me in making it run in a more straightforward way.
So thanks to the comments, they were very helpful.
I have bad news, since April 2021 Microsoft introduced an update that makes all Runspaces' run in NoLanguageMode
please see the link for more detail.
Another project with lots of details/different methods to connect to Exchange in powershell:
So the correct way is to indeed:
I therefore was not able to really change the functionality of my function, but to make it neater and more readable I broke it into three parts:
To prepare the powershell session, defining the Method (microsoft.exchange) the Uri (exchange.domain.local/powershell) and the credential (credentials)
private PSCommand NewPSSession()
{
//grab credentials from Model + encrypt them
string runasUsername = loginDetails.Username;
string runasPassword = loginDetails.Password;
SecureString ssRunasPassword = new SecureString();
foreach (char x in runasPassword)
{
ssRunasPassword.AppendChar(x);
}
// bind to PS cred Obj
PSCredential credentials =
new PSCredential(runasUsername, ssRunasPassword);
//create the PSCommand to connect to exchangeserver
PSCommand command = new PSCommand();
command.AddCommand("New-PSSession");
command.AddParameter("ConfigurationName", "Microsoft.Exchange");
command.AddParameter("ConnectionUri", new Uri("http://exchange.domain.local/PowerShell"));
command.AddParameter("Credential", credentials);
return command;
}
Prepare the import command, using the result returned from NewPSSession()
private PSCommand ImportSession(Collection<PSObject> result)
{
PSCommand command = new PSCommand();
command.AddCommand("Import-PSSession");
command.AddParameter("Session", result[0]);
return command;
}
Finally the function to combine it all together and call the script
protected string Receive_Exchange(string script)
{
//Define sessionState for local Powershell + set executionPolicy
InitialSessionState initialSessionState = InitialSessionState.CreateDefault();
initialSessionState.ExecutionPolicy = ExecutionPolicy.RemoteSigned;
//Define runspace passing sessionState
Runspace runspace = RunspaceFactory.CreateRunspace(initialSessionState);
//create powershell, binding runspace to it
PowerShell powershell = PowerShell.Create(runspace);
runspace.Open();
// first function: defining the exchange PSSession
powershell.Commands = NewPSSession();
var result = powershell.Invoke();
//Import the session using 2nd function
powershell.Commands = ImportSession(result);
//after importing connection, actually run the script
powershell.AddScript(script);
try
{
var result2 = powershell.Invoke();
runspace.Close();
string json = JsonConvert.SerializeObject(result2, Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
});
return json;
} catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return ex.ToString();
}
}