欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Powershell小技巧之使用WS-Man来调用PowerShell命令

程序员文章站 2022-07-05 10:27:31
虽然powershell远程管理被构建在 ws-management的之上,但它是协议中的协议。如果尝试使用 psrp (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就不会去解释它了。