第 7 章 - 使用 WMI

WMI 和 CIM

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

PowerShell 中存在多个本机 WMI cmdlet,无需安装任何其他软件或模块。 Get-Command 可用于确定 Windows PowerShell 中存在哪些 WMI cmdlet。 以下结果来自运行 PowerShell 版本 5.1 的 Windows 11 系统。 结果可能会有所不同,具体取决于正在运行的 PowerShell 版本。

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

通用信息模型 (CIM) cmdlet 在 PowerShell 3.0 中引入,并分组在专用模块中。 若要列出所有可用的 CIM cmdlet,请使用 Get-CommandModule 参数的 cmdlet,如以下示例所示。

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

CIM cmdlet 仍允许你使用 WMI,因此,当有人指出: “当我使用 PowerShell CIM cmdlet 查询 WMI 时”时,请不要混淆。

如前所述,WMI 是与 PowerShell 不同的技术,你只是使用 CIM cmdlet 来访问 WMI。 你可能会发现使用 WMI 查询语言(WQL)查询 WMI 的旧 VBScript,如以下示例所示。

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。 但是,它可以正常工作,并允许你轻松地将现有的 Visual Basic 脚本迁移到 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 时,在后台检索从未使用过的多个属性。 在本地计算机上查询 WMI 并不重要。 但是,开始查询远程计算机后,不仅需要额外的处理时间才能返回该信息,而且需要更多不必要的信息才能通过网络发送。 Get-CimInstance 具有一个 属性 参数,用于限制检索的信息,使 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:Stri
   ng) [Get-CimInstance], CimException
    + FullyQualifiedErrorId : HRESULT 0x80070005,Microsoft.Management.Infra
   structure.CimCmdlets.GetCimInstanceCommand
    + PSComputerName        : dc01

许多人对 PowerShell 有安全问题,但 PowerShell 中的权限与 GUI 中的权限相同。 不多不少,正好相符。 上一示例中的问题在于运行 PowerShell 的用户无权从 DC01 服务器查询 WMI 信息。 可以将 PowerShell 重新启动为域管理员,因为 Get-CimInstance 没有 Credential 参数。 但这不是一个好主意,因为从 PowerShell 运行的任何内容都将作为域管理员运行。根据情况,从安全的角度来看,这种情况可能很危险。

使用最低特权原则,针对每个命令使用凭据参数(如果命令有此参数)提升到域管理员帐户。 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,以便它可以在创建新会话之前先执行,并提示输入备用凭据。 在本章的后面部分,我向你展示了另一种更高效的方法来指定备用凭据,但在使其更加复杂之前,请务必了解此基本概念。

现在,可以使用上一示例中创建的 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 的版本,而是堆栈的版本。 可以使用 cmdlet 确定 Test-WSMan 堆栈版本。 它需要版本 3.0,可以使用 PowerShell 3.0 及更高版本找到它。

Test-WSMan -ComputerName dc01
wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentit
                  y.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 使用与旧版 Windows 兼容的 DCOM 协议。 但是,防火墙通常会在较新版本的 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 用于查看当前连接的 CimSession 以及它们使用的协议。

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

检索之前创建的 CimSessions 并将其存储在名为 $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 的博客文章之一提供了 PowerShell 函数,该函数可自动检测是使用 WSMan 还是 DCOM,然后为你设置适当的 CIM 会话。 有关详细信息,请参阅 用于创建远程计算机 CimSessions 并回退至 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. 默认情况下,cmdlet 使用什么协议 Get-CimInstance
  3. 使用 CIM 会话替代指定 Get-CimInstance 计算机名称有什么好处?
  4. 如何指定备用协议,而不是默认协议以供使用 Get-CimInstance
  5. 如何关闭或删除 CIM 会话?

参考文献