Ok so… Long time, no see, I know that. Last year was kind of hectic (to say as an understatement). A lot of things happened, a lot of things moved. And I hope to share some of the updates with my audience this year.

And what better way to hit the ground breaking again with a short and crisp post. So, let’s get the ball rolling.

Recently tinkered with an old PowerShell script of mine. And learnt a few lessons. Which maybe I had forgotten, or maybe are not well known about PowerShell.

One of those learnings was assigning a global variable inside a PowerShell function does not actually change the global variable. It rather creates a local variable with the same name. Let’s take an example.

$dbScripts = New-Object "System.Collections.Generic.List[System.String]";

function populateDbScripts ($con) {
	#Some code
	$dbScripts = $deserializeMethod.Invoke($serializer, $dbScriptsStr);
}

function dbScriptRegistered ($con, $scriptFilePath) {
	$scriptFileName = [System.IO.Path]::GetFileName($scriptFilePath);
	if($dbScripts.Contains($scriptFileName) -eq $false) {
		return $false;
	} else {
		return $true;
	}
}

#Some code
populateDbScripts $con;
#The function call below accesses empty $dbScripts global variable, instead of the value populated in Line 5 above.
dbScriptRegistered $con $scriptPath;

The script extract above creates a global List of strings at the beginning. It then defines 2 functions populateDbScripts and dbScriptRegistered. The first function assigns the value of the global $dbScripts variable, while the second function tries to use it.

One would have expected that line 10 accesses the variable populated by line 5 (the global $dbScripts variable). However line 5 actually creates a local copy of the $dbScripts variable accessible only inside the populateDbScripts function.

Essentially, we can access global variables inside a PowerShell function but cannot change the reference of the global variable to point to another object.

However you can still mutate (change internal state) of the global variable using its own functions. The idea above was to populate the global $dbScripts variable with something fetched from a Sql Server database. The same can be achieved with a slightly different approach where you store the results in a temporary local variable and then use the methods of existing global variable (if available) to mutate its state.

$dbScripts = New-Object "System.Collections.Generic.List[System.String]";

function populateDbScripts ($con) {
	#Some code
	$dbScripts2 = $deserializeMethod.Invoke($serializer, $dbScriptsStr);
	$dbScripts.AddRange($dbScripts2);
}

function dbScriptRegistered ($con, $scriptFilePath) {
	$scriptFileName = [System.IO.Path]::GetFileName($scriptFilePath);
	if($dbScripts.Contains($scriptFileName) -eq $false) {
		return $false;
	} else {
		return $true;
	}
}

#Some code
populateDbScripts $con;
#The function call below now accesses the populated $dbScripts global variable.
dbScriptRegistered $con $scriptPath;

The only change we made in the above code was to store the results of database call in a temporary variable in line 5 and then use the AddRange method of List class to update the results in the global $dbScripts variable. This actually works and the second function call now accesses the updated variable from the first function call.

What this also means is we cannot re-assign global references to objects of immutable classes like String inside a PowerShell function nor can cause any change in their state inside the function.

Happy learning!! (and there went a few hours of a night’s sleep :-)). Hope to publish another couple minor learnings with PowerShell shortly again.