Well, to be fair, I’ve never had any problem getting a function to run equally well in either mode. And I don’t think that’s your problem here, either. I think your problem has a lot more to do with using nested functions (which get awkward), and not tracking variable scope (which nested functions make harder to do). This business of using out-of-scope variables to pass data round is a horrible idea, and it trips people up all the time. It’s a bad design, and I truthfully think that’s where some of your problems stem from.
Take this:
{
$b1 = Get-Variable -Scope 1 -Name b
$b1.Value += $A
}
PowerShell passes variables ByValue, not ByReference. $b1 does not “point” to the original higher-scope $b; it is a copy of $b. Changing $b1 doesn’t change $b. Further, you’re still referring to out-of-scope variable $A. Not trying to be a jerk here, but you’re working against the way the shell prefers to work, and you’re going to make life harder on yourself if you go that route.
If I had a function whose sole purpose was to append a value to a variable, I wouldn’t write that as a function. I realize that you’re just doing that by way of illustration, but let’s look at what you’re trying to do:
“The embedded function being defined is the ‘deploy’ function and the last line of this function is supposed to add the current hostname to a variable array for reporting.”
Here’s how I’d handle that.
param([string[]]$hostnames)
$success_hosts = @()
$failed_hosts = @()
function check_whatever([string]$hostname) {
# check stuff.
if ($stuff -eq $working) { write $true } else { write $false }
}
foreach ($hostname in $hostnames) {
if (check_whatever $hostname) {
$success_hosts += $hostname
} else {
$failed_hosts += $hostname
}
}
In other words, I’m keeping all the data in the scope where it needs to be used. I’m not asking the embedded check_whatever function to look “up” the scope to see what the current host is - I’m explicitly passing in the host to check. And check_whatever isn’t concatenating values to a variable it doesn’t “own.” It’s returning a True/False value, and the parent scope - which is the one tracking success/fail - does the concatenation.
I wrote it before, but I’ll emphasize it: Functions should not access, or modify, anything outside their own scope, ever. Pass in what they need as parameters. They do their job, and produce a result to the pipeline. The parent can then examine that result, and do whatever needs to be done as a consequence. If you’re ever in a situation where you think, “well, this is an exception to the rule - in this case, I need to access out-of-scope stuff,” then you’re not thinking about it the right way.
All this is triply true for a function running in the pipeline, because the scope has a different lifetime. The BEGIN{} block runs before pipeline input has been bound, so you don’t necessarily have all your variables. The PROCESS{} block runs, but only one value will have been bound from the pipeline. This extra scope complexity can really throw a wrench in things when you’re not following recommended practices, and you end up working around the way the shell was designed.
I think you’ll find things easier if you kind of re-examine your overall approach to tracking data within the process. I’d be happy to help pseudo-code it with you, if you want, to maybe offer a different perspective or some alternate approaches.