# Variables losing value in a scriptblock when passed to a function

**URL:** <https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416>\
**Category:** PowerShell Help\
**Created:** [June 24, 2015, 5:06am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416 "2015-06-24T05:06:32Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![adbertram](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/adbertram/32/193_2.png) [@adbertram](https://forums.powershell.org/u/adbertram)\
**Post date:** [June 24, 2015, 5:06am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/1 "2015-06-24T05:06:32Z")

</div>

```
function func1 {
$variable1 = 'somevariable'
$ScriptBlock = { echo $variable1 }
func2 -ScriptBlock $ScriptBlock
}

function func2 {
param($ScriptBlock)
## Open up $ScriptBlock and replace the text $variable1 with $using:variable1 here
Invoke-Command -Computer somecomputer -ScriptBlock $ScriptBlock
}
```

I would expect this to output ‘somevariable’ but instead I receive an error: “The value of the using variable ‘$using:variable1’ cannot be retrieved because it has not been set in the local session.”

When the scriptblock is passed from one function to another the value associated with $variable1 is stripped. Is there a way to pass that scriptblock from one function to the other and still be able to get it to work?

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 24, 2015, 5:40pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/2 "2015-06-24T17:40:41Z")

</div>

Adam, in your example you try to use _locally_ defined variable in _remote_ session. It is a cause of $null i think

You can send variable value to remote thru -argumentlist or use splatting like  
here [Splatting Parameters Pt 2 – Remote Possibilities | The Powershell Workbench](https://mjolinor.wordpress.com/2014/01/24/splatting-parameters-pt-2-remote-possibilities/)

btw, this variant show the variable value

```
function func1 {
$variable1 = 'somevariable'
$ScriptBlock = { echo $using:variable1 }
func2 -ScriptBlock $ScriptBlock
}

function func2 {
param($ScriptBlock)
Invoke-Command -computer somecomputer -ScriptBlock $ScriptBlock
}
```

---

<div class="post-metadata">

**Author:** ![adbertram](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/adbertram/32/193_2.png) [@adbertram](https://forums.powershell.org/u/adbertram)\
**Post date:** [June 25, 2015, 12:05am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/3 "2015-06-25T00:05:28Z")

</div>

Thanks for the reply, Max. I realize from that code that I gave it looks like I’m just passing the local variable and using the argument list is a method to do this but the script I’m having a problem with is much more complicated than that. I probably should have went into a little more detail on what I’m trying to do.

I’m using the AST to parse out and rename variables inside the scriptblock. I have an Invoke-ScriptBlock function that allows the user to pass a scriptblock with local session variables inside and a computername. If the computer name is local it will simply execute the scriptblock. If it’s remote, it will find all [VariableExpressionAst] objects in the AST tree and attempt to prepend “$using:” to them as to make those variables expand prior to getting serialized over a WinRM session.

I had a small comment about “Open up $Scriptblock” in my example where I was trying to explain this.

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 25, 2015, 1:27am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/4 "2015-06-25T01:27:12Z")

</div>

[time passed… :)]

I compile all your code and it works for me if it in one scope (one module, one session)  
But if I get func2 from module, but func1 from cmdline, I get your error

and THERE is the problem. different contexts 🙂  
and because you construct new scriptblock you get it without original variable context  
but unfortunately I cant find any way to get this context of a script block. Just description of it in .GetNewClosure() method

simple test

```
$m = new-module {
function func2 {
param($ScriptBlock)
#dir variable:
'vf2';
get-variable variable1;

& $ScriptBlock

}
export-modulemember -function func2
}

function func1 {
$variable1 = 'somevariable'
$ScriptBlock = { 'vf1'; get-variable variable1; echo "var1: $variable1" } #.GetNewClosure()
func2 -ScriptBlock $ScriptBlock
}

func1
```
`
vf2
get-variable : Cannot find a variable with the name 'variable1'.
At line:10 char:1
+ get-variable variable1;
+ ~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (variable1:String) [Get-Variable], ItemNotFoundException
    + FullyQualifiedErrorId : VariableNotFound,Microsoft.PowerShell.Commands.GetVariableCommand
vf1
Name Value

`

`variable1 somevariable
var1: somevariable`

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 25, 2015, 10:47pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/5 "2015-06-25T22:47:24Z")

</div>

hmm, this is some hidden scope, because  
if my scriptblock is `get-variable variable1
‘Global’,‘Local’,‘Script’ | %{ get-variable variable1 -Scope $_}
`  
I get variable1 on first try but not any other 3 tries

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 25, 2015, 10:54pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/6 "2015-06-25T22:54:20Z")

</div>

i find it in parent scope  
`get-variable variable1 -Scope 1``  
but only inside func1

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 26, 2015, 12:39am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/7 "2015-06-26T00:39:21Z")

</div>

I Get it to work (pretty ugly variant, need some work, but not today 🙂 )

```
function func1 {
$variable1 = 'somevariable'
$ScriptBlock = { echo $variable1 }
func2 -ScriptBlock $ScriptBlock.GetNewClosure()
}

function func2 {
param($ScriptBlock)
## Open up $ScriptBlock and replace the text $variable1 with $using:variable1 here

$AstVariables = $ScriptBlock.Ast.FindAll({ $args[0] -is [System.Management.Automation.Language.VariableExpressionAst] }, $true)
$BlockText = $ScriptBlock.Ast.Endblock.Extent.Text
$selectProps = @(
	@{ n = 'Variable'; e = { $_.VariablePath.UserPath } }
	@{ n = 'Start'; e = { $_.Extent.StartOffset - $ScriptBlock.Ast.Extent.StartOffset -2 } }
	@{ n = 'End'; e = { $_.Extent.EndOffset - $ScriptBlock.Ast.Extent.StartOffset - 2} }
	'Parent'
)
$VariableLocations = $AstVariables | Select-Object -Property $selectProps | Sort-Object 'Start' -Descending
$VariableLocations | foreach {
	# If the variable is not inside double quotes and won't need to be expanded
	if ($_.Parent.Extent.Text -notmatch '^".*"$')
	{
		$NewName = '{0}:{1}' -f '$using', $_.Variable
	}
	else
	{
		$NewName = '$({0}:{1})' -f '$using', $_.Variable
	}
	$StartIndex = $_.Start
	$EndIndex = $_.End - $_.Start
	$BlockText = $BlockText.Remove($StartIndex, $EndIndex).Insert($StartIndex, $NewName)
}
# here is the magic
$SB = $ScriptBlock.Module.NewBoundScriptBlock([scriptblock]::Create($BlockText))
$SB1 = $ScriptBlock.Module.NewBoundScriptBlock({param($name) Get-Variable $name})

$AstVariables | Foreach-Object{
#TODO: here we need filtering, we do not want to import the same variables all the time
	$varname = $_.VariablePath.UserPath
	New-Variable -Name $varname -Value (& $SB1 $varname)
}

Invoke-Command -Computer somecomputer -ScriptBlock $SB
}

export-modulemember -function func2
```

pay attention that func1 call func2 with GetNewClosure() - Is’s important

---

<div class="post-metadata">

**Author:** ![adbertram](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/adbertram/32/193_2.png) [@adbertram](https://forums.powershell.org/u/adbertram)\
**Post date:** [June 26, 2015, 12:40am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/8 "2015-06-26T00:40:39Z")

</div>

Thanks for your hard work on this, Max! I’m planning on giving it a shot today.

---

<div class="post-metadata">

**Author:** ![adbertram](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/adbertram/32/193_2.png) [@adbertram](https://forums.powershell.org/u/adbertram)\
**Post date:** [June 26, 2015, 9:28am UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/9 "2015-06-26T09:28:32Z")

</div>

Thanks, Max. That looks like that did it!

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 28, 2015, 5:03pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/10 "2015-06-28T17:03:33Z")

</div>

And where is the final variant ? 🙂  
btw, I make an error here. we need only value  
`$SB1 = $ScriptBlock.Module.NewBoundScriptBlock({param($name) Get-Variable $name -ValueOnly})`

Besides variable filtering there is potential to optimize it removing (get-variable) but only if there is any way to use $variable:variablename syntax or any other way to get variable value by its name but do not use cmdlet.

And finally, in a function func2 we need to use highly unique internal variable names so we do not overwrite it with $Scriptblock variables when importing it in our own context

---

<div class="post-metadata">

**Author:** ![max-kozlov](https://avatars.discourse-cdn.com/v4/letter/m/50afbb/32.png) [@max-kozlov](https://forums.powershell.org/u/max-kozlov)\
**Post date:** [June 28, 2015, 7:15pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/11 "2015-06-28T19:15:52Z")

</div>

find a better way for variable setting

`function func2 {
[CmdletBinding()]
…
$AstVariables | Foreach-Object{
#TODO: here we need filtering, we do not want to import the same variables all the time
$varname = $_.VariablePath.UserPath
New-Variable -Name $varname -Value ($PSCmdlet.SessionState.PSVariable.GetValue($varname))
}
`  
$SB1 do not needed  
thanks to [http://get-powershell.com/](http://get-powershell.com/) (link from your twitter 🙂 )

---

<div class="post-metadata">

**Author:** ![dotnVo](https://avatars.discourse-cdn.com/v4/letter/d/4af34b/32.png) [@dotnVo](https://forums.powershell.org/u/dotnVo)\
**Post date:** [May 16, 2024, 8:45pm UTC](https://forums.powershell.org/t/variables-losing-value-in-a-scriptblock-when-passed-to-a-function/4416/12 "2024-05-16T20:45:05Z")

</div>


