在 C# 中启用远程 PowerShell 执行
讨论在 PowerShell 控制台中远程执行 PowerShell 脚本;还描述了如何在 C# 代码中执行相同的操作。
引言
本文介绍了如何在 PowerShell 控制台中启用远程执行 PowerShell 脚本。它还描述了如何在 C# 代码中本地或远程执行 PowerShell 脚本。
所有介绍的方法都基于我的实际工作,我的目的是通过在 Windows 7 机器上执行 PowerShell 脚本来管理远程 Windows Server 2008 R2 服务器。我将远程 Windows Server 2008 R2 服务器称为远程主机
,将 Windows 7 机器称为客户端
。
该示例代码展示了如何本地或远程执行 PowerShell 命令。在远程执行 PowerShell 命令之前,您必须先在远程主机和本地客户端上启用 PS remoting。
启用 PowerShell 远程
为了启用远程 PowerShell 执行,我执行了以下步骤:
- 在
远程主机
和本地
客户端
上安装PowerShell 4.0。4.0 不是强制的。2.0+ 即可。实际上,Windows Server 2008 R2 和 Windows 7 都默认安装了 PowerShell 2.0。 - 将我的 Windows 帐户添加到
远程主机
和本地客户端
的管理员组。如果两台机器都在同一域中,您可以使用您的域帐户;如果都在 WORKGROUP 中,您可以在每台机器上创建一个同名同密码的帐户,并都将其添加到管理员组。 - 在
远程主机
上运行以下命令:winrm quickconfig。此命令会自动分析和配置 WinRM 服务,这是远程 PowerShell 执行的核心服务。
winrm quickconfig 命令仅用于实验环境的快速设置。对于生产环境,您应根据[2] 和[3] 仔细配置权限和防火墙。
请注意,在执行 winrm quickconfig 或其他启用 PowerShell remoting 的 cmdlet 时,您可能会遇到几个问题。您可以参考[4] 查找最常见的三种问题的解决方案。
在 C# 中执行 PowerShell 脚本
要在 C# 代码中执行 PowerShell 命令,您需要引用项目中的“C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll”。注意:如果您的本地 PowerShell 版本低于 3.0,System.Management.Automation.dll 可能在不同的文件夹中。
您可能需要使用以下两个命名空间
using System.Management.Automation;
using System.Management.Automation.Runspaces;
您可以直接使用PowerShell
实例,但更好的方法是创建一个Runspace
实例。每个PowerShell
实例都在一个Runspace
中工作。您可以拥有多个 Runspace 来同时连接到不同的远程主机。
以下代码段展示了如何创建一个本地Runspace
并通过PowerShell
实例执行命令:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript("Get-Process");
var results = ps.Invoke();
// Do something with result ...
}
runspace.Close();
以下代码演示了如何远程执行命令
WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.ComputerName = machineAddress;
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript("Get-Service");
var results = ps.Invoke();
// Do something with result ...
}
runspace.Close();
注意:Runspace
和PowerShell
类都实现了IDisposable
。因此,在使用完它们之后,请不要忘记关闭或释放它们的实例。
Runspace
和PowerShell
的大多数重要方法都有同步和异步形式,例如Runspace.Open
和OpenAsync
,Runspace.Close
和CloseAsync
,PowerShell.Invoke
和BeginInvoke
等。
在 C# 中解析结果
PowerShell.Invoke
的返回值类型是Collection<PSObject>
。每个PSObject
实例反映了实际对象属性和方法,以键值对的形式存储在PSObject.Members
中。您还可以使用PSObject.Properties
访问实例属性,使用PSObject.Methods
访问实例方法。注意:如果命令是在本地执行的,PSObject.BaseObject
可能是实际对象。
让我们以Get-Process
命令为例。当您像前面的代码段所示使用ps.Invoke()
执行Get-Process
时,您将获得一个Collection<PSObject>
。每个PSObject
实例代表一个进程。然后您可以使用以下代码检索进程成员:
// Execute Get-Process and get results ...
foreach (var result in results)
{
Console.WriteLine(result.Members["Id"].Value);
Console.WriteLine(result.Members["ProcessName"].Value);
Console.WriteLine(result.Members["PrivateMemorySize64"].Value);
result.Methods["Kill"].Invoke();
}
// Clean up code ...
上面的代码段适用于本地和远程场景。 Value
是Object
类型。它可能是一个装箱的值类型。您可以调用进程的Kill
方法,如果有适当的权限,它将成功。
如果您的代码在本地执行,您可以将PSObject.BaseObject
强制转换为确切的类型实例,如下面的代码段所示:
// Code to execute Get-Process and get results ...
foreach (var result in results)
{
var process = (System.Diagnostics.Process)result.BaseObject;
Console.WriteLine(process.Id);
Console.WriteLine(process.ProcessName);
Console.WriteLine(process.PrivateMemorySize64);
process.Kill();
}
// Clean up code ...
如果您通过PowerShell.AddScript
和PowerShell.Invoke
(或BeginInvoke
)执行一个大的 PS 脚本,返回值将仅包含脚本中最后一个执行的命令的结果。
更多详细信息,请参阅[1]。
参考文献
大部分知识可以在以下链接中找到