Custom Task variables aren't in the correct scope. #37

AnthonyMastrean opened this Issue May 31, 2012 · 3 comments


None yet
3 participants

I'm trying to write a declarative custom task. Normally, your tasks look like this

Task echo {
    Write-Host "hello, this is my task"

But, if you want to be declarative and separate the definition and execution of the task, you'll want something like this

Echo-Task hello { param($x)
    $x.message = "hello, this is my task"

Yeah, it's a little verbose for this trivial example, but it's powerful for more complicated tasks. And it's what's happening over in the Albacore library (Rake tasks for .NET & some custom tasks I've written). So, I have an implementation of the Echo task.

function Echo-Task([string]$name, [scriptblock]$def = $null) {
    $x = @{}
    & $def($x)

    $action = {
        Write-Host "$($x.message)"

    Task $name $action @args

When I run this, I see the hashtable initialize, I can step through the $def block and see the hashtable properties initialized, the scriptblock is set, and the real psake Task is run. However, my message is not printed to the console! On further inspection, the variable $x has no value in the $action scriptblock.

I see on StackOverflow that there's a whole mess going on about scriptblock variable scoping. But, I don't control the execution of my scriptblock, psake does. I looked into the psake module at Invoke-Task and it's simply using the call operator

& $task.Action 

I see in another part of the module, Invoke-Psake, a note about dot-sourcing a scriptblock in the module scope or else.

# Simple dot sourcing will not work. We have to force the script block into our
# module's scope in order to initialize variables properly.
. $MyInvocation.MyCommand.Module $initialization

Does the call to the $action need to be modified to support this craziness?

The problem I'm having is almost the opposite of the one described on StackOverflow and here on Microsoft Connect. They say that

When invoked in a script, a ScriptBlock (ordinarily) treats the scope in which it was invoked as its parent scope.

I want my script block to treat it's declaration scope as its execution scope. I was able to get it working by jamming the $script: scope before every variable. But, that doesn't feel so good.

function Echo-Task([string]$name, [scriptblock]$def = $null) {
    $script:x = @{}
    & $def($script:x)

    $action = {
        Write-Host "$($script:x.message)"

    Task $name $action @args

viswaug commented Jul 11, 2013

I think "GetNewClosure" might be your solution here. Have you tried that?


damianh commented Mar 31, 2014

Yeah GetNewClosure would appear to be correct approach. Re-open if that doesn't work.

@damianh damianh closed this Mar 31, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment