概要
Windows管理规范(Windows Management Instrumentation, WMI)可能是微软提供给管理员使用最优秀的工具之一。WMI可以从计算机中收集大量系统信息。WMI是一个外部技术,PowerShell仅仅与其接口交互而已。
典型的Windows计算机包含数万个管理信息,WMI会将这些信息整理成易于访问的形式。
WMI是一个巨大的、复杂的技术,如果要深入研究,可以阅读书籍 PowerShell and WMI,作者是微软MVP Richard Siddaway(Manning, 2012)。该书提供了大量示例,并讨论了关于PowerShell v3引入关于CIM Cmdlets的新功能。
命名空间
在最顶层,WMI被组织成命名空间(namespaces)。可以把命名空间想象为关联到特定产品或技术的一个文件夹。比如:
- “
root\CIMv2
”,该命名空间包含了所有Windows操作系统和计算机硬件信息。 - 而“
root\MicrosoftDNS
”命名空间包含了所有关于DNS服务器(假设已经安装了该角色)的信息。 - 在客户端计算机上,“
root\SecurityCenter
”包含了关于防火墙、杀毒软件等工具的信息。- 新版本的Windows使用“
root\SecurityCenter2
”代替它。
- 新版本的Windows使用“
通过微软管理控制台(Microsoft Management Console, MMC)的WMI控制单元在本机上产生的命名空间:
在命名空间中,WMI被分成一系列的类,每个类都是可用于WMI查询的管理单元。比如:
- 在“
root\SecurityCenter
”中的“Antivirus-Product
”类被设计用于保存反间谍软件的信息; - 在“
root\CIMv2
”中的“Win32_LogicalDisk
”类被设计用于保存逻辑磁盘的信息。
注意:不是所有的计算机都包含相同的WMI命名空间或类。比如,新版本的Windows存在“Root\SecurityCenter2
”命名空间,而不是“Root\SecurityCenter”命名空间;而前者在新版本的计算机中包含了所有信息。
实例和类
下面看一下从“root\SecurityCenter2”中查询“AntiSpywareProduct”:
Get-CimInstance -Namespace root\SecurityCenter2 -ClassName AntiSpywareProduct
当有一个或多个可管理组件时,你可以看到在对应的类中有相同数量的实例(instances)。一个实例由类代表了一个现实世界的事件。如果计算机只有一个单一的BIOS,那么在“root\CIMv2”中会有一个关于“Win32_BIOS
”的实例。如果计算机安装了100个后台服务,你会看到100个“Win32Service”的实例。
请注意,在“root\CIMv2”中的类型一般以“Wim32”(即使在64位系统中亦然)或“CIM_”(Common Information Model的缩写,是WMI建立的标准)开头。
不同命名空间下的类型名称重复也存在可能性,虽然这种情况很少,但在WMI中允许存在,因为每个命名空间实际上是一种有边界的容器。当引用一个类时,同时需要引用其命名空间,以便WMI知道从哪里找到对应的类,从而避免因为多个重名,但不属于同一个命名空间的类造成混乱。
所有这些实例、类和其他不可名状的东西统称为WMI仓库(WMI Repository)。
表面看上去,使用WMI十分简单:只需要指出哪个类包含你要的信息,然后从WMI中查询类的实例,最后检查实例的属性得知其管理信息。有时候需要实例执行一个方法,从而启动一个动作(action)或开始一个配置变更(configuration change)。
WMI的不足
微软为WMI制定了一系列的编程标准,但是产品组或多或少把精力放在如何实现类和是否对其文档化。结果就是使得WMI变得混乱。
例如,在“root\CIMv2”命名空间中,只有很少的类提供了让你修改配置设置的方法(methods)。因为属性是只读的,意味着你必须使用方法来修改。如果对应的方法不存在,你就不能使用WMI来修改这些类。
微软从来没规定某个产品必须使用WMI,或者如果这个产品使用了WMI,必须公开WMI的每个可能的部分。微软的DHCP服务可以访问到WMI。正如旧版本的Windows服务器一样,你可以查询网卡的配置,但是不能查到连接速度,因为这些信息不支持通过WMI查询。
但是微软也对此进行了改善,微软正在努力使PowerShell Cmdlets尽可能完成更多的管理任务。比如,过去WMI仅用于某种特定的编程方式重启远程计算机,这个方法由“Win32_OperatingSystem”类实现。而现在,PowerShell提供了名称为“Restart-Computer”的Cmdlet来实现。在某些情况下,Cmdlets内部会通过WMI实现,而无须直接调用WMI。
实际上,在PowerShell v3及后续版本中,你会留意到大量“CIM”命令。在大部分情况下,这些命令都是对WMI的某些部分进行了封装,从而提供了更加以PowerShell为中心的方式与WMI交互。
探索WMI
WMI没有特定的工具。你可以尝试在搜索引擎中搜索WMI explorer并查看结果,也可以尝试访问https://www.ks-soft.net/hostmon.eng/wmi/index.htm。
由于每台计算机上的WMI命名空间和类都不尽相同,所以你需要把工具直接在准备查询的机器上运行,以便看到对应机器的WMI仓库。
范例:现在来查询一组计算机,并从中得知它们的图标间距设置。这个任务依赖于Windows桌面,并且是操作系统的核心部分,所以我们从“root\CIMv2”类开始。
搜索引擎是查询所需类信息的另一种好方法。在icon spacing之前添加wmi前缀作为关键字进行搜索。比如,在搜索引擎中查找“wmi icon spacing”。结果中会有https://stackoverflow.com/questions/202971/formula-or-api-for-calulating-desktop-icon-spacing-on-windows-xp,在该页中可以会找到一些C#代码。
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2","SELECT * FROM Win32_Desktop");
对此,我们并不知道上述代码的意思,但是“Win32_Desktop”看上去像是个类名称。接下来可以查询该类名,这样的查询通常会帮助我们找到一些存在的文档。
另外一个途径是使用PowerShell本身。比如,我们想知道一些关于磁盘的信息,那么需要从猜测正确的命名空间开始。但是我们已经知道“root\CIMv2”包含了所有OS核心和硬件设备的信息,所以可以使用下面的命令。
最终找到了“Win32_LogicalDisk
”。
注意:这些名称以“**CIM_**
”开头的类通常是基类,你通常不能直接使用这些类。“Win32”版本的类是Windows特有的,并且这种前缀仅存在于特定命名空间——其他命名空间不使用以CIM作为前缀这种命名方式。
WMI和CIM
在PowerShell v3及后续版本中,有两种与WMI交互的方式。
- 所谓的“
**WMI Cmdlets**
”(别名Gwmi),例如“Get-WmiObject”与“Invoke-WmiMethod”——这些都是遗留命令,但是微软不会对它们进行后续开发投入。它们与远程过程调用(RPC)交互,也就是说,只有在防火墙支持状态审查时才能通过防火墙(实际上很难)。 - 新版的“
**CIM Cmdlets**
”,例如“Get-CimInstance”与“Invoke-CimMethod”——它们或多或少等价于旧版本的“WMI Cmdlets”,但它们通过WS-MAN(Windows远程管理服务)交互,替代原有的RPCs。这是微软的主方向,执行“Get-Command –noun CIM*
”可以显示很多微软提供的这类命令的功能。
毫无疑问,这些命令的后端同样是WMI,其差异在于如何交互和如何被使用。对于已经装有PowerShell和启用了Windows远程管理服务的新系统,CIM Cmdlets提供最佳体验——微软也会对其进行持续的功能及性能改进。
实际上,在Windows Server 2012 R2以及更新版本中,旧版的WMI默认为禁用状态,因此尽可能使用CIM。除此之外,CIM cmdlet可以使用旧版的RPC(或DCOM)协议通讯。因此即使与老机器进行通讯时,也可以仅使用CIM cmdlet。
使用Get-WmiObject
通过“Get-WmiObject”命令,可以指定一个命名空间、一个类名称甚至远程计算机的名称以及备用凭据名。如果需要,还可以从指定的计算机中查询该类的所有实例。如果需要减少类实例的返回结果,甚至可以提供筛选条件实现。
使用下面的语法,可以获取一个命名空间中的类列表。
也可以通过指定命名空间和类型查询一个类:
其中“root\CIMv2”是系统默认的命名空间,所以如果你的类在该命名空间中,可以不显式指定。同时,“-class
”是位置参数,也就是说,如果把类名称放到第一个位置,它依旧能正常工作。
PS C:\> gwmi antispywareproduct -Namespace root\securitycenter2
PS C:\> Get-WmiObject win32_desktop -Namespace root\CIMv2
# 可以不显式指定root\CIMv2
PS C:\> Get-WmiObject win32_desktop
查看属性
对于许多WMI类,PowerShell的默认配置已经设定了需要展示的属性。“Win32_OperatingSystem”是一个很好的例子,因为它默认仅在列表中展示了6个属性。可以把WMI对象用管道传输到Gm
或Format-List *
中,以便查看所有可用的属性。
过滤指定规则
另外,“-filter
”参数允许指定规则,来查询特定实例。该参数使用起来有点棘手。
对于该命令和输出结果,需要注意下面事项:
- 筛选条件通常被双引号包裹。
- 筛选比较操作符,并不使用PowerShell的常规操作符“-eq”或“-lt”,而使用更加传统的操作符,比如=、>、<、<=、>=和<>。
- 可以使用“
LIKE
”操作符,但在匹配值时必须使用“%”作为字符通配符,如“NAME LIKE '%administrator%'
”。 - 注意,这里不能像PowerShell一样使用*作为通配符。
- 可以使用“
- 字符串匹配是以单引号包裹,这也是筛选表达式的最外层的引号是双引号的原因。
- 避免在WMI中使用反斜杠。当需要使用文本的反斜杠时,必须使用两个反斜杠替代。
- Gwmi的输出结果总会包含大量系统属性,PowerShell的默认显示配置通常会隐藏这些属性。
- 系统属性名称以双下划线开始,有两个非常有用的属性:
__SERVER
:包含被查询的实例所在的计算机名称。该属性与“PSComputerName”属性功能一致。__PATH
:是实例本身的绝对引用,可以用来查询实例。检索远程计算机信息
该Cmdlet不仅可以从远程计算机中查询信息,也可以从多台计算机中检索。可以使用任何可以生成一个包含计算机名称或IP列表的技术。
计算机名称按顺序连接,如果某一台计算机不可用,该Cmdlet会产生一个错误,并跳过这台计算机,并转向下一台计算机。对于不可用的计算机,Cmdlet通常需要等待直到发生超时,意味着Cmdlet可能会暂停30~45秒之后才决定放弃这台计算机,然后产生错误并继续向后连接。
一旦查询到一个WMI实例的集合后,就可以把它们用管道连接到任何以“-Object”Cmdlet、“Format-”Cmdlet或“Out-”“Export-”“ConvertTo-”开头Cmdlet中。你可以使用自定义表格显示“Win32_BIOS”类的信息。格式化输出
可以创建一个关于表的自定义列,并用列的表达式执行一个全新的WMI查询。 ```powershell PS C:> gwmi -Class win32_bios -ComputerName localhost |Format-Table @{n=’ComputerName’; e={$.__SERVER}}, @{l=’BIOSSerial’; exp={$.SerialNumber}}, @{n=’OSBuild’; e={gwmi -class win32opreatingsystem -computer $.__SERVER | select -expand BuildNumber}} -AutoSize
ComputerName BIOSSerial OSBuild
IM20211010 M9NRCX05M144394
下面是上述示例说明:
- “Get-WmiObject”从两台计算机中查询“Win32_BIOS”信息。
- 结果被管道传输到“Format-Table”,并创建了三个自定义列。
- 第一列:名称为ComputerName,使用“Win32_BIOS”实例中的“__SERVER”系统属性得出。
- 第二列:名称为BIOSSerial,使用“Win32_BIOS”实例中的“SerialNumber”属性得出。
- 第三列:名称为OSBuild。
- 该列执行一个新的“Get-WmiObject”查询,从“Win32_BIOS”实例的“`__SERVER`”属性中查询“Win32_OperatingSystem”类。
- 然后把结果用管道传输到“Select-Object”中,这些信息来自于“Win32_OperatingSystem”实例的“BuildNumber”属性的内容,并用于填充OSBuild列的值。
<a name="O1JK6"></a>
## 使用Get-CimInstance
Get-CimInstance是PowerShell v3引入的新命令,与“Get-WmiObject”有很多相似的地方,但是也有几个语法上的差异。
- 需要使用“-ClassName”代替“-Class”(可简写为-Class)。
- 没有用于列出命名空间中的所有类的“-List”参数。
- 可以使用“Get-CimClass”并搭配“-Namespace”参数获取类列表。
- 
- **没有“-Credential”参数**;
- 如果需要使用凭据从远程计算机查询,可以通过“Invoke-Command”发送“`Get-CimInstance`”。
范例:查看逻辑磁盘信息
```powershell
PS C:\> Get-CimInstance -ClassName Win32_LogicalDisk
DeviceID DriveType ProviderName VolumeName Size FreeSpace
-------- --------- ------------ ---------- ---- ---------
C: 3 OS 128849350656 10425810944
D: 3 Data 358349795328 30610714624
Z: 4 \\10.1.2.83\d
范例:使用替代凭据查询远程计算机进程
PS C:\> Invoke-Command -ScriptBlock { Get-CimInstance -ClassName Win32_Process } -ComputerName im20211010 -Credential im30\rick