PowerShell 技能连载 - 安装 ActiveDirectory 模块

这是一个对所有处理 Active Directory 的 PowerShell 用户的好消息:在最新的 Windows 10 版本(企业版、专业版)中,Microsoft 提供了 RSAT 工具,因此不需要另外下载。要将使用 AD 的 PowerShell 命令,只需启用 RSAT 功能(请参见下文)。

此外,PowerShell 7 终于原生支持 Active Directory 模块!如果您开始将新的 PowerShell 与Windows PowerShell 并行使用,则现在可以在 PowerShell 7 中使用以前仅在 Windows PowerShell 中工作的所有 AD cmdlet。

在提升权限的 PowerShell 中运行此命令,以查看可用的 RSAT 组件:

1
2
Get-WindowsCapability -Online |
Where-Object Name -like Rsat*

要使用 Active Directory 和组策略 PowerShell 模块,请启用 RSAT 功能

1
2
3
4
Get-WindowsCapability -Online |
Where-Object Name -like Rsat* |
Where-Object State -ne Installed |
Add-WindowsCapability -Online

完成后,Windows PowerShell 中将同时提供 Active DirectoryGroupPolicy PowerShell 模块。在 PowerShell 7 中,只能使用 ActiveDirectory 模块。

1
2
3
4
5
6
7
8
9
10
PS> Get-Module -Name ActiveDirectory, GroupPolicy -ListAvailable


Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules


ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0.1.0 ActiveDirectory {Add-ADCentralAccessPolicyMember, Add-ADCom...
Manifest 1.0.0.0 GroupPolicy {Backup-GPO, Block-GPInheritance, Copy-GPO...

PowerShell 技能连载 - 测试等待重启

当 Windows 安装了更新或对操作系统做了相关改变,改变可能需要在重启以后才能生效。当有一个挂起的重启时,操作系统可能不能被完全保护,而且可能无法安装其它软件。

可以通过测试指定的注册表项来确定是否有挂起的重启:

1
2
3
$rebootRequired = Test-Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending"

"Pending reboot: $rebootRequired"

如果 $rebootRequired 的值是 $true,那么就存在一个挂起的重启。

PowerShell 技能连载 - 隐藏启动 PowerShell 脚本

没有内置的方法可以隐藏启动 PowerShell 脚本:即使您运行 powershell.exe 并指定 -WindowStyle Hidden,PowerShell 控制台仍将一闪而过。

要隐藏启动 PowerShell 脚本,可以使用 VBScript:

1
2
3
4
5
6
Set objShell = CreateObject("WScript.Shell")
path = WScript.Arguments(0)

command = "powershell -noprofile -windowstyle hidden -executionpolicy bypass -file """ & path & """"

objShell.Run command,0

将 test.vbs 保存,然后确保以 ANSI 编码保存(用记事本并在另存为对话框的地步下拉列表中选择编码)。VBS 无法处理 UTF8 编码的脚本。当您试图运行这样的脚本时,会得到一个非法字符的异常。

要隐藏启动一个 PowerShell 脚本,可以运行这行命令:

请注意,虽然 wscript.exe 隐藏了 PowerShell 的控制台窗口,但您打开任何 WPF窗口(例如使用 Out-GridView)时,将继续工作并且正常显示。

1
Wscript.exe c:\pathtovbs.vbs c:\pathtoPS1file.ps1

PowerShell 技能连载 - 杀死无响应的进程

Get-Process 返回的进程对象可以判断该进程当前是否正在响应窗口消息,从而是否正在响应用户请求。此行代码返回当前的 PowerShell 进程的 “Responding” 属性:

1
2
3
4
5
PS> Get-Process -Id $Pid | Select-Object *respond*

Responding
----------
True

进程偶尔会变得无响应是很常见的,例如由于高负载和较弱的软件体系结构。当某个进程在较长时间内没有响应时,就会“挂起”,会让用户发疯。

这行代码列出了当前未响应的进程:

1
2
3
4
5
6
7
8
9
PS> Get-Process | Where-Object { !$_.Responding }

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
560 28 21752 580 0.38 18544 1 Calculator
915 65 26660 2528 0.39 14244 1 MicrosoftEdge
488 21 7108 7988 0.09 13400 1 MicrosoftEdgeCP
543 27 16148 520 0.31 21200 1 Time
1132 77 63544 836 1.55 15212 1 WinStore.App

请注意,此列表可能包括已启动但不再使用的 Windows 应用程序。由于属性“正在响应”仅描述当前状态,因此它无法确定进程多长时间未响应。

如果您想汇报(或杀死)一段时间未响应的所有进程,则需要自己进行反复检查并跟踪结果。

下面的代码在网格视图窗口中列出了 3 秒钟没有响应的所有进程。然后,用户可以选择一个或多个要杀死的进程(按住 CTRL 键选择多个进程)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# report processes hanging for more than 3 seconds
$timeout = 3

# use a hash table to keep track of processes
$hash = @{}

# use an endless loop and test processes
do
{
Get-Process |
# look at processes with a window only
Where-Object MainWindowTitle |
ForEach-Object {
# use process ID as key to the hash table
$key = $_.id
# if the process is responding, reset the counter
if ($_.Responding)
{
$hash[$key] = 0
}
# else, increment the counter by one
else
{
$hash[$key]++
}
}

# copy the hash table keys so that the collection can be
# modified
$keys = @($hash.Keys).Clone()

# emit all processes hanging for longer than $timeout seconds
# look at all processes monitored
$keys |
# take the ones not responding for the time specified in $timeout
Where-Object { $hash[$_] -gt $timeout } |
ForEach-Object {
# reset the counter (in case you choose not to kill them)
$hash[$_] = 0
# emit the process for the process ID on record
Get-Process -id $_
} |
# exclude those that already exited
Where-Object { $_.HasExited -eq $false } |
# show properties
Select-Object -Property Id, Name, StartTime, HasExited |
# show hanging processes. The process(es) selected by the user will be killed
Out-GridView -Title "Select apps to kill that are hanging for more than $timeout seconds" -PassThru |
# kill selected processes
Stop-Process -Force

# sleep for a second
Start-Sleep -Seconds 1

} while ($true)

当然,您可以轻松更改代码以生成挂起的进程报表。只需将 Stop-Process 替换为您想要执行的任何操作,即可使用 Add-Content 将流程写入日志文件。要避免一次次地记录相同的进程,您可能想要添加某种黑名单,以跟踪已经记录的进程。

PowerShell 技能连载 - 测试网络连接(第 2 部分)

如果您想测试一台特定的计算机或 URL 是否在线,几十年来一直使用 ping 请求 (ICMP)。最近,许多服务器和防火墙关闭了 ICMP 以减少攻击面。默认情况下,Test-NetConnection 使用 ICMP,因此在不响应 ICMP 的计算机上会失败:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS> Test-NetConnection -ComputerName microsoft.com
WARNING: Ping to 40.76.4.15 failed with status: TimedOut
WARNING: Ping to 40.112.72.205 failed with status: TimedOut
WARNING: Ping to 13.77.161.179 failed with status: TimedOut
WARNING: Ping to 40.113.200.201 failed with status: TimedOut
WARNING: Ping to 104.215.148.63 failed with status: TimedOut


ComputerName : microsoft.com
RemoteAddress : 40.76.4.15
InterfaceAlias : Ethernet 4
SourceAddress : 192.168.2.108
PingSucceeded : False
PingReplyDetails (RTT) : 0 ms

Test-NetConnection 内置了另一种计算机无法回避的测试:端口测试。端口提供对特定服务的访问,因此端口必须在任何公共服务可用。对于 Web 服务器,可以使用 80 端口,例如:

1
2
3
4
5
6
7
8
9
PS> Test-NetConnection -ComputerName microsoft.com -Port 80


ComputerName : microsoft.com
RemoteAddress : 104.215.148.63
RemotePort : 80
InterfaceAlias : Ethernet 4
SourceAddress : 192.168.2.108
TcpTestSucceeded : True

以下是常用的端口列表:

HTTP: Port 80
HTTPS: Port 443
FTP: Port 21
FTPS/SSH: Port 22
TELNET: Port 23
POP3: Port 110
POP3 SSL: Port 995
IMAP: Port 143
IMAP SSL: Port 993
WMI: Port 135
RDP: Port 3389
DNS: Port 53
DHCP: Port 67, 68
SMB/NetBIOS: 139
NetBIOS over TCP: 445
PowerShell Remoting: 5985
PowerShell Remoting HTTPS: 5986

PowerShell 技能连载 - 测试网络连接(第 1 部分)

PowerShell 随附了 Test-NetConnection 命令,它可以像复杂的 ping 工具一样工作。默认情况下,您可以这样 ping 计算机:

1
2
3
4
5
6
7
8
9
PS> Test-NetConnection -ComputerName powershell.one


ComputerName : powershell.one
RemoteAddress : 104.18.46.88
InterfaceAlias : Ethernet 4
SourceAddress : 192.168.2.108
PingSucceeded : True
PingReplyDetails (RTT) : 26 ms

使用 -TraceRoute,它包括路由跟踪,列出了用于传播到目标的所有网络节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS> Test-NetConnection -ComputerName powershell.one -TraceRoute


ComputerName : powershell.one
RemoteAddress : 104.18.46.88
InterfaceAlias : Ethernet 4
SourceAddress : 192.168.2.108
PingSucceeded : True
PingReplyDetails (RTT) : 25 ms
TraceRoute : 192.168.2.1
62.155.243.83
62.154.2.185
62.157.250.38
195.22.215.192
195.22.215.59
104.18.46.88

PowerShell 技能连载 - 探索即插即用设备(第 4 部分)

在前面的技能中,我们研究了 UPnP.UPnPDeviceFinder 以及如何识别网络中的设备。让我们看一些用例。显然,这些用例是您的灵感来源,因为您可以做的事情完全取决于连接到网络的实际设备。

我的第一个用例是管理 NAS 设备,名为 Synology Disk Station。可以通过 WEB 界面进行管理,但我总是会忘记 URL,当然 URL 和端口会根据配置而有所不同。

这是一种搜索任何磁盘站并自动打开其 Web 界面的方法。由于所有连接数据都是从设备中检索的,因此即使 IP 地址更改或您配置了别的端口,此操作也仍然有效。

1
2
3
4
5
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("ssdp:all", 0) |
Where-Object ManufacturerName -eq 'Synology' |
Select-Object -ExpandProperty PresentationUrl |
ForEach-Object { Start-Process -FilePath $_ }

注意:如果您没有 Synology 磁盘站,请查看您拥有的其他设备。只需查看 FindByType() 返回的数据,然后根据需要定制 Where-Object

由于该代码枚举了所有 UPnP 设备,因此需要 10 到 20 秒。如果要加快处理速度,可以找出所用设备的 UDN(唯一名称),以后再使用这些 UDN:

1
2
3
4
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("ssdp:all", 0) |
Where-Object ManufacturerName -eq 'Synology' |
Select-Object -ExpandProperty UniqueDeviceName

UDN 是唯一的,并且因设备而异,因此,如果您需要多次访问同一设备,则此方法才有意义。在我的情况下,UDN 是 uuid:7379AA6F-6473-6D00-0000-001132283f5e,现在打开 Web 界面的速度要快得多:

1
2
3
4
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("uuid:7379AA6F-6473-6D00-0000-001132283f5e", 0) |
Select-Object -ExpandProperty PresentationUrl |
Foreach-Object { Start-Process -FilePath $_ }

这引出了我的第二个使用场景:我正在使用 Philips Hue 系统管理家里的灯光。飞利浦提供了丰富的 REST API 来实现自动化。您所需要的是网桥的 IP 地址。

这段代码将列出本地网络中所有 Philips Hue 网桥的 IP 地址:

``powershell
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType(“upnp:rootdevice”, 0) |
Where-Object Description -like ‘Philips hue*’ |
Select-Object -ExpandProperty FriendlyName |
ForEach-Object {
if ($_ -match ‘(?\w*).?((?.))‘)
{
$null = $matches.Remove(0)
[PSCustomObject]$matches
}
}


该代码查找根设备,其描述以 "Philips hue" 开头的根设备,然后使用正则表达式拆分 "FriendlyName" 属性的内容并返回网桥名称及其 IP 地址。

就我而言,结果如下所示:

    IP            BridgeName
    --            ----------
    192.168.22.10 BridgeOne
    192.168.23.16 BridgeWork

<!--本文国际来源:[Exploring Plug&amp;Play Devices (Part 4)](https://community.idera.com/database-tools/powershell/powertips/b/tips/posts/exploring-plug-play-devices-part-4)-->

PowerShell 技能连载 - 探索即插即用设备(第 3 部分)

在前一个技能中我们演示了如何使用 UPnP.UPnPDeviceFinder 来查找网络中的设备。您已了解到如何枚举所有的根设备 (“upnp:rootdevice“),以及如何通过设备的唯一标识符来访问设备。

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("upnp:rootdevice", 0) | Out-GridView

在这一部分中,让我们完成搜索类型,看看如何枚举所有设备(而不仅仅是根设备),以及如何枚举设备类型组。

要列出所有设备,请使用 “ssdb:all" 而不是 "upnp:rootdevice`”:

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("ssdp:all", 0) | Out-GridView

结果包括根设备(“IsRootDevice” 为 $true,“ParentDevice”为空)以及所有子设备(“IsRootDevice” 为 $false,“ParentDevice” 指向该设备链接到的上级设备)。

在 “UniqueDeviceName” 中,可以找到可用于直接访问设备的唯一设备名称:

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByUDN("uuid:...", 0)

每个设备都属于一个类别,该类别在“Type”中显示。要查看类型列表,请尝试以下操作:

1
2
3
4
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("ssdp:all", 0) |
Select-Object -ExpandProperty Type |
Sort-Object -Unique

结果取决于网络中找到的设备。这是我得到的清单:

urn:dial-multiscreen-org:device:dial:1
urn:schemas-upnp-org:device:Basic:1
urn:schemas-upnp-org:device:InternetGatewayDevice:1
urn:schemas-upnp-org:device:MediaRenderer:1
urn:schemas-upnp-org:device:WANConnectionDevice:1
urn:schemas-upnp-org:device:WANDevice:1

要查找特定类型的所有设备,请将该类型与 FindByType() 一起使用:

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("urn:schemas-upnp-org:device:InternetGatewayDevice:1", 0)

最后一点:设备是否响应组搜索,甚至是“upnp:rootdevice”,都取决于设备及其实现。在我的场景中,即使存在那种类型的设备,我也无法获得“Basic”和“WANDevice”组的结果。

如果找不到特定设备,请尝试适用于所有设备的唯一搜索,然后通过“ssdp:all”列出所有设备。如果设备现在显示出来,则可以通过 Where-Object 使用“ssdp:all”和客户端过滤,或者通过查找唯一的设备标识符并通过其 UDN 和 FindByUDN() 直接访问特定设备来加快搜索速度。

PowerShell 技能连载 - 探索即插即用设备(第 2 部分)

在前一个技能中,我们使用 UPnP.UPnPDeviceFinder 来发现连入您网络的智能设备。今天,让我们仔细看看返回的对象。

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$result = $UPnPFinder.FindByType("upnp:rootdevice", 0)

当您运行一个搜索(可能需要执行一定的时间),将会获取到许多信息,但是某些属性只是以 System.__ComObject 的形式返回。如何查看它们背后隐藏的信息?

让我们取其中的一个返回对象。在我的例子中,通过查看 $result,我注意到一个来自 “NetGear” 对象。接下来,我使用 Where-Object 来存取它。

在您的场景中,可用的对象明显不同,这取决于您网络中使用的设备,所以您需要调整过滤器表达式来匹配返回的对象之一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PS> $switch = $result | Where-Object ManufacturerName -like *NetGear*

PS> $switch


IsRootDevice : True
RootDevice : System.__ComObject
ParentDevice :
HasChildren : True
Children : System.__ComObject
UniqueDeviceName : uuid:4d696e69-444c-164e-9d42-3894ed0e1db5
FriendlyName : RBR50 (Gateway)
Type : urn:schemas-upnp-org:device:InternetGatewayDevice:1
PresentationURL : http://www.orbilogin.net/
ManufacturerName : NETGEAR, Inc.
ManufacturerURL : http://www.netgear.com/
ModelName : NETGEAR Orbi Desktop AC3000 Router
ModelNumber : RBR50
Description : http://www.netgear.com/home/products/wirelessrouters
ModelURL : http://www.netgear.com/orbi
UPC : RBR50
SerialNumber : 5R21945T06DA4
Services : System.__ComObject

大多数属性使用普通的数据类型,例如 string 或者 integer。例如,”UniqueDeviceName” 返回一个设备的唯一名称(稍后会变得很重要)。然而某些属性,只是返回 “System.__ComObject”。让我们看看这些:

每个 PnP 设备可以是一个链条的一部分,并且由一个根设备开始。由于我们一开始搜索的是根设备,所以 “RootDevice” 属性总是对应返回的对象:

1
2
PS> $switch -eq $switch.RootDevice
True

要找出那些设备链接到根设备,请查看 “HasChildren” 和 “Children”:”HasChildren” 是一个简单的 Boolean 值。然而 “Children” 是一个 “System.__ComObject” 对象并且包含了我们想了解的信息:

1
2
PS> if ($switch.HasChildren) { $switch.Children.Count } else { 'no children' }
1

显然地,”Children” 看起来是某种数组。总之,一个根设备可以有许多子设备。通过存取该属性,PowerShell 自动将 “System.__ComObject” 转换为可读取的 .NET 对象,以下是我的 NetGear 设备的子设备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PS> $switch.Children


IsRootDevice : False
RootDevice : System.__ComObject
ParentDevice : System.__ComObject
HasChildren : True
Children : System.__ComObject
UniqueDeviceName : uuid:4d696e69-444c-164e-9d43-3894ed0e1db5
FriendlyName : WAN Device
Type : urn:schemas-upnp-org:device:WANDevice:1
PresentationURL :
ManufacturerName : NETGEAR
ManufacturerURL : http://www.netgear.com/
ModelName : NETGEAR Orbi Desktop AC3000 Router
ModelNumber : RBR50
Description : WAN Device on NETGEAR RBR50 Orbi Router
ModelURL : http://www.netgear.com/
UPC : RBR50
SerialNumber : 5R21945T06DA4
Services : System.__ComObject

这个子设备之下还有子设备。但是,继续深挖并列出子设备之下的子设备看起来失败了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS> # works:
PS> $switch.Children | Select-Object HasChildren, Children

HasChildren Children
----------- --------
True System.__ComObject



PS> # fails:
PS> $switch.Children.HasChildren
PS> $switch.Children.Children
PS> $switch.Children[0].HasChildren
Value does not fall within the expected range.
PS> $switch.Children[0].Children
Value does not fall within the expected range.

失败的原因是 COM 数组的一个特异性。它们使用一个非常规的枚举器,和普通的对象数组不同,所以 PowerShell 无法直接存取数组元素。一个简单的解决方案是使用 ForEach-Object

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PS> $child = $switch.Children

PS> $child | ForEach-Object { $_.Children }


IsRootDevice : False
RootDevice : System.__ComObject
ParentDevice : System.__ComObject
HasChildren : False
Children : System.__ComObject
UniqueDeviceName : uuid:4d696e69-444c-164e-9d44-3894ed0e1db5
FriendlyName : WAN Connection Device
Type : urn:schemas-upnp-org:device:WANConnectionDevice:1
PresentationURL :
ManufacturerName : NETGEAR
ManufacturerURL : http://www.netgear.com/
ModelName : NETGEAR Orbi Desktop AC3000 Router
ModelNumber : RBR50
Description : WANConnectionDevice on NETGEAR RBR50 Orbi Router
ModelURL : http://www.netgear.com/
UPC : 606449084528
SerialNumber : 5R21945T06DA4
Services : System.__ComObject

同样地,也适用于 “ParentDevice” 和 `Services”。我们来查看我的 NetGear 设备的更多服务。这次,我希望直接存取我的 NetGear 设备(这样比枚举所有设备快得多)。不过您需要知道它的唯一设备名。在我的例子中,”UniqueDeviceName” 的值是 “uuid:4d696e69-444c-164e-9d42-3894ed0e1db5”:

1
2
3
4
5
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
# the UDN is unique, so you need to find out the UDN for your device first
# you cannot use the UDN I used
$myNetgearSwitch = $UPnPFinder.FindByUDN('uuid:4d696e69-444c-164e-9d42-3894ed0e1db5')
$myNetgearSwitch

这次,几乎立即识别出我的设备。要列出它的服务,我只需要获取它的 “Services” 属性,并且 PowerShell 自动将该 COM 对象转为可见的属性:

1
2
3
4
5
PS> $myNetgearSwitch.Services

ServiceTypeIdentifier Id LastTransportStatus
--------------------- -- -------------------
urn:schemas-upnp-org:service:Layer3Forwarding:1 urn:upnp-org:serviceId:L3Forwarding1 0

PowerShell 技能连载 - 探索即插即用设备(第 1 部分)

您可能已经生活在一个互联的智能家居,许多设备连接到您的网络。PowerShell 只需几行代码就可以帮助您找到您的设备:

1
2
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("upnp:rootdevice", 0)

请注意 UPnP 查找器组件需要一些时间来检测您的设备。结果看起来类似这样:

IsRootDevice     : True
RootDevice       : System.__ComObject
ParentDevice     :
HasChildren      : False
Children         : System.__ComObject
UniqueDeviceName : uuid:73796E6F-6473-6D00-0000-001132283f5e
FriendlyName     : Storage2 (DS414)
Type             : urn:schemas-upnp-org:device:Basic:1
PresentationURL  : http://192.168.2.107:5000/
ManufacturerName : Synology
ManufacturerURL  : http://www.synology.com/
ModelName        : DS414
ModelNumber      : DS414 5.1-5055
Description      : Synology NAS
ModelURL         : http://www.synology.com/
UPC              :
SerialNumber     : 001132283f5e
Services         : System.__ComObject

IsRootDevice     : True
RootDevice       : System.__ComObject
ParentDevice     :
HasChildren      : False
Children         : System.__ComObject
UniqueDeviceName : uuid:2f402f80-da50-11e1-9b23-001788ac0af1
FriendlyName     : BridgeOne (192.168.2.100)
Type             : urn:schemas-upnp-org:device:Basic:1
PresentationURL  : http://192.168.2.100/index.html
ManufacturerName : Signify
ManufacturerURL  : http://www.meethue.com/
ModelName        : Philips hue bridge 2015
ModelNumber      : BSB002
Description      : Philips hue Personal Wireless Lighting
ModelURL         : http://www.meethue.com/
UPC              :
SerialNumber     : 001788ac0af1
Services         : System.__ComObject
...

使用 Select-Object 来选择您感兴趣的属性:

1
2
3
4
$UPnPFinder = New-Object -ComObject UPnP.UPnPDeviceFinder
$UPnPFinder.FindByType("upnp:rootdevice", 0) |
Select-Object ModelName, FriendlyName, PresentationUrl |
Sort-Object ModelName

在我的家中,该列表看起来类似这样:

ModelName                          FriendlyName               PresentationURL
---------                          ------------               ---------------
AFTMM                              Tobias's 2nd Fire TV
AFTS                               Tobias's Fire TV
AFTT                               Tobias's 3rd Fire TV stick
DS414                              Storage2 (DS414)           http://192.168.2.107:5000/
NETGEAR Orbi Desktop AC3000 Router RBR50 (Gateway)            http://www.orbilogin.net/
Philips hue bridge 2015            BridgeOne (192.168.2.100)  http://192.168.2.100/index.html
Philips hue bridge 2015            BridgeWork (192.168.2.106) http://192.168.2.106/index.html
SoundTouch 20                      Bad
SoundTouch 30                      Portable
SoundTouch SA-4                    Garden
SoundTouch SA-4                    LivingRoom

我正在使用群晖 NAS,假设我忘了访问它的 URL,那么现在可以快速地找回它。我的飞利浦灯光系统也是这样:UPnP 查找器返回所有 hub 的 IP 地址。

请注意 UPnP 查找器只能返回和您的计算机连入相同网络的设备。如果您的设备 IP 地址是在其它子网,那么不能通过这种方式检测。