PowerShell 技能连载 - 为任何用户启动 Windows 终端

在 Windows 10 上,任何 PowerShell 用户都可以使用一个很棒的新工具:Windows Terminal。它使您可以同时使用多个 PowerShell 和其他控制台选项卡,并且可以混合使用 Windows PowerShell、PowerShell 7 和 Azure Cloud Shell 控制台。您可以从 Microsoft Store 安装Windows Terminal。

由于 Windows Terminal 是一个“应用程序”,因此始终以为每个用户独立安装。只有事先为该用户安装了该应用程序,才可以通过其可执行文件 wt.exe 启动它。

启动 Windows Terminal 的另一种方法是以下命令:

1
start shell:appsFolder\Microsoft.WindowsTerminal_8wekyb3d8bbwe!App

如果此调用不能帮助您从其他用户帐户启动 Windows Terminal,那么在即将发布的技能中,我们将介绍如何将应用程序转变为不再由 Windows 管理的便携式应用程序。取而代之的是,您可以用任何用户(包括高级帐户)运行它。敬请关注。

PowerShell 技能连载 - 彻底删除 AD 对象

许多 Active Directory 对象都受到保护,无法删除。尝试删除它们时,会出现错误,从而防止您意外删除无法还原的用户帐户。

当然,这可以防止您合法删除甚至将对象移动到新的 OU。

若要确定是否防止意外删除 AD 对象,请使用以下命令:

1
Get-ADObject ‹DN of object› -Properties ProtectedFromAccidentalDeletion

要关闭保护,即在您计划移动或删除对象时,请将属性设置为 $false

1
Set-ADObject ‹DN of object› -ProtectedFromAccidentalDeletion $false

PowerShell 技能连载 - 以可点击图标的方式部署 PowerShell(第 2 部分)

在上一个技巧中,我们说明了如何在Windows资源管理器快捷方式文件中嵌入多达 4096 个字符的PowerShell代码并生成可单击的PowerShell代码。

只需右键单击链接文件并打开属性对话框,即可轻松查看嵌入的 PowerShell 代码。

通过非常简单的调整,您就可以隐藏有效的负载代码。运行以下代码以在桌面上生成示例可单击的 PowerShell 图标:

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
$code = {
# place your code here (must be less than 4096 characters)
# (this example generates a battery report on notebooks
# and opens it in your default browser)
$r = "$env:temp\report.html"
powercfg /batteryreport /duration 14 /output $r
Invoke-Item -Path $r
Start-Sleep -Seconds 2
Remove-Item -Path $r
}

# turn code into a one-liner, remove comments, escape double quotes
# NOTE: this is a very simplistic conversion. Does not support block comments
# or quoted double quotes or any edgy stuff
# USE with simple staight-forward code only
$oneliner = $code.ToString().Trim().Replace('"','\"').Replace([Char[]]10,'').
Split([Char[]]13).Trim().Where{!$_.StartsWith('#')} -join ';'

# create path to a link file. It is always placed on your desktop
# and named "clickme.lnk"
$desktop = [Environment]::GetFolderPath('Desktop')
$linkpath = Join-Path -Path $desktop -ChildPath 'ClickMe.lnk'

# create a blank string of 260 chars
$blanker = " " * 260

# create a shortcut file
$com = New-Object -ComObject WScript.Shell
$shortcut = $com.CreateShortcut($linkpath)
# minimize window so PowerShell won't pop up
$shortcut.WindowStyle = 7
# use a different icon. Adjust icon index if you want
$shortcut.IconLocation = 'shell32.dll,8'
# run PowerShell
$shortcut.TargetPath = "powershell.exe"
# submit code as an argument and prepend with a blank string
# so payload is hidden in the properties dialog
$shortcut.Arguments = "$blanker-noprofile $oneliner"

# save and create the shortcut file
$shortcut.Save()

当您双击桌面上的 “clickme” 图标时,嵌入的负载代码将运行并创建电池报告,然后将其显示在默认浏览器中。

右键单击图标并选择“属性”时,将不会显示嵌入式 PowerShell 代码。该对话框仅显示powershell.exe的路径,但不显示任何参数。

这是通过在参数前面加上 260 个空白字符来实现的。快捷方式文件最多支持 4096 个字符的命令行,而 Windows 资源管理器及其对话框仅显示前 260 个字符。要查看嵌入式有效负载,用户必须使用上面的 PowerShell 代码以编程方式读取快捷方式文件并转储 arguments 属性。

PowerShell 技能连载 - 以可点击图标的方式部署 PowerShell(第 1 部分)

您可以使用 .lnk 文件将小型 PowerShell 解决方案部署到最终用户。以下是实现方法:

使用以下代码,然后将 $code 中的有效负载代码替换为您希望点击图标时执行的任意 PowerShell 代码。只要确保代码总数少于 4096 个字符即可。然后运行脚本。

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
$code = {
# place your code here (must be less than 4096 characters)
# (this example generates a battery report on notebooks
# and opens it in your default browser)
$r = "$env:temp\report.html"
powercfg /batteryreport /duration 14 /output $r
Invoke-Item -Path $r
Start-Sleep -Seconds 2
Remove-Item -Path $r
}

# turn code into a one-liner, remove comments, escape double-quotes
# NOTE: this is a very simplistic conversion. Does not support block comments
# or quoted double quotes or any edgy stuff
# USE with simple staight-forward code only
$oneliner = $code.ToString().Trim().Replace('"','\"').
Replace([Char[]]10,'').Split([Char[]]13).
Trim().Where{!$_.StartsWith('#')} -join ';'

# create path to a link file. It is always placed on your desktop
# and named "clickme.lnk"
$desktop = [Environment]::GetFolderPath('Desktop')
$linkpath = Join-Path -Path $desktop -ChildPath 'ClickMe.lnk'

# create a shortcut file
$com = New-Object -ComObject WScript.Shell
$shortcut = $com.CreateShortcut($linkpath)
# minimize window so PowerShell won't pop up
$shortcut.WindowStyle = 7
# use a different icon. Adjust icon index if you want
$shortcut.IconLocation = 'shell32.dll,8'
# run PowerShell
$shortcut.TargetPath = "powershell.exe"
# submit code as an argument
$shortcut.Arguments = "-noprofile $oneliner"

# save and create the shortcut file
$shortcut.Save()

结果是在桌面上出现一个名为 “clickme” 的图标。当双击该图标时,将运行嵌入的 PowerShell 代码。如果您没有在上面的示例中更改有效负载脚本,它将生成电池报告并将其显示在默认浏览器中。

由于有效载荷代码已嵌入图标文件中,因此您可以方便地将其地传递给其他人或进行部署。

在 $code 中调整嵌入式有效负载脚本时,需要考虑以下几点:

  1. 代码必须少于 4096 个字符
  2. 不要使用块注释,或最好删除所有注释行(以减小有效负载大小)
  3. 不要使用带引号的双引号,因为必须将双引号转义,而脚本不是很智能。它只是转义找到的所有双引号。

PowerShell 技能连载 - 试用新的 SSH 远程操作

如果您想试用 SSH 而非 WinRM 的新 PowerShell 远程操作替代方案,请确保先安装了 PowerShell 7。

接下来,在 PowerShell 7 中,安装以下模块:

1
PS> Install-Module -Name Microsoft.PowerShell.RemotingTools -Scope CurrentUser

一旦安装了模块,就可以在提升权限的 PowerShell 7 Shell 中,仅需一个调用即可启用基于 SSH 的新远程处理:

1
PS> Enable-SSHRemoting

安装完成后,打开提升的 PowerShell 7 Shell,然后尝试远程连接到您自己的计算机上:

1
PS> Enter-PSSession -HostName $env:computername -UserName remotingUser

一旦能按预期运行后,就可以使用相同的技术将跨平台从任何 PowerShell 7 实例远程连接到另一个实例。

PowerShell 技能连载 - 测试应用程序是否存在

这是一段简单的单行代码,可以测试您的系统(或任何其他应用程序)上是否安装了 PowerShell 7:

1
2
3
4
5
6
7
8
9
10
11
12
# name of application you want to test
$name = 'pwsh'

# try and find the application. Discard result and errors.
Get-Command -Name $name -ErrorAction Ignore | Out-Null

# if there was an error, the application does not exist
$exists = $?


# output result
"Does $name exist? $exists"

本质上,代码使用 Get-Command 按名称检查应用程序。 $env:path 中列出的文件夹中所安装的所有应用程序都将被识别出来。

如果将 PowerShell 7 安装在 “$env:path” 中列出的默认文件夹之一中,则上面的代码将返回 $true。如果尚未安装 PowerShell 7 或将其安装在 Get-Command 无法发现的某些专用文件夹中,它将返回 $false

PowerShell 技能连载 - 将文件路径转为 8.3 格式(第 2 部分)

在上一篇文章中,我们解释了如何使用旧的 COM 组件将默认的长路径名转换为短的 8.3 路径名。虽然可以偶尔进行转换,但使用 COM 组件的速度很慢且占用大量资源。

一种“清洁”的方法是直接使用 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
# this is the long path to convert
$path = "C:\Program Files\PowerShell\7\pwsh.exe"


# signature of internal API call
$signature = '[DllImport("kernel32.dll", SetLastError=true)]
public static extern int GetShortPathName(String pathName, StringBuilder shortName, int cbShortName);'
# turn signature into .NET type
$type = Add-Type -MemberDefinition $signature -Namespace Tools -Name Path -UsingNamespace System.Text

# create empty string builder with 300-character capacity
$sb = [System.Text.StringBuilder]::new(300)
# ask Windows to convert long path to short path with a max of 300 characters
$rv = [Tools.Path]::GetShortPathName($path, $sb, 300)

# output result
if ($rv -ne 0)
{
$shortPath = $sb.ToString()
}
else
{
$shortPath = $null
Write-Warning "Shoot. Could not convert $path"
}


"Short path: $shortPath"

PowerShell 技能连载 - 将文件路径转为 8.3 格式(第 1 部分)

许多年前,文件和文件夹名称最多包含 8 个字符,而这些短路径名称仍然存在。它们甚至仍然有用:短路径名永远不会包含空格和其他特殊字符,因此永远不需要引号或转义。当路径变得很长时,短路径也可能会有所帮助。

但是,如何获得默认长路径名称的短路径名称呢?一种方法是使用 Windows 脚本宿主使用的旧 COM 组件:

1
2
3
4
5
6
7
8
9
10
11
# take any path
# in this example, I am taking the path where Powershell 7 is installed
# this requires that PowerShell 7 in fact is installed.
# You can use any other path as well
$path = (Get-Command -Name pwsh).Source

"Long path: $path"

# convert it to 8.3 short name
$shortPath = (New-Object -ComObject Scripting.FileSystemObject).GetFile($path).ShortPath
"Short path: $shortPath"

结果看起来像这样:

Long path: C:\Program Files\PowerShell\7\pwsh.exe
Short path: C:\PROGRA~1\POWERS~1\7\pwsh.exe

PowerShell 技能连载 - 识别 PowerShell 宿主和路径

这是一个快速的单行代码,用于标识当前 PowerShell 宿主的完整路径:

1
2
PS> (Get-Process -Id $pid).Path
C:\Program Files\PowerShell\7\pwsh.exe

该路径会告诉您当前宿主的位置,并且您可以检查代码是否在 Windows PowerShell、PowerShell 7 或PowerShell ISE 中执行。

用类似的方法,您还可以按名称查找可执行文件的路径。例如,如果您想知道 PowerShell 7 在系统上的安装位置,请尝试以下操作:

1
2
PS C:\> (Get-Command -Name pwsh).Source
C:\Program Files\PowerShell\7\pwsh.exe

当然,如果找不到可执行文件,此行将产生错误。它必须位于 $env:path 中列出的文件夹之一中。

PowerShell 技能连载 - 使用在线帮助(第 2 部分)

在上一个技能中,我们提到许多 PowerShell 用户更喜欢在线帮助,而不是本地下载的帮助。要默认使用联机帮助文档,请在新的 PowerShell 控制台中尝试以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# by default, -? opens LOCAL help
PS> dir -?

NAME
Get-ChildItem

SYNOPSIS
Gets the items and child items in one or more specified locations.


SYNTAX
...


# with this line you tell PowerShell to use the ONLINE help by default
PS> $PSDefaultParameterValues.Add("Get-Help:Online",$true)

# now, whenever you use -?, the ONLINE help opens in a nicely formatted browser window
PS> dir -?

使用 $PSDefaultParameterValues.Add("Get-Help:Online",$true) 命令告诉 PowerShell,Get-Help 命令应始终自动使用 -Online 参数,因此现在您始终可以获得基于浏览器的帮助。

尽管在大多数情况下这很好,但是联机帮助无法自动显示主题。如果需要显示有关主题的信息,只需使用 Get-Help 或带有 -ShowWindow 参数的帮助以显示本地帮助:

1
2
3
4
5
6
# this fails when help defaults to show ONLINE help
PS> help about_for
Get-Help : The online version of this Help topic cannot be displayed because the Internet address (URI) of the Help topic is not specified in the command code or in the help file for the command.

# the -ShowWindow parameter always shows local help in an extra window
PS C:\> help about_for -ShowWindow