Undocumented changes to Powershell Scope handling

2019-05-18 16:37发布

问题:

Background: I've been writing a powershell script to migrate files from a Sharpoint 2010 instance on Windows Server '08 (with Powershell 2.x) to a Sharepoint 2013 instance on Windows Server '12 (with Powershell 3.x). I have that working but I noticed a change in how scope is handled.

Issue: I have the following code that gets run on both PSSessions ($param is a hashtable of parameter values)

Invoke-Command -session $Session -argumentlist $params -scriptblock `
{
    Param ($in)
    $params = $in # store parameters in remote session

    # need to run with elevated privileges to access sharepoint farm
    # drops cli stdout support (no echo to screen...)
    [Microsoft.SharePoint.SPSecurity]::RunWithElevatedPrivileges(
    {
        # start getting the site and web objects
        $site = get-spsite($params["SiteURL"])
    })
}

I noticed that in the PS 2.x remote session that assigning to $site also assigned to the same variable in Invoke-Command's scope, i.e. either scope gets passed or they share the same scope. BUT in the PS 3.x remote session assigning to $site does not change the value in Invoke-Command (true child scope).

My Solution: I wrote a function to compute the correct scope on each server which it calls and then uses the return value as an input to Get-Variable and Set-Variable's -Scope option. This solved my problem and allows assignment and access of the variables.

Function GetCorrectScope
{
    # scoping changed between version 2 and 3 of powershell
    # in version 3 we need to transfer variables between the
    # parent and local scope.
    if ($psversiontable.psversion.major -gt 2)
    {
        $ParentScope = 1 # up one level, powershell version >= 3
    }else
    {
        $ParentScope = 0 # current level, powershell version < 3
    }

    $ParentScope
}

The Question: Where, if anywhere, is this documented by Microsoft? (I couldn't find it in about_scope on TechNet, which says it applies to both 2.x and 3.x and is the standard reference I've seen in other questions).

Also, is there a better/proper way to do this?

回答1:

It is documented in the WMF 3 Release Notes in the section "CHANGES TO THE WINDOWS POWERSHELL LANGUAGE".

Script blocks executed as delegates run in their own scope

Add-Type @"
public class Invoker
{
    public static void Invoke(System.Action<int> func)
    {
        func(1);
    }
}
"@
$a = 0
[Invoker]::Invoke({$a = 1})
$a

Returns 1 in Windows PowerShell 2.0 
Returns 0 in Windows PowerShell 3.0