Connect to Microsoft Exchange PowerShell inside C #

I am trying to connect to a remote powershell file from a C # .NET WinForms application. My goal is to create my own version of Microsoft PowerShell ISE. Therefore, I need a way to execute PowerShell scripts from my application on remote computers. I created a couple of methods and tested them on the local machine from my application. If I do not use WSManConnectionInfo and use using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace ()) , I can execute scripts locally, as if it were true powershell (small scripts, using variables, output using ft , fl , do a lot other things that I usually do with PowerShell. The problem starts when I add WSManConnectionInfo and point it to my Exchange server instead of using a local connection. It seems to be able to do basic things like get-mailbox, but as soon as I'm trying to transfer data using use some of the scripting features, for example, variables that it breaks, saying that they are not supported.

Similarly, I need to disable powershell.AddCommand ("out-string"); if it is not used locally.

An unhandled exception of type "System.Management.Automation.RemoteException" occurred in System.Management.Automation.dll.

Additional information: the term "Out-String" is not recognized as the name of a cmdlet, function, script file or executable program. Check the spelling of the name or if the path was included, check the path and try again.

The error itself does not appear if I cannot forcibly delete the connection, but just do it locally. SchemaUri seems to make it very strict to execute only the basic commands. I saw other examples in which people using very direct information, such as:

powershell.AddCommand("Get-Users"); powershell.AddParameter("ResultSize", count); 

But with this approach, I would need to identify many possible options, and I do not want to go through the defining parameters and other things. I just wanted to load the "script" and execute it the same way as in the PowerShell window. Here is an example of what I am using now.

  public static WSManConnectionInfo PowerShellConnectionInformation(string serverUrl, PSCredential psCredentials) { var connectionInfo = new WSManConnectionInfo(new Uri(serverUrl), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", psCredentials); //var connectionInfo = new WSManConnectionInfo(new Uri(serverUrl), "http://schemas.microsoft.com/powershell", psCredentials); connectionInfo.AuthenticationMechanism = AuthenticationMechanism.Basic; connectionInfo.SkipCACheck = true; connectionInfo.SkipCNCheck = true; connectionInfo.SkipRevocationCheck = true; connectionInfo.MaximumConnectionRedirectionCount = 5; connectionInfo.OperationTimeout = 150000; return connectionInfo; } public static PSCredential SecurePassword(string login, string password) { SecureString ssLoginPassword = new SecureString(); foreach (char x in password) { ssLoginPassword.AppendChar(x); } return new PSCredential(login, ssLoginPassword); } public static string RunScriptPs(WSManConnectionInfo connectionInfo, string scriptText) { StringBuilder stringBuilder = new StringBuilder(); // Create a remote runspace using the connection information. //using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace()) using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo)) { // Establish the connection by calling the Open() method to open the runspace. // The OpenTimeout value set previously will be applied while establishing // the connection. Establishing a remote connection involves sending and // receiving some data, so the OperationTimeout will also play a role in this process. remoteRunspace.Open(); // Create a PowerShell object to run commands in the remote runspace. using (PowerShell powershell = PowerShell.Create()) { powershell.Runspace = remoteRunspace; powershell.AddScript(scriptText); //powershell.AddCommand("out-string"); powershell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); Collection<PSObject> results = powershell.Invoke(); foreach (PSObject result in results) { stringBuilder.AppendLine(result.ToString()); } } // Close the connection. Call the Close() method to close the remote // runspace. The Dispose() method (called by using primitive) will call // the Close() method if it is not already called. remoteRunspace.Close(); } // convert the script result into a single string return stringBuilder.ToString(); } 

Any advice on why this is happening and the workaround is how to make it behave the same? I have seen many blogs, for example, but defining each simple command does not make sense to me. I also saw the possibility of creating a local connection and then making a remote connection inside this , but this should be a last resort, as it relies on several other factors.

+8
c # powershell exchange-server
source share
3 answers

Check out https://blogs.msdn.microsoft.com/akashb/2010/03/25/how-to-migrating-exchange-2007-powershell-managed-code-to-work-with-exchange-2010/ :

The management experience provided by Exchange 2010 through PowerShell has been moved completely from Local to Remote. [...] Only exchange cmdlets will work in this remote script; you cannot run most powershell cmdlets . [...] Yes, this means that you cannot run cmdlets such as Where-Object and .PS1 scripts in a remote workspace .

Is this a limitation? I do not think so. We can easily get around it by creating a new session and importing it .


So you need to do something like this:

 PSCredential creds = new PSCredential(userName, securePassword); System.Uri uri = new Uri("http://Exchange-Server/powershell?serializationLevel=Full"); Runspace runspace = RunspaceFactory.CreateRunspace(); PowerShell powershell = PowerShell.Create(); PSCommand command = new PSCommand(); command.AddCommand("New-PSSession"); command.AddParameter("ConfigurationName", "Microsoft.Exchange"); command.AddParameter("ConnectionUri", uri); command.AddParameter("Credential", creds); command.AddParameter("Authentication", "Default"); powershell.Commands = command; runspace.Open(); powershell.Runspace = runspace; Collection<PSSession> result = powershell.Invoke<PSSession>(); powershell = PowerShell.Create(); command = new PSCommand(); command.AddCommand("Set-Variable"); command.AddParameter("Name", "ra"); command.AddParameter("Value", result[0]); powershell.Commands = command; powershell.Runspace = runspace; powershell.Invoke(); powershell = PowerShell.Create(); command = new PSCommand(); command.AddScript("Import-PSSession -Session $ra"); powershell.Commands = command; powershell.Runspace = runspace; powershell.Invoke(); # now you can use remote PS like it local one 
+7
source share

Starting with Exchange Server 2010, you need to use a remote PowerShell session instead of directly adding a PowerShell sharing snapin (due to using RBAC instead of ACLs in exchange for 2010). Therefore, you need to create a new PowerShell session (using New-PSSession) and then import it (using Import-PSSession). You can use this code to execute PowerShell remote commands:

 void ExecutePowerShellUsingRemotimg() { RunspaceConfiguration runspaceConfig = RunspaceConfiguration.Create(); PSSnapInException snapInException = null; Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfig); runspace.Open(); Pipeline pipeline = runspace.CreatePipeline(); string serverFqdn = "FQDN of you server"; pipeline.Commands.AddScript(string.Format("$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://{0}/PowerShell/ -Authentication Kerberos", serverFqdn)); pipeline.Commands.AddScript("Import-PSSession $Session"); pipeline.Commands.AddScript("your PowerShell script text"); pipeline.Commands.Add("Out-String"); Collection<PSObject> results = pipeline.Invoke(); runspace.Close(); StringBuilder sb = new StringBuilder(); if (pipeline.Error != null && pipeline.Error.Count > 0) { // Read errors succeeded = false; Collection<object> errors = pipeline.Error.ReadToEnd(); foreach (object error in errors) sb.Append(error.ToString()); } else { // Read output foreach (PSObject obj in results) sb.Append(obj.ToString()); } runspace.Dispose(); pipeline.Dispose(); } 
+2
source share

It looks like you are facing double forwarding authentication issues, I had the same problems when trying this.

After the mess, I finished installing on-premises Exchange Powershell applications and used them to connect remotely to the Exchange server.

Example:

  RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create(); PSSnapInInfo info = runspaceConfiguration.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException); Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration); runspace.Open(); using (PowerShell powershell = PowerShell.Create()) { powershell.Runspace = runspace; string script = string.Format(@"Get-Mailbox -Server {0} -Identity {1}", serverName, identity); powershell.AddScript(script); powershell.Invoke(); // Do something with the output } 
0
source share

All Articles