PowerShell 技能连载 - 在多台计算机中并行运行命令

假设您已经启用了 PowerShell 远程处理(请看我们之前的技能),那么您可以同时在多台计算机上运行命令。

以下例子演示了这个操作,并且在列表中所有计算机中所有用户的桌面上放置一个文件。警告:这是个很强大的脚本。它将在列表中的所有机器上运行 $code 中的任何代码,假设启用了远程处理,并且您有相应的权限:

1
2
3
4
5
6
7
8
9
10
# list of computers to connect to
$listOfComputers = 'PC10','TRAIN1','TRAIN2','AD001'
# exclude your own computer
$listOfComputers = $listOfComputers -ne $env:COMPUTERNAME
# code to execute remotely
$code = {
"Hello" | Out-File -FilePath "c:\users\Public\Desktop\result.txt"
}
# invoke code on all machines
Invoke-Command -ScriptBlock $code -ComputerName $listOfComputers -Throttle 1000

例如,如果您将 $code 中的代码替换为 Stop-Computer -Force,所有机器将会被关闭。

PowerShell 技能连载 - 通过 PowerShell 远程处理操作远程机器

当您在目标机器上启用了 PowerShell 远程处理,请试试交互式地连接它。以下是您值得尝试的例子。只需要确保将 targetComputerName 替换成您需要连接的目标机器名即可:

1
2
3
4
5
6
7
8
9
PS C:\> Enter-PSSession -ComputerName targetComputerName

[targetComputerName]: PS C:\Users\User12\Documents> $env:COMPUTERNAME
TARGETCOMPUTERNAME

[targetComputerName]: PS C:\Users\User12\Documents> exit

PS C:\> $env:COMPUTERNAME
YOURCOMPUTERNAME

如果连接失败并且报 “Access Denied” 错误,您可能需要使用 -Credential 参数并且使用一个不同的用户账户来登录远程计算机。您可能需要本地管理员特权。

如果连接失败并且报告 RDP 错误,或者如果 WinRM 无法找到目标计算机名,请使用 Test-WSMan 检查连接。如果这无法连接上,请重新检查远程设置。您可能需要像之前的技能中描述的那样,先在目标机器上运行 Enable-PSRemoting

请不要运行会打开窗口的命令。只能运行产生文本信息的命令。

要离开远程会话,请使用 exit 命令。

PowerShell 技能连载 - 使用 PowerShell 远程处理

如果您想试试 PowerShell 远程处理,您至少需要在目标机器(您希望访问的机器)上启用它。要使用它,您需要目标机器上的本地管理员特权。请用管理员特权打开 PowerShell,并且运行以下代码:

1
2
3
4
#requires -RunAsAdministrator

# manually enable PowerShell remoting
Enable-PSRemoting -SkipNetworkProfileCheck -Force

接下来,在另一台能通过网络访问的计算机上,运行运行 PowerShell 远程处理的 “Ping“ 来测试是否能连上目标机器:

1
2
3
4
# "ping" for PowerShell remoting
# tests anonymously whether you can reach the target
$IPorNameTargetComputer = 'place name or IP address here'
Test-WSMan $IPorNameTargetComputer

当目标机器响应时,Test-WSMan 返回类似以下的文本。它进行了一个匿名测试,所以如果它失败了,您就知道防火墙或者目标机器的配置有问题。

wsmid           : http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.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

在下一个技能中,我们将看看能用 PowerShell 远程处理来做什么,以及如何远程执行脚本。

PowerShell 技能连载 - 以 JSON 格式读取和保存选项

如果您想在脚本中保存信息,您也许希望将数据保存成 JSON 格式的对象。以下是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# define options object
$options = [PSCustomObject]@{
Color = 'Red'
Height = 12
Name = 'Weltner'
}

# play with options settings
$options.Color = 'Blue'

# save options to file
$Path = "c:\test\options.json"
$options | ConvertTo-Json | Set-Content -Path $Path

# load options from file
$options2 = Get-Content -Path $Path | ConvertTo-Json

请确保路径 “c:\test” 存在。脚本将创建一个包含指定数据的自定义对象,并将它以 JSON 的格式保存到磁盘。下一步,数据将在您需要的时候读取出来。例如在另外一个脚本中,或读取初始设置时。

PowerShell 技能连载 - 将机器加入域

以下是 PowerShell 将机器加入 AD 域的基本步骤:

1
2
3
4
5
6
7
8
9
10
11
12
# do not store passwords in production solutions,
# or you MUST control access permissions to this sensitive data
$username = "mydomain\UserName"
$password = 'Password' | ConvertTo-SecureString -AsPlainText -Force
$domainName = 'NameOfDomain'

# convert username and password to a credential
$cred = [PSCredential]::new($username, $password)
# join computer
Add-Computer -DomainName $domainName -Credential $cred
# restart computer
Restart-Computer

您可以根据需要修改这段代码。这个例子中用明文存储密码,这是不安全的。您可能需要从一个文件中读取密码。

PowerShell 技能连载 - 覆盖 Execution Policy 设置

如果 PowerShell 不允许执行脚本,您可能需要先允许脚本执行,例如:

1
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Bypass -Force

当 execution policy 是在组策略中定义时,这样操作不会起作用,因为组策略设置优先级更高:

1
2
3
4
5
6
7
8
9
PS C:\> Get-ExecutionPolicy -List

Scope ExecutionPolicy
----- ---------------
MachinePolicy Restricted
UserPolicy Undefined
Process Bypass
CurrentUser Bypass
LocalMachine Undefined

在这种情况下,您可以将 PowerShell 内置的授权管理器替换成一个新的。只需要运行以下代码,PowerShell 将总是允许在指定的会话中执行脚本:

1
2
3
$context = $executioncontext.gettype().getfield('_context','nonpublic,instance').getvalue($executioncontext);
$field = $context.gettype().getfield('_authorizationManager','nonpublic,instance');
$field.setvalue($context,(new-object management.automation.authorizationmanager 'Microsoft.PowerShell'))

PowerShell 技能连载 - 格式化文本输出

如果您希望将输出文本格式化漂亮,您可能需要用 PSCustomObject 并且将它输出为一个格式化的列表,例如:

1
2
3
4
5
6
7
$infos = [PSCustomObject]@{
Success = $true
Datum = Get-Date
ID = 123
}

Write-Host ($infos| Format-List | Out-String) -ForegroundColor Yellow

实际使用时只需要增加或者调整哈希表中的键即可。

PowerShell 技能连载 - 将用户名转换为 SID

以下代码演示了如何查找一个用户名的 SID:

1
2
$domain = 'MyDomain'
$username = 'User01'
1
2
3
$sid = (New-Object Security.Principal.NTAccount($domain, $username)).Translate([Security.Principal.SecurityIdentifier]).Value

$sid

只需要确保正确地调整了域名和用户名。如果您需要查看本地用户的 SID,只需要将域名设置为本地计算机的名称。

1
2
3
4
5
$username = 'Administrator'

$sid = (New-Object Security.Principal.NTAccount($env:computername, $username)).Translate([Security.Principal.SecurityIdentifier]).Value

$sid

这在 PowerShell 5 中更简单,因为有一个新的 Get-LocalUser cmdlet:

1
2
3
4
5
6
7
8
PS C:\> Get-LocalUser | Select-Object -Property Name, Sid

Name SID
---- ---
Administrator S-1-5-21-2951074159-1791007430-3873049619-500
CCIE S-1-5-21-2951074159-1791007430-3873049619-1000
DefaultAccount S-1-5-21-2951074159-1791007430-3873049619-503
Guest S-1-5-21-2951074159-1791007430-3873049619-501

PowerShell 技能连载 - 测试运行 PowerShell 6 - 并行运行

PowerShell 可以下载并和 Windows 官方的 PowerShell 并行运行。如果您想测试运行它,请访问 https://github.com/PowerShell/PowerShell/releases,并选择合适您平台的发布版本。

其注意要下载 50MB 内容。当解压/安装发行版之后,将会得到一个全黑的 PowerShell 图标,代表 PowerShell 6。文件名也改了:可执行文件名不再是 “powershell.exe” 而是 “pwsh.exe”。

PowerShell 技能连载 - 在 Grid View 窗口中垂直显示数据

Out-GridView 总是以每个对象一行的方式生成表格:

1
Get-Process -Id $pid | Out-GridView

有些时候,在 grid view 窗口中垂直显示对象属性,每个属性一行,更有用。

要做到这个效果,请看看 Flip-Object:这个函数输入对象,并将它们按每个属性分割成独立的键-值对象。它们可以通过管道导到 Out-GridView 中。通过这种方式,对象属性可以以更详细的方式查看:

1
Get-Process -Id $pid | Flip-Object | Out-GridView

以下是 Flip-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
function Flip-Object
{
param
(
[Object]
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
$InputObject
)
process
{

$InputObject |
ForEach-Object {
$instance = $_
$instance |
Get-Member -MemberType *Property |
Select-Object -ExpandProperty Name |
ForEach-Object {
[PSCustomObject]@{
Name = $_
Value = $instance.$_
}
}
}

}
}