PowerShell 技能连载 - 签名 PowerShell 脚本(第 2 部分)

在上一个技能中,我们解释了如何使用 New-SeftSignedCert 创建自签名的代码签名证书。今天,我们将使用自签名或公司代码签名证书真实地对 PowerShell 脚本进行数字签名。

为此,请使用您想要的任何 PowerShell 脚本文件。您所需要的只是它的路径。另外,您需要一个在 Windows 证书存储中存储的有效代码签名证书的路径。这是第一部分的快速回顾,以创建自签名证书,以防您没有公司证书:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$Subject = 'MyPowerShellCode'
$FriendlyName = 'My Valid PowerShell Code'

# expires 5 years from now:
$ExpirationDate = (Get-Date).AddYears(5)

# store in user personal store:
$certStore = 'Cert:\CurrentUser\my'

# create certificate:
$cert = New-SelfSignedCertificate -Subject $Subject -Type CodeSigningCert -CertStoreLocation $certStore -FriendlyName $FriendlyName -NotAfter $ExpirationDate

$thumbprint = $cert.Thumbprint

$Path = Join-Path -Path $certStore -ChildPath $thumbprint
Write-Warning "Certificate Path: $Path"

运行此代码时,您将获得一个自签名的代码签名证书,并且该代码将返回生成证书的路径,即:

Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87   

要使用您的证书,请像这样通过 Get-Item 读取它(确保修改匹配证书的路径 - 每个证书都有唯一的指纹):

1
PS> $myCert = Get-Item -Path Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87

要将数字签名添加到 PowerShell 脚本文件(或其他能够为此问题携带数字签名的文件),请使用 Set-AuthenticodeSignature。运行以下演示代码(根据需要调整文件和证书的路径):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# digitally sign this file (adjust path to an existing ps1 file):
$Path = "$env:temp\test.ps1"

# adjust this path to point to a valid code signing certificate:
$CertPath = 'Cert:\CurrentUser\my\F4C1F9978D564E143D554F3679746B3A79E1FF87'

# if it does not exist, create a dummy file
$exists = Test-Path -Path $Path
if ($exists -eq $false)
{
'Hello World!' | Set-Content -Path $Path -Encoding UTF8
}

# read a code signing certificate to use for signing:
$myCert = Get-Item -Path $CertPath

# add a digital signature to a PS script file:
Set-AuthenticodeSignature -FilePath $Path -Certificate $myCert

# show changes inside script file:
notepad $Path

运行此代码时,在 $Path 中指定的脚本文件将打开并显示添加到脚本底部的数字签名:

Hello World!   

# SIG # Begin signature block   
# MIIFcAYJKoZIhvcNAQcCoIIFYTCCBV0CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB   
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR   
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU4QK+x7NLicgrIdzN+Nvxqbuq   
# Qv2gggMKMIIDBjCCAe6gAwIBAgIQG5jphqHXvLJFA0oCJTgcpDANBgkqhkiG9w0B   
...

恭喜,您刚刚已经对一个 PowerShell 脚本进行了数字签名!我们将在第三部分中探讨这种签名的好处。

评论

PowerShell 技能连载 - 签名 PowerShell 脚本(第 1 部分)

如今,将数字签名添加到 PowerShell 脚本不再是黑魔法,尽管理想情况下需要从公司或可信赖的权威中获得正式的“信任”代码签名证书,但现在不再是强制性的。

在进行代码签名之前,首先了解为什么要对 PowerShell 脚本进行签名?

数字签名是脚本代码的哈希,并使用数字证书的私钥进行加密。将其视为针对脚本的“包装器”或“密封”。有了它,您现在可以随时分辨出某人是否篡改了您的脚本。没有它,您将无法实现这个目的。

如果您尚未拥有适合代码签名的数字证书,那么在 Windows 机器上,您可以使用 New-SelfSignedCertificate cmdlet 快速创建一个。

运行此行代码以创建自己的全新代码签名证书,并将其存储在您的个人证书存储中(除非您使用 -NotAfter 指定不同的到期日期,否则有效期为一年):

1
2
3
4
5
6
7
8
PS> New-SelfSignedCertificate -Subject MyPowerShellCode -Type CodeSigningCert -CertStoreLocation Cert:\CurrentUser\my -FriendlyName 'My Valid PowerShell Code'


PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\my

Thumbprint Subject
---------- -------
57402F9D82231CABA4586127C99819F055AA2AF2 CN=MyPowerShellCode

要稍后在任何时候检索它,请记住它的指纹并像这样访问它(修改指纹以匹配您的其中一个证书):

1
2
3
4
5
6
7
8
9
10
11
PS> $cert = Get-Item -Path Cert:\CurrentUser\My\57402F9D82231CABA4586127C99819F055AA2AF2

PS> $cert.Subject
CN=MyPowerShellCode

PS> $cert.FriendlyName
My Valid PowerShell Code

PS> $cert.NotAfter

Monday, May 1, 2023 17:47:43

或者,如果您只记得主题或友好名称,则可以使用过滤器:

1
2
3
4
5
6
7
8
PS> Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert | Where-Object Subject -like *MyPowerShell*


PSParentPath: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint Subject
---------- -------
57402F9D82231CABA4586127C99819F055AA2AF2 CN=MyPowerShellCode

在下一个技能中,我们将开始使用此证书签署 PowerShell 脚本。

评论

PowerShell 技能连载 - 专业地处理错误

通常,PowerShell 脚本使用这样简单的错误报告形式,该报告的结构是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
# clearing global error list:
$error.Clear()
# hiding errors:
$ErrorActionPreference = 'SilentlyContinue'

# do stuff:
Stop-Service -Name Spooler
dir c:\gibtsnichtabc


# check errors at end:
$error.Count
$error | Out-GridView

尽管这没有错,但您应该了解 $error 是一个全局变量,因此,如果您在脚本中使用外部代码(即其他人写的功能或模块),这些作者可能已经使用了相同的技术,并且如果他们产生了错误并清除了全局错误列表,那么您将失去以前记录的错误。

一种更好,更健壮的方式是使用私有变量进行记录。 实际上并没有重写太多的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# hiding errors:
$ErrorActionPreference = 'SilentlyContinue'
# telling all cmdlets to use a private variable for error logging:
$PSDefaultParameterValues.Add('*:ErrorVariable', '+myErrors')
# initializing the variable:
$myErrors = $null

# do stuff:
Stop-Service -Name Spooler
dir c:\gibtsnichtabc


# check errors at end USING PRIVATE VARIABLE:
$myErrors.Count
$myErrors | Out-GridView

基本技巧是定义 -ErrorVariable 的默认参数值,并为其分配私有变量的名称。确保在名称之前添加一个 “+”,以便附加新错误,而不是覆盖现有错误。

评论

PowerShell 技能连载 - 清理硬盘(第 2 部分)

在上一篇文章中,我们介绍了 Windows 工具 cleanmgr 及其参数 /sageset/sagerun,您可以用它们来定义和运行自动硬盘清理。

今天,我们将研究如何自定义 cleanmgr.exe 执行的实际清理任务。

此工具将所有配置存储在 Windows 注册表的这个位置:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches

在下面,您可以找到 cleanmgr 可以执行的每项清理任务的关键。在上一个技能中,我们定义了这样的自定义清理任务(请记住,使用提升的管理员权限执行它):

1
PS> cleanmgr.exe /sageset:5388

这行命令打开一个对话框窗口,您可以在其中检查应绑定到已提交 ID 5388 的清理任务。

关闭对话框后,可以在 Windows 注册表中找到这些设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
# the ID you picked when saving the options:
$id = 5388

# the name of the reg value that stores your choices:
$flag = "StateFlags$id"

# the location where user choices are stored:
$path = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*"

# get all subkeys where your user choice was enabled:
Get-Item -Path $path |
Where-Object { $_.Property -contains $flag } # contains your registry value (StateFlags5388)
Where-Object { $_.GetValue($flag) -gt 0 } # your registry value contains a number greater than 0

执行结果准确列出了您先前在选择对话框中检查的清理器模块:

Name                           Property
----                           --------
Active Setup Temp Folders      (default)      : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
                               Autorun        : 1
                               Description    : These files should no longer be needed. They were originally 
                                                created by a setup program that is no longer running.
                               FileList       : *.tmp
                               Flags          : {124, 0, 0, 0}
                               Folder         : C:\Windows\msdownld.tmp|?:\msdownld.tmp
                               LastAccess     : {2, 0, 0, 0}
                               Priority       : 50   
                               StateFlags0001 : 2
                               StateFlags0003 : 2
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
BranchCache                    (default)      : {DE661907-527D-4d6a-B6A6-EBC7F88D9B95}
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
D3D Shader Cache               (default)      : {D8D133CD-3F26-402F-86DA-90B710751C2C}
                               Autorun        : 1
                               ReserveIDHint  : 2
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Delivery Optimization Files    (default)      : {4057C1AD-A51F-40BB-B960-22888CEB9812}
                               Autorun        : 0
                               Description    : @C:\WINDOWS\system32\domgmt.dll,-104
                               Display        : @C:\WINDOWS\system32\domgmt.dll,-103
                               Flags          : 128  
                               ReserveIDHint  : 2
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Diagnostic Data Viewer         (default)        : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
database files                 Autorun          : 0
                               CleanupString    : rundll32.exe utcutil.dll,DiskCleanupEnd
                               Description      : @C:\WINDOWS\system32\utcutil.dll,-302
                               Display          : @C:\WINDOWS\system32\utcutil.dll,-301
                               FileList         : *.*
                               Flags            : 573
                               Folder           : C:\ProgramData\Microsoft\Diagnosis\EventTranscript\
                               IconPath         : C:\WINDOWS\system32\utcutil.dll,0
                               PreCleanupString : rundll32.exe utcutil.dll,DiskCleanupStart
                               Priority         : 100
                               StateFlags0001   : 0  
                               StateFlags0003   : 0  
                               StateFlags0033   : 0  
                               StateFlags6254   : 0  
                               StateFlags5388   : 2  
Downloaded Program Files       (default)          : {8369AB20-56C9-11D0-94E8-00AA0059CE02}
                               AdvancedButtonText : @C:\Windows\System32\occache.dll,-1072
                               Autorun            : 1
                               Description        : @C:\Windows\System32\occache.dll,-1071
                               Display            : @C:\Windows\System32\occache.dll,-1070
                               Priority           : {100, 0, 0, 0}
                               StateFlags0001     : 2
                               StateFlags0003     : 2
                               StateFlags0033     : 0
                               StateFlags6254     : 2
                               StateFlags5388     : 2
Internet Cache Files           (default)          : {9B0EFD60-F7B0-11D0-BAEF-00C04FC308C9}
                               AdvancedButtonText : &View Files
                               Autorun            : 1
                               Description        : The Temporary Internet Files folder contains Web pages stored 
                                                    on your hard disk for quick viewing.
                                                    Your personalized settings for Web pages will be left intact. 
                               Display            : Temporary Internet Files
                               Priority           : 100
                               StateFlags0001     : 2
                               StateFlags0003     : 2
                               StateFlags0033     : 0
                               StateFlags6254     : 2
                               StateFlags5388     : 2
Language Pack                  (default)      : {191D5A6B-43B9-477A-BB22-656BF91228AB}
                               Autorun        : 1
                               StateFlags0001 : 0
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2
Old ChkDsk Files               (default)      : {C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}
                               Autorun        : 1
                               FileList       : *.CHK
                               Flags          : 288
                               Folder         : ?:\FOUND.000|?:\FOUND.001|?:\FOUND.002|?:\FOUND.003|?:\FOUND.004|
                                                ?:\FOUND.005|?:\FOUND.006|?:\FOUND.007|?:\FOUND.008|?:\FOUND.009
                               IconPath       : C:\WINDOWS\System32\DATACLEN.DLL,3
                               Priority       : 50   
                               PropertyBag    : {60F6E464-4DEF-11d2-B2D9-00C04F8EEC8C}
                               StateFlags0001 : 2
                               StateFlags0003 : 2
                               StateFlags0033 : 0
                               StateFlags6254 : 2
                               StateFlags5388 : 2
Recycle Bin                    (default)      : {5ef4af3a-f726-11d0-b8a2-00c04fc309a4}
                               PluginType     : 2 
                               StateFlags0001 : 2
                               StateFlags0003 : 0
                               StateFlags0033 : 0
                               StateFlags6254 : 0
                               StateFlags5388 : 2

每个清理器模块在 “(default)” 中具有唯一的GUID。 如您所见,GUID “{C0E13E61-0CC6-11d1-BBB6-0060978B2AE6}” 用于许多清理模块。这是一个通用的文件删除模块,您可以轻松地在自己的文件清理器模块中使用。只需在注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches 中添加新的子键,使用上面提到的 GUID,然后配置该清理器应找到并删除的文件。您可能需要查看现有的清理器,例如 regedit.exe 中的 “Old ChkDsk Files”,以找出定义要删除文件的注册表值的名称。

评论

PowerShell 技能连载 - 清理硬盘(第 1 部分)

cleanmgr.exe 是 Windows 自带的一个古老的工具,可以清除您的硬盘驱动器。

该工具可以删除各种垃圾数据,有时会删除许多 GB 的空间。对于 PowerShell 来说,它更有趣的地方在于支持自动化。

为了自动化清理磁盘,首先您需要启动具有管理员特权的 PowerShell(如果没有,不会提出错误,但是您的清理选择无法正确保存)。接下来,选择一个随机数,例如 5388,然后执行以下代码:

1
PS> cleanmgr.exe /sageset:5388

这将打开一个对话框窗口,您可以在其中选择下一步要执行的清理任务。检查所有适用的项目,然后单击“确定”。

现在,这些选择存储在您的自定义 ID 5388(或您选择的其他位置)下的 Windows 注册表中。 通过再次运行命令检查:对话框应再次打开并记住您的选择。如果对话框不记得您的选择,则可能没有使用管理员特权运行它。

要自动化磁盘清理,请使用参数 /sagerun 而不是 /sageset 运行命令,然后使用相同的 ID 号:

1
PS> cleanmgr /sagerun:5388

现在,清理程序无人值守地执行清理操作,因此这可能是某些情况下清理磁盘的最佳方法。

请注意,对话框显示清理进度,当前用户可以通过单击“取消”来中止清理。不过,无法隐藏此对话框。

评论

PowerShell 技能连载 - 管理蓝牙设备(第 2 部分)

如果您想通过编程方式删除配对的蓝牙设备,则没有内置的 cmdlet。 PowerShell 仍然可以解决问题,甚至通常可以解除配对无法通过 UI 删除或不断重复出现的蓝牙设备。

您需要首先删除蓝牙设备的硬件地址。 这是有关如何列出所有蓝牙设备并返回其硬件地址的示例:

1
2
3
4
5
6
7
8
9
10
11
$Address =     @{
Name='Address'
Expression={$_.HardwareID |
ForEach-Object { [uInt64]('0x' + $_.Substring(12))}}
}

Get-PnpDevice -Class Bluetooth |
Where-Object HardwareID -match 'DEV_' |
Select-Object FriendlyName, $Address |
Where-Object Address |
Out-GridView -Title 'Select Bluetooth Device to Remove' -OutputMode Single

结果看起来类似这样,并在网格视图窗口中显示,您可以在其中选择一个蓝牙设备:

FriendlyName                               Address
------------                               -------
Bamboo Ink Plus                       480816531482
SMA001d SN: 2110109033 SN2110109033   550378395892
MX Master 3                            20919489792
MX Keys                              1089715743697
Bose QC35 II                        44056255752152   

附带说明,该代码说明了一个简单的技巧,可以以编程方式将十六进制数字转换为十进制:

1
2
3
4
PS> $hex = 'A0FD'

PS> [int]"0x$hex"
41213

一旦知道要解除配对的蓝牙设备的硬件地址,接下来,您必须将其传给一个内部的 Windows API。内部方法 Bluetoothremavyevice() 将其删除。 下面的代码灵感来自 Keith A. Miller 在 Microsoft 论坛中提供的建议。

以下是一个包装内部 Windows API 签名的函数,它输入一个硬件地址,然后解绑设备:

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
function Unpair-Bluetooth
{
# take a UInt64 either directly or as part of an object with a property
# named "DeviceAddress" or "Address"
param
(
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[Alias('Address')]
[UInt64]
$DeviceAddress
)

# tell PowerShell the location of the internal Windows API
# and define a static helper function named "Unpair" that takes care
# of creating the needed arguments:
begin
{
Add-Type -Namespace "Devices" -Name 'Bluetooth' -MemberDefinition '
[DllImport("BluetoothAPIs.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.U4)]
static extern UInt32 BluetoothRemoveDevice(IntPtr pAddress);
public static UInt32 Unpair(UInt64 BTAddress) {
GCHandle pinnedAddr = GCHandle.Alloc(BTAddress, GCHandleType.Pinned);
IntPtr pAddress = pinnedAddr.AddrOfPinnedObject();
UInt32 result = BluetoothRemoveDevice(pAddress);
pinnedAddr.Free();
return result;
}'
}

# do this for every object that was piped into this function:
process
{
$result = [Devices.Bluetooth]::Unpair($DeviceAddress)
[PSCustomObject]@{
Success = $result -eq 0
ReturnValue = $result
}
}
}

由于新函数 Unpair-Bluetooth 是支持管道的,因此您可以将其附加到以前的代码之后即可解除蓝牙配对:

1
2
3
4
5
6
7
8
9
10
11
12
$Address =     @{
Name='Address'
Expression={$_.HardwareID |
ForEach-Object { [uInt64]('0x' + $_.Substring(12))}}
}

Get-PnpDevice -Class Bluetooth |
Where-Object HardwareID -match 'DEV_' |
Select-Object FriendlyName, $Address |
Where-Object Address |
Out-GridView -Title 'Select Bluetooth Device to Unpair' -OutputMode Single |
Unpair-Bluetooth

运行代码时,它再次显示所有蓝牙设备。选择一个要解除配对的设备,然后点击网格视图窗口右下角的确定。请注意,如果设备不是“记住的设备”,则解除配对会失败。当解除配对成功时,返回值为 0,并将设备从蓝牙设备列表中删除。

评论

PowerShell 技能连载 - 管理蓝牙设备(第 2 部分)

如果您只是在 Windows 中寻找一种快速的方法来配对和接触配对蓝牙设备,请尝试以下命令:

1
PS> explorer.exe ms-settings-connectabledevices:devicediscovery

这将立即弹出一个对话框,显示所有蓝牙设备。只需在 PowerShell 中添加一个函数,因此您不必记住命令,只需要将其放入个人资料脚本中:

1
2
3
PS> function Show-Bluetooth { explorer.exe ms-settings-connectabledevices:devicediscovery }

PS> Show-Bluetooth

如果您想在桌面放一个蓝牙图标的快捷方式,请尝试以下操作:

1
2
3
4
5
6
7
8
$desktop = [Environment]::GetFolderPath('Desktop')
$path = Join-Path -Path $desktop -ChildPath 'bluetooth.lnk'
$shell = New-Object -ComObject WScript.Shell
$scut = $shell.CreateShortcut($path)
$scut.TargetPath = 'explorer.exe'
$scut.Arguments = 'ms-settings-connectabledevices:devicediscovery'
$scut.IconLocation = 'fsquirt.exe,0'
$scut.Save()
评论

PowerShell 技能连载 - 管理蓝牙设备(第 1 部分)

列出计算机已连接的蓝牙设备只需要一行代码:

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
PS> Get-PnpDevice -Class Bluetooth 

Status Class FriendlyName InstanceId
------ ----- ------------ ----------
OK Bluetooth Bose QC35 II Avrcp Transport BTHENUM\{0000110C-00...
OK Bluetooth Generic Attribute Profile BTHLEDEVICE\{0000180...
OK Bluetooth Generic Access Profile BTHLEDEVICE\{0000180...
OK Bluetooth Generic Attribute Profile BTHLEDEVICE\{0000180...
OK Bluetooth Bamboo Ink Plus BTHLE\DEV_006FF2E608...
OK Bluetooth Bose QC35 II Avrcp Transport BTHENUM\{0000110E-00...
OK Bluetooth Generic Attribute Profile BTHLEDEVICE\{0000180...
OK Bluetooth SMA001d SN: 2110109033 SN2110109033 BTHENUM\DEV_0080251B...
OK Bluetooth MX Master 3 BTHLE\DEV_D304DEE615...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0000FE5...
OK Bluetooth Microsoft Bluetooth LE Enumerator BTH\MS_BTHLE\6&1B2C8...
OK Bluetooth Device Information Service BTHLEDEVICE\{0000180...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0000180...
OK Bluetooth Device Information Service BTHLEDEVICE\{0000180...
OK Bluetooth Generic Access Profile BTHLEDEVICE\{0000180...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0001000...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0000180...
OK Bluetooth MX Keys BTHLE\DEV_D9FDB81EAB...
OK Bluetooth Device Information Service BTHLEDEVICE\{0000180...
OK Bluetooth Microsoft Bluetooth Enumerator BTH\MS_BTHBRB\6&1B2C...
OK Bluetooth Generic Access Profile BTHLEDEVICE\{0000180...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0001000...
OK Bluetooth Intel(R) Wireless Bluetooth(R) USB\VID_8087&PID_002...
OK Bluetooth Bluetooth Device (RFCOMM Protocol TDI) BTH\MS_RFCOMM\6&1B2C...
OK Bluetooth Bluetooth LE Generic Attribute Service BTHLEDEVICE\{0000180...
OK Bluetooth Bose QC35 II BTHENUM\DEV_2811A579...

要根据名称搜索特定的蓝牙设备,请尝试下面一行代码。它会查找名称中带有 “Bose” 的所有设备:

1
2
3
4
5
6
7
PS> Get-PnpDevice -Class Bluetooth -FriendlyName *Bose*

Status Class FriendlyName
------ ----- ------------
OK Bluetooth Bose QC35 II Avrcp Transport
OK Bluetooth Bose QC35 II Avrcp Transport
OK Bluetooth Bose QC35 II

通过添加 Select-Object,您还可以显示蓝牙设备的其他详细信息:

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
PS> Get-PnpDevice -Class Bluetooth  | 
Select-Object -Property Caption, Manufacturer, Service

Caption Manufacturer Service
------- ------------ -------
Bose QC35 II Avrcp Transport Microsoft Microsoft_Bluetooth_AvrcpTransport
Generic Attribute Profile Microsoft UmPass
Generic Access Profile Microsoft UmPass
Generic Attribute Profile Microsoft UmPass
Bamboo Ink Plus Microsoft BthLEEnum
Bose QC35 II Avrcp Transport Microsoft Microsoft_Bluetooth_AvrcpTransport
Generic Attribute Profile Microsoft UmPass
SMA001d SN: 2110109033 SN2110109033 Microsoft
MX Master 3 Microsoft BthLEEnum
Bluetooth LE Generic Attribute Service Microsoft UmPass
Microsoft Bluetooth LE Enumerator Microsoft BthLEEnum
Device Information Service Microsoft UmPass
Bluetooth LE Generic Attribute Service Microsoft UmPass
Device Information Service Microsoft UmPass
Generic Access Profile Microsoft UmPass
Bluetooth LE Generic Attribute Service Microsoft UmPass
Bluetooth LE Generic Attribute Service Microsoft UmPass
MX Keys Microsoft BthLEEnum
Device Information Service Microsoft UmPass
Microsoft Bluetooth Enumerator Microsoft BthEnum
Generic Access Profile Microsoft UmPass
Bluetooth LE Generic Attribute Service Microsoft UmPass
Intel(R) Wireless Bluetooth(R) Intel Corporation BTHUSB
Bluetooth Device (RFCOMM Protocol TDI) Microsoft RFCOMM
Bluetooth LE Generic Attribute Service Microsoft UmPass
Bose QC35 II Microsoft

当您使用名词 “PnPDevice” 搜索其他 cmdlet 时,还可以发现启用或禁用的命令:

1
2
3
4
5
6
7
PS> Get-Command -Noun PnPDevice

CommandType Name Version Source
----------- ---- ------- ------
Function Disable-PnpDevice 1.0.0.0 PnpDevice
Function Enable-PnpDevice 1.0.0.0 PnpDevice
Function Get-PnpDevice 1.0.0.0 PnpDevice

要了解如何确定当前连接状态并完全删除蓝牙设备,请参阅我们的下一个提示。

评论

PowerShell 技能连载 - 2022 年的 PowerShell 计划

Microsoft PowerShell 团队刚刚发布了 2022 年的计划和投资:https://devblogs.microsoft.com/powershell/powershell-and-openssh-team-investments-for-2022/

简而言之,这些投资围绕着更高的安全性。此外,自定义远程连接可能会成为一个有趣的补充:您为访问远程系统而编写的代码随后可以被其他 cmdlet 使用,实质上形成了一个远程基础架构。

此外,该团队希望让现有的 Windows PowerShell 用户更容易获得 PowerShell 7。我们可能会在 2022 年看到 Windows PowerShell 多年来的第一次更新:团队讨论向 Windows PowerShell 添加一个新的 cmdlet,以在其上安装 PowerShell 7。

为了让 IntelliSense 更加智能,该团队已经引入了 “CompleterPredictors”,它可以“猜测”你将要输入的内容。虽然这些最初仅限于 Azure cmdlet,但该技术现在将向任何开发人员广泛开放。

通过添加为每个模块单独加载 .NET 程序集的方法,模块将变得更加健壮。目前,当一个模块加载具有给定版本的 .NET 程序集,而另一个模块需要不同版本的相同 .NET 程序集时,任何时候都只能加载一个版本,从而导致失败。

PowerShell 的内部包管理系统 PowerShellGet 将升级到 3.0 版,该版本之前仅作为预览版提供。它解决了从 powershellgallery.com 等存储库上传和安装模块的常见功能请求。

团队在 2022 年将关注更多感兴趣的领域,即适用于 Windows 的 OpenSSH。在此处阅读完整的声明:https://devblogs.microsoft.com/powershell/powershell-and-openssh-team-investments-for-2022/

评论

PowerShell 技能连载 - 利用 WMI(第 5 部分)

WMI 类被组织在所谓的命名空间中,这些命名空间从“根”开始并像目录结构一样工作。默认命名空间是 root\cimv2,当不指定命名空间时,你只能看到位于默认命名空间中的 WMI 类。这是我们在本系列的过去部分中一直使用的。

还有更多带有许多附加 WMI 类的命名空间,其中一些非常有用。在我们探索 WMI 的这一部分之前,请注意,您将进入 Windows 的文档稀少的区域,这些区域不一定适用于一般受众。您可能需要 google,此处找到的类可能仅适用于特定的 Windows 操作系统和/或许可证类型。

要获取计算机上可用的 WMI 命名空间列表,请运行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# create a new queue
$namespaces = [System.Collections.Queue]::new()

# add an initial namespace to the queue
# any namespace in the queue will later be processed
$namespaces.Enqueue('root')

# process all elements on the queue until all are taken
While ($namespaces.Count -gt 0 -and ($current = $namespaces.Dequeue()))
{
# find child namespaces
Get-CimInstance -Namespace $current -ClassName __Namespace -ErrorAction Ignore |
# ignore localization namespaces
Where-Object Name -NotMatch '^ms_\d{2}' |
ForEach-Object {
# construct the full namespace name
$childnamespace = '{0}\{1}' -f $current, $_.Name
# add namespace to queue
$namespaces.Enqueue($childnamespace)
}

# output current namespace
$current
}

代码可能会运行一段时间,所以请喝杯咖啡。结果是您机器上可用的命名空间列表,可能类似于以下内容:

root
root\subscription
root\DEFAULT
root\CIMV2
root\msdtc
root\Cli
root\Intel_ME
root\SECURITY
root\HyperVCluster
root\SecurityCenter2
root\RSOP
root\PEH
root\StandardCimv2
root\WMI
root\MSPS
root\directory
root\Policy
root\virtualization
root\Interop
root\Hardware
root\ServiceModel
root\SecurityCenter
root\Microsoft
root\Appv
root\dcim
root\CIMV2\mdm
root\CIMV2\Security
root\CIMV2\power
root\CIMV2\TerminalServices
root\HyperVCluster\v2
root\RSOP\User
root\RSOP\Computer
root\StandardCimv2\embedded
root\directory\LDAP
root\virtualization\v2
root\Microsoft\HomeNet
root\Microsoft\protectionManagement
root\Microsoft\Windows
root\Microsoft\SecurityClient
root\Microsoft\Uev
root\dcim\sysman
root\CIMV2\mdm\dmmap
root\CIMV2\Security\MicrosoftTpm
root\CIMV2\Security\MicrosoftVolumeEncryption
root\Microsoft\Windows\RemoteAccess
root\Microsoft\Windows\Dns
root\Microsoft\Windows\Powershellv3
root\Microsoft\Windows\Hgs
root\Microsoft\Windows\WindowsUpdate
root\Microsoft\Windows\DeviceGuard
root\Microsoft\Windows\TaskScheduler
root\Microsoft\Windows\DesiredStateConfigurationProxy
root\Microsoft\Windows\SmbWitness
root\Microsoft\Windows\Wdac
root\Microsoft\Windows\StorageReplica
root\Microsoft\Windows\winrm
root\Microsoft\Windows\AppBackgroundTask
root\Microsoft\Windows\DHCP
root\Microsoft\Windows\PS_MMAgent
root\Microsoft\Windows\Storage
root\Microsoft\Windows\HardwareManagement
root\Microsoft\Windows\SMB
root\Microsoft\Windows\EventTracingManagement
root\Microsoft\Windows\DesiredStateConfiguration
root\Microsoft\Windows\Attestation
root\Microsoft\Windows\CI
root\Microsoft\Windows\dfsn
root\Microsoft\Windows\DeliveryOptimization
root\Microsoft\Windows\Defender
root\dcim\sysman\biosattributes
root\dcim\sysman\wmisecurity
root\Microsoft\Windows\RemoteAccess\Client
root\Microsoft\Windows\Storage\PT
root\Microsoft\Windows\Storage\Providers_v2
root\Microsoft\Windows\Storage\PT\Alt  

一旦您知道了命名空间的名称,就可以使用这个迷你系列的前面部分来查询位于给定命名空间中的 WMI 类名。例如,下面的代码列出了命名空间 “root\Microsoft\Windows\WindowsUpdate” 中的所有 WMI 类:

1
2
3
4
$ns = 'root\Microsoft\Windows\WindowsUpdate'
Get-CimClass -Namespace $ns |
Select-Object -ExpandProperty CimClassName |
Sort-Object

以双下划线开头的类名用于内部用途:

__AbsoluteTimerInstruction
__ACE
__AggregateEvent
__ClassCreationEvent
__ClassDeletionEvent
__ClassModificationEvent
__ClassOperationEvent
__ClassProviderRegistration
__ConsumerFailureEvent
__Event
__EventConsumer
__EventConsumerProviderRegistration
__EventDroppedEvent
__EventFilter
__EventGenerator
__EventProviderRegistration
__EventQueueOverflowEvent
__ExtendedStatus
__ExtrinsicEvent
__FilterToConsumerBinding
__IndicationRelated
__InstanceCreationEvent
__InstanceDeletionEvent
__InstanceModificationEvent
__InstanceOperationEvent
__InstanceProviderRegistration
__IntervalTimerInstruction
__MethodInvocationEvent
__MethodProviderRegistration
__NAMESPACE
__NamespaceCreationEvent
__NamespaceDeletionEvent
__NamespaceModificationEvent
__NamespaceOperationEvent
__NotifyStatus
__NTLMUser9X
__ObjectProviderRegistration
__PARAMETERS
__PropertyProviderRegistration
__Provider
__ProviderRegistration
__QOSFailureEvent
__SecurityDescriptor
__SecurityRelatedClass
__SystemClass
__SystemEvent
__SystemSecurity
__thisNAMESPACE
__TimerEvent
__TimerInstruction
__TimerNextFiring
__Trustee
__Win32Provider
CIM_ClassCreation
CIM_ClassDeletion
CIM_ClassIndication
CIM_ClassModification
CIM_Error
CIM_Indication
CIM_InstCreation
CIM_InstDeletion
CIM_InstIndication
CIM_InstModification
MSFT_ExtendedStatus
MSFT_WmiError
MSFT_WUOperations
MSFT_WUSettings
MSFT_WUUpdate  

要查询此命名空间中的 MSFT_WUSettings 类(如果存在于您的机器上),请运行以下命令:

1
2
$ns = 'root\Microsoft\Windows\WindowsUpdate'
Get-CimInstance -ClassName MSFT_WUSettings -Namespace $ns

结果是一个异常,表明您在探索新的且文档稀少的内部 WMI 类时可能会感到意外。根据您的操作系统和许可证类型,可能支持也可能不支持操作。如果它们不受支持,您最终可能会遇到 “provider failure” 异常。

此外,某些 WMI 类(如此命名空间中的类)并非旨在包含属性和返回信息。相反,它们包含方法(命令),这个例子用于管理 Windows 更新:

1
2
3
$ns = 'root\Microsoft\Windows\WindowsUpdate'
Get-CimClass -Namespace $ns -ClassName 'MSFT_WUOperations' |
Select-Object -ExpandProperty CimClassMethods

结果显示了它的方法:

Name           ReturnType Parameters                              Qualifiers           
----           ---------- ----------                              ----------           
ScanForUpdates     UInt32 {SearchCriteria, Updates}               {implemented, static}
InstallUpdates     UInt32 {DownloadOnly, Updates, RebootRequired} {implemented, static}

这是 Windows Server 2019 机器的实际示例。该脚本远程访问服务器(并行)并使用本地 WMI 检查安全更新,然后安装它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# list of names of Windows Server 2019 machines to update:
$servers="Server2019-IIS","Server2019-AD"

$ns = "root/Microsoft/Windows/WindowsUpdate"
$class = "MSFT_WUOperations"

Invoke-Command -ComputerName $servers -ScriptBlock {
# find missing updates:
$arg = @{SearchCriteria="IsInstalled=0 AND AutoSelectOnWebSites=1"}
$r = Invoke-CimMethod -Namespace $ns -ClassName $class -MethodName ScanForUpdates -Arguments $arg

# install missing updates:
if ($r.Updates)
{
$arg = @{Updates=$r.Updates}
Invoke-CimMethod -Namespace $ns -ClassName $class -MethodName InstallUpdates -Arguments $arg
}
}

请注意,上面的代码在 Windows Server 2019 上有效,但在 Windows Server 2016 上失败(因为 WMI 类名略有变化)。如果你还想深入挖掘,我们强烈推荐 https://github.com/microsoft/MSLab/blob/master/Scenarios/Windows%20Update/readme.md 这是一组关于如何使用 WMI 管理 Windows 服务器上的 Windows 更新的丰富示例。

如果您对 WMI 感兴趣,请前往 https://powershell.one/wmi/commands 了解更多信息。

评论