第 7 章 - 使用 WMI

WMI 和 CIM

默认情况下,Windows PowerShell 附带用于处理其他技术(如 Windows Management Instrumentation(WMI)的 cmdlet。 WMI cmdlet 已弃用,在 PowerShell 6+ 中不可用,但在 Windows PowerShell 上运行的较旧脚本中可能会遇到这些 cmdlet。 对于新开发,请改用 CIM cmdlet。

PowerShell 中存在多个本机 WMI cmdlet,且无需安装任何其他软件或模块。 Get-Command 可用于确定 Windows PowerShell 中存在哪些 WMI cmdlet。 以下结果来自运行 5.1 版 PowerShell 的 Windows 10 实验环境计算机。 结果因运行的 PowerShell 版本而异。

Get-Command -Noun WMI*
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Get-WmiObject                                      3.1.0.0    Microsof...
Cmdlet          Invoke-WmiMethod                                   3.1.0.0    Microsof...
Cmdlet          Register-WmiEvent                                  3.1.0.0    Microsof...
Cmdlet          Remove-WmiObject                                   3.1.0.0    Microsof...
Cmdlet          Set-WmiInstance                                    3.1.0.0    Microsof...

PowerShell 版本 3.0 中引入了通用信息模型 (CIM) cmdlet。 CIM cmdlet 的设计目的是使其可以同时在 Windows 和非 Windows 计算机上使用。

所有 CIM cmdlet 都包含在一个模块中。 若要获取 CIM cmdlet 的列表,请结合使用 Get-Command 与 Module 参数,如以下示例中所示。

Get-Command -Module CimCmdlets
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Cmdlet          Export-BinaryMiLog                                 1.0.0.0    CimCmdlets
Cmdlet          Get-CimAssociatedInstance                          1.0.0.0    CimCmdlets
Cmdlet          Get-CimClass                                       1.0.0.0    CimCmdlets
Cmdlet          Get-CimInstance                                    1.0.0.0    CimCmdlets
Cmdlet          Get-CimSession                                     1.0.0.0    CimCmdlets
Cmdlet          Import-BinaryMiLog                                 1.0.0.0    CimCmdlets
Cmdlet          Invoke-CimMethod                                   1.0.0.0    CimCmdlets
Cmdlet          New-CimInstance                                    1.0.0.0    CimCmdlets
Cmdlet          New-CimSession                                     1.0.0.0    CimCmdlets
Cmdlet          New-CimSessionOption                               1.0.0.0    CimCmdlets
Cmdlet          Register-CimIndicationEvent                        1.0.0.0    CimCmdlets
Cmdlet          Remove-CimInstance                                 1.0.0.0    CimCmdlets
Cmdlet          Remove-CimSession                                  1.0.0.0    CimCmdlets
Cmdlet          Set-CimInstance                                    1.0.0.0    CimCmdlets

如果使用 CIM cmdlet,仍可使用 WMI,因此当有人说出“当我使用 PowerShell CIM cmdlet 查询 WMI 时...”这样的话时,请不要感到疑惑

如前所述,WMI 是一项独立于 PowerShell 的技术,现在不过是使用 CIM cmdlet 来访问 WMI 而已。 你可能会发现一个旧的 VBScript,它使用 WMI 查询语言 (WQL) 来查询 WMI,如以下示例所示。

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

Set colBIOS = objWMIService.ExecQuery _
    ("Select * from Win32_BIOS")

For each objBIOS in colBIOS
    Wscript.Echo "Manufacturer: " & objBIOS.Manufacturer
    Wscript.Echo "Name: " & objBIOS.Name
    Wscript.Echo "Serial Number: " & objBIOS.SerialNumber
    Wscript.Echo "SMBIOS Version: " & objBIOS.SMBIOSBIOSVersion
    Wscript.Echo "Version: " & objBIOS.Version
Next

可以从该 VBScript 中获取 WQL 查询,并将其与 Get-CimInstance cmdlet 一起使用,而无需进行任何修改。

Get-CimInstance -Query 'Select * from Win32_BIOS'
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 3810-1995-1654-4615-2295-2755-89
Version           : VRTUAL - 4001628

这并不是我使用 PowerShell 查询 WMI 的常用方式。 但是该方法确实有效,通过该方法,可以轻松地将现有 VBScript 迁移到 PowerShell。 当我开始编写用于查询 WMI 的单项时,我使用以下语法。

Get-CimInstance -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 3810-1995-1654-4615-2295-2755-89
Version           : VRTUAL - 4001628

如果只需要序列号,则可以通过管道将输出传递给 Select-Object 并仅指定 SerialNumber 属性

Get-CimInstance -ClassName Win32_BIOS | Select-Object -Property SerialNumber
SerialNumber
------------
3810-1995-1654-4615-2295-2755-89

默认情况下,系统会在后台检索一些从不使用的属性。 在本地计算机上查询 WMI 时,这可能不会产生什么影响。 但是,如果开始查询远程计算机,前述检索不仅要花费更多的处理时间来返回相关信息,还会在网络中拉取不必要的额外信息。 Get-CimInstance 有一个可以限制检索到的信息的 Property 参数。 这会提升 WMI 查询的效率。

Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber |
Select-Object -Property SerialNumber
SerialNumber
------------
3810-1995-1654-4615-2295-2755-89

之前的结果返回了一个对象。 若要返回简单字符串,请使用 ExpandProperty 参数

Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber |
Select-Object -ExpandProperty SerialNumber
3810-1995-1654-4615-2295-2755-89

也可以使用点式语法返回简单字符串。 这样就无需通过管道传递到 Select-Object

(Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber).SerialNumber
3810-1995-1654-4615-2295-2755-89

使用 CIM cmdlet 查询远程计算机

我仍以作为域用户的本地管理员身份运行 PowerShell。 当我尝试使用 Get-CimInstance cmdlet 从远程计算机查询信息时,我收到一条“拒绝访问”的错误消息。

Get-CimInstance -ComputerName dc01 -ClassName Win32_BIOS
Get-CimInstance : Access is denied.
At line:1 char:1
+ Get-CimInstance -ComputerName dc01 -ClassName Win32_BIOS
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (root\cimv2:Win32_BIOS:String) [Get-CimI
   nstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80070005,Microsoft.Management.Infrastructure.Cim
   Cmdlets.GetCimInstanceCommand
    + PSComputerName        : dc01

在谈到 PowerShell 时,很多人都担心安全性,但事实是,你在 PowerShell 中拥有与在 GUI 中完全相同的权限。 不多不少,正好相符。 上一个示例中的问题是,运行 PowerShell 的用户不具有通过 DC01 服务器查询 WMI 信息的权限。 由于 Get-CimInstance 没有 Credential 参数,我能够以域管理员的身份重启 PowerShell。 但是,信任我,这不是一个好主意,因为我从 PowerShell 运行的任何内容都将作为域管理员运行。从安全的角度来看,这可能很危险,具体取决于情况。

基于最低特权原则,在每个命令的基础上,我使用 Credential 参数(如果命令包含该参数)提升到我的域管理员帐户Get-CimInstance 没有 Credential 参数,因此在这种情况下,解决方案是先创建一个 CimSession。 然后,使用 CimSession(而不是计算机名)查询远程计算机上的 WMI

$CimSession = New-CimSession -ComputerName dc01 -Credential (Get-Credential)
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

CIM 会话存储在名为 $CimSession 的变量中。 请注意,我还在括号中指定了 Get-Credential cmdlet,以便在创建新会话之前先执行该 cmdlet,提示我输入备用凭据。 本章的后面部分将介绍另一种更为有效的方式来指定备用凭据,但在深入介绍相关概念及更复杂的内容之前,请务必先了解这一基本概念。

现在可以将上一个示例中创建的 CIM 会话与 Get-CimInstance cmdlet 一起使用,从远程计算机上的 WMI 查询 BIOS 信息。

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 0986-6980-3916-0512-6608-8243-13
Version           : VRTUAL - 4001628
PSComputerName    : dc01

使用 CIM 会话还有其他好处,而不仅仅是指定计算机名。 在同一台计算机上运行多个查询时,为每个查询使用 CIM 会话比使用计算机名更有效。 创建 CIM 会话时只需建立一次连接。 然后,多个查询使用此同一会话检索信息。 如果使用计算机名,cmdlet 需要建立和断开与每个查询的连接。

Get-CimInstance cmdlet 默认使用 WSMan 协议,这意味着远程计算机需要 PowerShell 3.0 或更高版本才能进行连接。 实际上,PowerShell 版本并不那么重要,重要的是堆栈版本。 可以使用 Test-WSMan cmdlet 确定堆栈版本。 堆栈版本需为 3.0。 PowerShell 3.0 及更高版本提供该版本的堆栈。

Test-WSMan -ComputerName dc01
wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd
ProtocolVersion : http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd
ProductVendor   : Microsoft Corporation
ProductVersion  : OS: 0.0.0 SP: 0.0 Stack: 3.0

较旧的 WMI cmdlet 使用 DCOM 协议,该协议与较低版本的 Windows 兼容。 而较新 Windows 版本上的防火墙通常会阻止 DCOM。 可使用 New-CimSessionOption cmdlet 创建可与 New-CimSession 一起使用的 DCOM 协议连接。 这样便可使用 Get-CimInstance cmdlet 与低至 Windows Server 2000 的 Windows 版本进行通信。 这也意味着,将 Get-CimInstance cmdlet 与配置为使用 DCOM 协议的 CimSession 一起使用时,远程计算机上不需要 PowerShell。

使用 New-CimSessionOption cmdlet 创建 DCOM 协议选项,并将其存储在变量中。

$DCOM = New-CimSessionOption -Protocol Dcom

为了提高效率,可以将域管理员凭据或提升的凭据存储在变量中,这样就不必一直为每个命令输入这些信息。

$Cred = Get-Credential
cmdlet Get-Credential at command pipeline position 1
Supply values for the following parameters:
Credential

我有一台名为 SQL03 的服务器,该服务器运行 Windows Server 2008(非 R2)。 它是默认情况下未安装 PowerShell 的最新 Windows Server 操作系统。

使用 DCOM 协议在 SQL03 上创建 CimSession

$CimSession = New-CimSession -ComputerName sql03 -SessionOption $DCOM -Credential $Cred

请注意,在上一条命令中,我将名为 $Cred 的变量指定为 Credential 参数的值,这样就不必再次手动输入这些值

无论使用哪种基础协议,查询的输出都是相同的。

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 7237-7483-8873-8926-7271-5004-86
Version           : VRTUAL - 4001628
PSComputerName    : sql03

Get-CimSession cmdlet 用于查看当前连接的 CimSessions 及其正在使用的协议

Get-CimSession
Id           : 1
Name         : CimSession1
InstanceId   : 80742787-e38e-41b1-a7d7-fa1369cf1402
ComputerName : dc01
Protocol     : WSMAN

Id           : 2
Name         : CimSession2
InstanceId   : 8fcabd81-43cf-4682-bd53-ccce1e24aecb
ComputerName : sql03
Protocol     : DCOM

检索两个先前创建的 CimSession 并将其存储在名为 $CimSession 的变量中

$CimSession = Get-CimSession

使用一个命令同时查询这两台计算机,它们分别使用 WSMan 协议和 DCOM。

Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS
SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 0986-6980-3916-0512-6608-8243-13
Version           : VRTUAL - 4001628
PSComputerName    : dc01

SMBIOSBIOSVersion : 090006
Manufacturer      : American Megatrends Inc.
Name              : Intel(R) Xeon(R) CPU E3-1505M v5 @ 2.80GHz
SerialNumber      : 7237-7483-8873-8926-7271-5004-86
Version           : VRTUAL - 4001628
PSComputerName    : sql03

我写了许多有关 WMI 和 CIM cmdlet 的博客文章。 其中最有用的一篇文章与我创建的一个函数有关,该函数自动确定应使用 WSMan 还是 DCOM 并自动设置 CIM 会话,而无需手动查找是哪一个。 该博客文章的标题为用于在远程计算机上创建 CimSession 并将其回退到 Dcom 的 PowerShell 函数

完成 CIM 会话后,应使用 Remove-CimSession cmdlet 将其删除。 要删除所有 CIM 会话,只需通过管道将 Get-CimSession 传递到 Remove-CimSession

Get-CimSession | Remove-CimSession

总结

在本章中,你了解了如何通过 PowerShell 在本地和远程计算机上使用 WMI。 你还了解了如何使用 CIM cmdlet 分别处理具有 WSMan 和 DCOM 协议的远程计算机。

审阅

  1. WMI 和 CIM cmdlet 有何区别?
  2. 默认情况下,Get-CimInstance cmdlet 使用哪种协议?
  3. 使用 CIM 会话而不使用 Get-CimInstance 指定计算机名有哪些好处?
  4. 如何指定与 Get-CimInstance 一起使用的备用协议而不使用默认协议?
  5. 如何克隆或删除 CIM 会话?