Powershell小技巧之使用WS-Man来调用PowerShell命令
虽然powershell远程管理被构建在 ws-management的之上,但它是协议中的协议。如果尝试使用 psrp (powershell远程处理协议)直接进行交互,本质上需要在客户端机器上运行一个powershell副本。另一种方法是使用一个鲜为人知的远程命令行工具,称为winrs。winrs是一个简单的工具,允许远程cmd.exe,它也是构建在ws-management之上的。所不同的是winrs重用了 ws-transfer中的create和delete,并引入了一些新的自定义的soap web-methods。本文中,我将重点放在winrs“协议”,不会讨论 ws-transfer,soap,http等细节。关于winrs,ws-management的一些详细文档可以参考:[ms-wsmv]: web services management protocol extensions for windows vista。
winrs具有相对简单的协议,工作流程为:
ws-transfer创建一个shell,一个epr(端点引用)。创建的shell会被返回,用于接下来的一系列操作。
调用命令的自定义soap动作,开始一个新的命令
调用自定义的soap接受动作,来接收命令输出(发送输入时有相应的send命令,但不是该场景必须的)
重复步骤3,直到commandstate完成
ws-transfer来删除shell上的端点引用。
让我们较为详细地浏览每个步骤吧:
对于ws-transfer create soap消息,body中应当包含你要发送或者接受的流,资源的uri应当为:.
所以本质上我们创建了一个cmd.exe shell用来运行powershell。
<shell xmlns='http://schemas.microsoft.com/wbem/wsman/1/windows/shell'>
<inputstreams>stdin</inputstreams>
<outputstreams>stdout stderr</outputstreams>
</shell>
如果请求成功,你会接受到一个标准的ws-transfer create soap响应,它包含了一个刚才创建的类似的shell epr:
<w:selectorset>
<w:selector name="shellid">afcfb065-f551-4604-bfdfd9b706798b5d</w:selector>
</w:selectorset>
这个epr应该缓存的所有后续操作。第一个自定义soap动作命令使用动作uri:http://schemas.microsoft.com/wbem/wsman/1/windows/shell/command。 winrs支持两种控制台模式:交互式和批处理。对于一个交互式会话,winrs将等待输入(即使命令已经完成),直到客户端指示没有更多。对于一个批处理会话,winrs期望只在运行命令的生命周期有输入被发送。对于此场景,指定的ws-management选项winrs_consolemode_stdin为true来意味正在使用批处理模式非常重要。命令行被分成单独的命令和参数。soap片段像这样:
…
<w:optionset>
<w:option name='winrs_consolemode_stdin'>true</w:option>
</w:optionset>
</s:header>
<s:body>
<commandline xmlns='http://schemas.microsoft.com/wbem/wsman/1/windows/shell'>
<command>powershell</command>
<arguments>get-service | format-csv </arguments>
</commandline>
</s:body>
如果这个请求是成功的,该响应将包含一个 commandid元素,应当会被缓存在body中,用于后续操作来接收输出。虽然该协议被定义为允许一个shell来托管多个命令,但是winrs被限制了每个shell只能处理单个命令。类似的响应例子如下:
<rsp:commandresponse>
<rsp:commandid>772b44df-2ea2-4aa5-87d1-a07e1fae7a4e</rsp:commandid>
</rsp:commandresponse>
一旦接收到命令的响应,该命令在服务器上运行。 一旦数据量达到了最大值,winrs将阻止输出(当然也包括命令)。自定义soap动作,接收使用操作uri。自定义soap使用动作uri:。因为所产生的输出可能会超过soap请求大小,客户端需要指定一个递增sequenceid防止数据包丢失。 winrs只会缓存最后发送的数据包。请求应当包含你想读取的数据流,commandid也会关联body中的数据流。
<receive sequenceid='0'
xmlns='http://schemas.microsoft.com/wbem/wsman/1/windows/shell'>
<desiredstream commandid='772b44df-2ea2-4aa5-87d1-a07e1fae7a4e'>
stdout stderr
</desiredstream>
</receive>
响应将包含base64流编码的文本输出(保持soap xml格式良好和有效)。客户端应检查命令的状态,以了解是否继续以调用接收更多的输出。
<rsp:receiveresponse>
<rsp:stream name="stdout" commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e">dqo=</rsp:stream>
<rsp:stream name="stdout" commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e">
u3rhdhvzicagtmftzsagicagicagicagicagierpc3bsyxloyw1licagicagicagicagicagicagicagicagicag</rsp:stream>
<rsp:stream name="stdout" commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e">
dqotls0tls0gicatls0ticagicagicagicagicagls0tls0tls0tls0gicagicagicagicagicagicagicagicagicanclj1bm5pbmcgih
dpbm1nbxqgicagicagicagicbxaw5kb3dzie1hbmfnzw1lbnqgsw5zdhj1bwvudgf0aw9uicagia0kdqoncg==</rsp:stream>
<rsp:stream name="stdout" commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e" end="true"></rsp:stream>
<rsp:stream name="stderr" commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e" end="true"></rsp:stream>
<rsp:commandstate commandid="772b44df-2ea2-4aa5-87d1-a07e1fae7a4e"
state="http://schemas.microsoft.com/wbem/wsman/1/windows/shell/commandstate/done">
<rsp:exitcode>0</rsp:exitcode>
</rsp:commandstate>
</rsp:receiveresponse>
一旦commandstate为“done”,会没有更多的输出,并且ws-transfer delete 会在shelll epr上被调用。这将会清理服务器上正在使用的资源。
该示例代码展示如何调用一个powershell 命令。它不使用任何winrm的api,而是从模板创建必要的soap消息,并使用system.net.httpwebrequest将其通过网络发送。为了使用windows中的示例代码,您需要启用winrm服务配置基本身份验证(只适用于本地账号),您可以以管理员权限运行此powershell命令:
winrspsh user password "get-service"
如果你想让输出更加规范一点可以转换为为xml((.net serialization):
winrspsh user password "(get-service ^| convertto-xml).outerxml"
注意上面的例子中,你必须把管道字符转义,这样cmd.exe就不会去解释它了。
推荐阅读
-
PowerShell中使用Get-ChildItem命令读取目录、文件列表使用例子和小技巧
-
Powershell小技巧之使用WMI查询插上的U盘
-
Powershell小技巧之使用Copy-Item添加程序到开机启动
-
Powershell小技巧之使用-F方法带入数据
-
Powershell小技巧之使用WMI测试服务响应
-
Powershell小技巧之使用Jint引擎在PowerShell中执行Javascript函数
-
Powershell小技巧之使用Get-ChildItem得到指定扩展名文件
-
PowerShell小技巧之同时使用可选强制参数
-
PowerShell小技巧之使用Verb打开程序
-
PowerShell小技巧之使用Hotmail账号发送邮件