powershell - 远程 session 上的 Invoke-Command 返回本地值

标签 powershell formatting powershell-5.0 winrm invoke-command

问题

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

上下文

我针对服务器列表运行了以下 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)

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

我猜这是一个错误;但因为到目前为止我还没有玩过 PSSession ,所以想在这里检查一下,以防我对这些命令的理解/使用存在问题,或者在使用时是否有需要注意的已知问题PSSessions。

最佳答案

tl;博士:

  • 根本原因是 formatting instructions 中的错误对于导致远程和本地信息意外混合的注册表项(从 Windows PowerShell 5.1.18362.125 和 PowerShell Core 7.0.0-preview.2 开始) - 请参阅 GitHub issue #10341 .

  • 最好的解决方法就是简单地使用 Get-ItemProperty (没有 -Name 参数)而不是 Get-Item .


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

PowerShell 附带 formatting instructions对于类型Microsoft.Win32.RegistryKey ,如 Get-Item 的输出带有注册表路径。

这些格式化指令定义了一个名为 Property计算列对于默认(表格) View ,该 View 有助于显示输出注册表项的值的摘要,其中涉及使用 Get-ItemProperty 再次访问注册表。如js2010的答案所示。

然而,幕后Get-ItemProperty调用始终访问本地注册表 - 即使 key 是通过PowerShell远程处理从另一台计算机检索的,因此您将最终得到远程和本地信息的虚假混合

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

PowerShell 根据输出对象的完整类型名称应用格式化指令,但没有特定指令或给定 Deserialized.<originalTypeName>类型,PowerShell 应用 <originalTypeName> 的指令,这就是导致这里问题的原因。

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

(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

这会生成 TableView 的列名称和定义:


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                                                                                     
                                                                                                                                         

js2010 的答案中建议的解决方法 - 管道到 Format-Table *Format-List *是有效的,因为它可以防止显示不适用的本地信息:通过显式指定属性(甚至通过通配符模式 * ),只有那些属性显示在输出 - 也不是有缺陷的计算列。

然而,虽然真实的Property输出对象的属性提供对当前注册表项中的值名称的访问,它不提供实际的数据,方式计算出Property 确实如此。

相比之下,使用 Get-ItemProperty没有-Name参数代替 Get-Item作为解决方法,返回值名称和数据(即使在远程处理时也正确),甚至没有限制(而 Get-Item 将输出限制为 5000 个字符。)

输出格式会略有不同,但所有信息都在那里。


[1] 也就是说,该命令也适用于 PowerShell Core,其中内置格式化指令不再维护为外部 *.format.ps1xl 文件并被编译成可执行文件。

关于powershell - 远程 session 上的 Invoke-Command 返回本地值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57400948/

相关文章:

json - 将Curl转换为Invoke-RestMethod

接受正则表达式组的前瞻部分

python - 替换for循环Python中的一部分文本元素

powershell - 尝试在带有 IE 11 的 Powershell 中使用 getElementById,来自 HRESULT : 0x800A01B6 的错误异常

powershell - Powershell-错误检查以查找和替换(ForEach对象)

PowerShell:传递的参数不是格式字符串

Javascript:使用 toLocaleString + Tofixed

mysql - 在用户定义的函数(mysql)中格式化日期

powershell - Powershell 5 中的类,需要帮助创建 $foo.bar.run()

powershell - 代码块阻止脚本运行,但以交互方式运行