远程会话上的Invoke-Command返回本地值

时间:2019-08-07 19:13:52

标签: powershell formatting powershell-5.0 winrm invoke-command

问题

Invoke-Command的脚本块与PSSession一起运行时,是否应该始终在远程计算机上运行?

上下文

我对服务器列表运行了以下Powershell:

Clear-Host
$cred = get-credential 'myDomain\myUsername'
$psSessions = New-PSSession -ComputerName @(1..10 | %{'myServer{0:00}' -f $_}) -Credential $cred
Invoke-Command -Session $psSessions -ScriptBlock {
    Get-Item -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters' 
} | Sort-Object PSComputerName
# $psSessions  | Remove-PSSession

此返回:

    Hive: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos


Name                           Property                   PSComputerName
----                           --------                   --------------
Parameters                     MaxPacketSize : 1          myServer01
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer02
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer03
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer04
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer05
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer06
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer07
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer08
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer09
                               MaxTokenSize  : 65535
Parameters                     MaxPacketSize : 1          myServer10
                               MaxTokenSize  : 65535

一切看起来不错;我只希望我不会看到这些值/在设置这些服务器上的值以确保没有覆盖任何内容之前,我已经将其作为一种快速检查来运行。

我使用regedit快速查看了其中一台服务器;并发现MaxTokenSizeMaxPacketSize不存在。

然后我修改了命令以使用Get-ItemProperty代替Get-Item

Invoke-Command -Session $psSessions -ScriptBlock {
    Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters' -Name 'MaxTokenSize'
} | Sort-Object PSComputerName

这次我有10个错误:

Property MaxTokenSize does not exist at path HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters.
    + CategoryInfo          : InvalidArgument: (MaxTokenSize:String) [Get-ItemProperty], PSArgumentException
    + FullyQualifiedErrorId : System.Management.Automation.PSArgumentException,Microsoft.PowerShell.Commands.GetItemPropertyCommand
    + PSComputerName        : myServer01
# ... (and the same for the other 9 servers, with only PSComputerName changing)

关于返回值的来源...它们来自我的本地计算机。修改我的本地注册表项并重新运行原始命令,显示所有“服务器”具有新值。

我猜这是一个错误;但是由于到目前为止我还没有玩过PSSession,所以我想在这里检查一下,以防我理解/使用这些命令有问题,或者在使用{{1 }}。

2 个答案:

答案 0 :(得分:1)

将其放置在fl *或ft *上,因此它不使用格式文件显示注册表项。格式文件在本地运行get-itemproperty,以尝试显示属性。

从$ PSHOME \ Registry.format.ps1xml的底部键入Microsoft.Win32.RegistryKey:

<form  action="/update" method="post"> 
    <button class="btn edit_ico btn-sm" type="submit" name="update_btn" 
     value="<%=post.title%>"><i class="fas fa-edit"></i></button>
</form>

答案 1 :(得分:1)

tl;博士

  • 根本原因是formatting instructions中注册表项的 bug (自Windows PowerShell 5.1.18362.125和PowerShell Core 7.0.0起) -preview.2)导致远程信息和本地信息的意外混合-请参阅this GitHub issue

  • 最好的解决方法是仅使用Get-ItemProperty(不带-Name参数)而不是Get-Item


Mathias R. Jessen在对该问题的评论中提供了关键的指针,js2010's answer提供了一个有限的解决方法和根本原因的指针,但是值得提供更多背景信息:

PowerShell带有formatting instructions,类型为Microsoft.Win32.RegistryKey,由Get-Item输出,具有 registry 路径。

这些格式说明为默认(表格)视图定义了名为Property计算列,该列有助于显示输出注册表项的的摘要,这涉及使用Get-ItemProperty重新访问注册表 ,如js2010的答案所示。

但是,幕后Get-ItemProperty调用总是会访问 local 注册表-即使从其他计算机上检索到密钥也是如此,通过PowerShell远程处理,因此您将最终得到远程和本地信息的虚假组合

请注意,从技术上讲,Get-Item远程运行​​ 时,您在本地收到的 是原始{的{1}}对象,由于远程处理涉及序列化和反序列化。此近似值是一个具有原始对象属性值的静态副本的自定义对象,其(模拟的)类型名称为Microsoft.Win32.RegistryKey-注意前缀。

PowerShell根据输出对象的完整类型名称应用格式设置指令,但是在没有特定说明或给定Deserialized.Microsoft.Win32.RegistryKey类型的情况下,PowerShell为{{1 }},这就是导致这里出现问题的原因。

A-麻烦但与版本无关的 [1] -查看有问题的格式化指令的方法是运行以下命令:

Deserialized.<originalTypeName>

这将产生表视图的列名和定义:

<originalTypeName>

js2010答案中建议的解决方法-用管道输送到(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control | % { $colNames = $_.Headers.Label $colValues = $_.Rows.Columns.DisplayEntry.Value foreach ($i in 0..($colNames.Count-1)) { [pscustomobject] @{ ColumnName = $colNames[$i] ColumnValue = $colValues[$i] } } } | Format-Table -Wrap ColumnName ColumnValue ---------- ----------- Name PSChildName Property $result = (Get-ItemProperty -LiteralPath $_.PSPath | Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider | Format-List | Out-String | Sort).Trim() $result = $result.Substring(0, [Math]::Min($result.Length, 5000) ) if($result.Length -eq 5000) { $result += "..." } $result 的意思是有效的,因为它可以防止显示不适用的 local 信息:通过显式指定属性(即使使用通配符模式Format-Table *),也只会在输出中显示那些属性-不会显示有缺陷的计算列。

但是,虽然输出对象的真实Format-List * 属性可以访问当前注册表项中的值 names ,但它不提供实际的数据,即计算出的* 的方式。

通过对比,使用不带Property参数的Property代替Get-ItemProperty作为解决方法,将同时返回值名称和数据(即使在远程处理时也正确)甚至没有限制(-Name将输出限制为5000个字符。)

输出格式会稍有不同,但是所有信息都在其中。


[1]也就是说,该命令在PowerShell Core 中也有效,其中内置的格式设置指令不再作为外部Get-Item files < / em>,而是编译为可执行文件。