Invoke-Command on remote session returns local values

后端 未结 2 1984
春和景丽
春和景丽 2021-01-15 13:45

Question

Should the script block of Invoke-Command, when run with a PSSession, always run on the remote computer?

Context

I ran the

相关标签:
2条回答
  • 2021-01-15 13:46

    Pipe it to fl * or ft * so it doesn't use the format file to display the registry keys. The format file runs get-itemproperty locally to try to display the properties.

    From the bottom of $PSHOME\Registry.format.ps1xml for type Microsoft.Win32.RegistryKey:

    <ScriptBlock>
      $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
    </ScriptBlock>
    
    0 讨论(0)
  • 2021-01-15 13:56

    tl;dr:

    • The root cause is a bug in the formatting instructions for registry keys (as of Windows PowerShell 5.1.18362.125 and PowerShell Core 7.0.0-preview.2) leading to the unexpected mix of remote and local information - see this GitHub issue.

      • As an aside: Similarly, PowerShell's ETS (extended type system) can introduce problems too, as shown in Mathias' answer here.
    • The best workaround is to simply use Get-ItemProperty (without a -Name argument) instead of Get-Item.


    Mathias R. Jessen has provided the crucial pointer in a comment on the question, and js2010's answer provides a limited workaround and a pointer to the root cause, but it's worth providing more background information:

    PowerShell comes with formatting instructions for type Microsoft.Win32.RegistryKey, as output by Get-Item with a registry path.

    These formatting instructions define a calculated column named Property for the default (tabular) view, which helpfully shows a summary of the output registry key's values, which involves accessing the registry again, using Get-ItemProperty as shown in js2010's answer.

    However, that behind-the-scenes Get-ItemProperty call always accesses the local registry - even when the keys were retrieved from a different machine, via PowerShell remoting, so you'll end up with a spurious mix of remote and local information.

    Note that, technically, when Get-Item is run remotely, what you receive locally is an approximation of the original Microsoft.Win32.RegistryKey object, due to the serialization and deserialization involved in remoting. This approximation is a custom object with static copies of the original object's property values, and its (simulated) type name is Deserialized.Microsoft.Win32.RegistryKey - note the prefix.

    PowerShell applies formatting instructions based on the full type name of output objects, but in the absence of a specific instructions or a given Deserialized.<originalTypeName> type, PowerShell applies the instructions for <originalTypeName>, which is what causes the problems here.

    A - cumbersome, but edition-agnostic[1] - way to see the problematic formatting instruction is to run the following command:

    (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
    

    This yields the column names and definitions for the table view:

    
    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                                                                                     
    
    

    The workaround suggested in js2010's answer - piping to Format-Table * or Format-List * is effective in the sense that it prevents the inapplicable local information from being displayed: by specifying properties explicitly (even by wildcard pattern *), only those properties are displayed on output - not also the flawed calculated column.

    However, while the true Property property of the output objects provides access to the value names in the registry key at hand, it doesn't provide the actual data, the way that the calculated Property column does.

    By contrast, using Get-ItemProperty without a -Name argument in lieu of Get-Item as a workaround returns both value names and data (correctly even when remoting) and even does so without restrictions (whereas Get-Item limits output to 5000 chars.)

    The output format will be slightly different, but all the information is there.


    [1] That is, the command works also in PowerShell Core, where the built-in formatting instructions are no longer maintained as external *.format.ps1xl files and are instead compiled into the executable.

    0 讨论(0)
提交回复
热议问题