欢迎光临 Enjoy IT (ITECN.NET) 登录 | 注册 | 帮助

Powershell学习笔记(8):在PowerShell中使用WinRM

本文在ITECN首发,未经许可严禁转载!

  不知诸位是否还记得我在笔记3中提到的和WMI相关的内容,作为系统管理员手中的利器,WMI给我们带来了莫大的帮助,然而现实中我们有时无法随心所欲的使用WMI,其中的一个原因就是防火墙。

 

  因为经历了这几年的蠕虫病毒冲击后,WMI使用的端口很有可能被防火墙阻塞掉。那么,我们就只能这样束手无策,缴械投降了么?也许以前是这样的,但是当WinRM出现后,我们又多了一种选择。

  让我先来对WinRM啰嗦几句。WinRMWindows远程管理是“WS 管理协议的 Microsoft 实施,该协议是基于标准 SOAP、不受防火墙影响的协议,允许不同供应商的硬件和操作系统相互操作。”(这段文字引用自11月的Technet杂志,点击前往)该协议使用80或者443端口来进行通信,王希老大在

Windows Vista核心技术系列之十三:Windows Vista管理新技术

这个webcast中有介绍过WinRM,我在此就不再赘述。下面要介绍的是我的测试环境。

  我的客户端采用的是Vista,服务端是Windows Server 2003 R2,都是独立的工作站,这样的选择的原因仅仅是出于我自身机器性能的限制,在Vista和Windows Server 2008中WinRM都是内置的而且有相应的组策略可以调整设置,而Windows Server 2003 R2需要单独下载安装,大家可以去Microsoft下载中心搜索winrm然后下载安装。说完这些,我们开始对WinRM进行适当的配置,首先是服务端。

  我们可以在命令行下输入“winrm qc”来进行快速配置

 

然后是客户端,我们需要修改TrustedHosts属性,否则会因为身份验证问题而失败。(理由在错误提示中已经写的很清楚了)

  如果想要向TrustedHosts中添加值,那么只需运行以下命令(在提升权限的命令行下)

winrm set winrm/config/client @{TrustedHosts="192.168.1.10"}

192.168.1.10是服务端的IP

  添加完毕后,我们再测试下服务端的WinRM是否已经运行(使用winrm id 命令,使用方法可以查阅WinRM帮助)

 

  至此,准备工作可以说是告一段落了,下面我们就开始PowerShell脚本的编写。

  当然,凭空是写不出代码的,我们需要参阅相关的文档。

一份是MSDN文档,http://msdn2.microsoft.com/en-us/library/aa384423.aspx

,里面包含了VBS代码,还有一份就是我前面提到的11月份的Technet杂志上的文章,那篇文章介绍了下使用VBS来实现WinRM。有了这两份文档,我们就可以写出PowerShell脚本了,实质上就是做一些代码转换的工作。

  我们先声明代表我们要访问的那台计算机名称(或者FQDNIP)的变量,代码如下:

$computer="192.168.1.10"

  然后我们创建代表WinRMcom对象,代码如下:

$winrm = New-Object -ComObject Wsman.Automation

 

  接下来我们开始准备使用CreateSession来创建会话,不过在使用这个方法之前,我们需要为这个方法准备三个参数,分别是,URI,远程连接需要的验证方法参数及为远程连接准备的用户名和密码。 

  URI比较方便 只要用"http://"加上$computer变量即可。而远程连接需要的验证方法参数,因为我们会使用用户名和密码所以用$winrm.SessionFlagCredUsernamePassword()方法来获得该参数

  下面就剩用户名和密码了,按照文档提示,我们也许只需要这么做就行了(下面代码是经过vbspowershell转换的)

$objConnectionOptions = $objWsman.CreateConnectionOptions()

$objConnectionOptions.UserName = "Username"

$objConnectionOptions.Password = "Password"

  可是事实上没有我们想得那么简单,Password给我们出了一道不大不小的难题,请注意观察下图

  Username给出Definitionstring,而Password则没有给出定义,因此我们直接用$objConnectionOptions.Password = "Password" 赋值语句会遇到麻烦。

  赋值语句将会造成一个类型不匹配错误然后提示你输入密码,当正确的密码输入后,也能得到相关信息,我们是设置一个$ErrorActionPreference = "SilentlyContinue"就此放任不管,还是要处理这个问题?

  当然,我们要处理这个问题,因为管理员输入了两次密码(第一次是使用Get-Credential cmdlet触发的),这不是一个好的用户体验。

  这里我们要再次感谢MVP MOW他为我们写了一个function来解决这个问题,而这个function实际上使用了VBS代码。(具体代码见最后整体代码部分)

  然后我们就可以使用CreateSession方法创建会话了,代码如下:

$session = $winrm.CreateSession("http://"+$computer,$flag,$objConnectionOptions)

  解决了这个头痛的password问题,下面我们就该对我们需要的信息进行枚举了。这里我们需要一个代表我们所需要的WMI类的资源URI,就是形如以下的内容

$strRes = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process"

 

  关于这个URI我们只需关注wmi/后的部分,而如果你只对Win32*WMI类感兴趣,那么只需关注最后一部分即可,想要获得你需要的信息只需要修改这里即可。

  接下来我们使用$session.enumerate方法来枚举我们需要的信息,代码如下:

$results = $session.enumerate($strRes)

  下面我们尝试用$results.ReadItem()来读取一项内容,返回结果如下:

 

  仔细观察下,我们不能发现,这是XML格式的文本,接下来就是如何处理这些文本的问题了。

  首先我们用[xml]将这些文字格式化成XMLDocument,然后我们用([xml]$results.ReadItem()) | gm看下这个对象的成员

 

  我们不难发现整个对象只有最后一个Win32_Process是我们在意的内容,那么接下来我们看看这个属性里有什么

  具体命令是这个:([xml]$results.ReadItem()).Win32_Process

 

  这个格式很接近我们用Get-WMIObject所看到的东西了,剩下的就是目前我们只是获得一个进程,我该如何获得全部进程呢?注意看那张XML格式文本的截图的第一行,AtEndOfStream属性,这是一个布尔值,如果熟悉用VBS处理文本文件的朋友很快就能反应过来,这个属性可以用在循环上,然后我们使用脚本块(&{})将循环结果作为一个整体赋值给变量(这部分代码见完整代码示例)

 

  那么至此,我们的脚本就算大功告成了,下面给出完整代码,最后一行的格式化输出想必不用我多解释了

Function Get-WinRmCredentials([Management.Automation.PSCredential]$cred = (Get-Credential)){

$vbs = new-object -com MSScriptControl.ScriptControl

$vbs.language = 'vbscript'

$VBS.ExecuteStatement('Set objWsman = CreateObject( "WSMAN.Automation" )')

$VBS.ExecuteStatement('Set objOptions = objWSMan.CreateConnectionOptions')

$VBS.ExecuteStatement("objOptions.userName = ""$($cred.GetNetworkCredential().username)""")

$VBS.ExecuteStatement("objOptions.Password = ""$($cred.GetNetworkCredential().Password)""")

$VBS.eval('objOptions')

} #Powered by MOW

 

$computer = "192.168.1.10"

$strRes = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Process"

 

$winrm = New-Object -ComObject Wsman.Automation

$flag = $winrm.SessionFlagCredUsernamePassword()

$objConnectionOptions = Get-WinRmCredentials

 

$session = $winrm.CreateSession("http://"+$computer,$flag,$objConnectionOptions)

$results = $session.Enumerate($strRes)

$services = &{do {

([xml]$results.ReadItem()).Win32_Process

}

until ($results.AtEndOfStream)}

 

$services | ft CSName,ProcessID,Name,CommandLine -a

运行结果

 

  这样我们就完成了通过PowerShell来使用WinRM的任务,也许有点麻烦,因为在PowerShellV1版本中远程访问功能不是很强,而在不久后的V2版本中(下周会有V2的CTP版本),这个功能会大大增强,让我们期待V2的发布吧!
已发表 2007年11月4日 14:42 作者 ghjconan
归档在:

评论

禁止匿名发表评论