在C#中连接到Microsoft Exchange PowerShell

我正在尝试从C#.NET WinForms应用程序连接到远程PowerShell。 我的目标是创建自己的Microsoft PowerShell ISE版本。 所以我需要一种从远程计算机上的应用程序执行PowerShell脚本的方法。 我已经创建了几种方法,并在我的应用程序中在本地计算机上进行了测试。 如果我不使用WSManConnectionInfo并使用(Runspace remoteRunspace = RunspaceFactory.CreateRunspace())我可以在本地执行脚本,就好像它是真的powershell(小脚本,变量的使用,输出数据使用ftfl ,做了很多我通常使用powershell做的其他事情。当我添加WSManConnectionInfo并将其指向我的Exchange Server而不是使用本地连接时,问题就出现了。它似乎能够执行像“get-mailbox”这样的基本内容但是只要我尝试管道东西,使用一些脚本function,如$ variables,它打破说它不受支持。

同样,我必须禁用powershell.AddCommand(“out-string”); 什么时候不在本地使用它。

System.Management.Automation.dll中发生了未处理的“System.Management.Automation.RemoteException”类型exception。

附加信息:术语“Out-String”不被识别为cmdlet,函数,脚本文件或可操作程序的名称。 检查名称的拼写,或者如果包含路径,请validation路径是否正确,然后重试。

如果我不强制远程连接但只是在本地执行,则不会出现相同的错误。 似乎SchemaUri只对执行基本命令非常严格。 我看到了其他使用非常直接信息的人的例子:

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

但是通过这种方法,我必须定义许多可能的选项,我不想通过定义参数和其他东西。 我只想加载“脚本”并像在PowerShell窗口中一样执行它。这是我现在使用的一个例子。

  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 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(); } 

关于为什么会发生这种情况的任何建议,并解决如何让它以相同的方式运行? 我见过很多像这样的博客,但定义每个简单的命令对我来说都没有意义。 我还看到了创建本地连接然后在其中执行远程连接的选项,但这必须是最后的手段,因为它依赖于多个其他因素。

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

Exchange 2010通过PowerShell提供的管理体验已从Local更改为Remote。 […]只有交换cmdlet才能在此远程处理方案中运行, 您将无法运行大多数PowerShell cmdlet 。 […]是的,这意味着您将无法在远程运行空间中运行Where-Object和.PS1脚本等cmdlet

这是一个限制吗? 我不这么认为。 我们可以通过创建一个新的Session并导入它来轻松解决它


所以你需要做这样的事情

 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 result = powershell.Invoke(); 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's local one 

从Exchange Server 2010开始,您需要使用远程PowerShell会话,而不是直接添加交换PowerShell管理单元(由于在Exchange 2010中使用RBAC而不是ACL)。 因此,您需要创建新的PowerShell会话(使用New-PSSession),然后导入它(使用Import-PSSession)。 您可以使用这样的代码来执行远程PowerShell命令:

 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 results = pipeline.Invoke(); runspace.Close(); StringBuilder sb = new StringBuilder(); if (pipeline.Error != null && pipeline.Error.Count > 0) { // Read errors succeeded = false; Collection 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(); } 

看起来你遇到了双跳认证问题,我在尝试时遇到了同样的问题。

搞乱之后,我最终在本地安装了Exchange Powershell插件,并使用它们远程连接到Exchange服务器。

粗略的例子:

  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 }