We came across some strange PowerShell behavior when it comes to function return values.
Some context:
In our scripts and modules, we always want to set $ErrorActionPreference and $DebugPreference global default value, which can be restored using the function. Suppose we want to set additional messages in our log for $DebugPreference to "Continue" .
So, we are doing something like this:
$DebugPreference = Get-GlobalDebugPreference
Whether this is a good approach or not is not part of this question.
Problem:
On some machines with different versions of PowerShell, this just didn't work as expected. When we called our function, which returns "Continue ", $DebugPreference did retain the correct value, but we did not have any additional log messages in our log file.
What we learned:
Whether Write-Debug works or not is somehow related to how you return the "Continue ” value from the function that manages our global defaults.
eg. when the function looks like this, Write-Debug behaves as expected and prints debugging messages in PowerShell 3. However, in PowerShell 2, the value is "Continue ”, but Write-Debug does not print any additional messages.
function Get-GlobalDebugPreference { return "Continue" } $DebugPreference = Get-GlobalDebugPreference
However, if we simply drop the value onto the shell and omit the return , it will work for all versions of PowerShell v2 +.
function Get-GlobalDebugPreference { "Continue" } $DebugPreference = Get-GlobalDebugPreference
There are several ways to return a value from a function in PowerShell. For some methods this works for PS version 2, some work with v3. However, using Write-Output to return "Continue " does not work until v5.
In my opinion, all the different approaches should work fine and should be interchangeable. That the behavior is different for such basic things is relevant and makes PowerShell somewhat unpredictable.
I compiled a small script that sets up $DebugPreference with various approaches for returning functions that should behave the same. If you run it with different versions of PowerShell, you get different amounts of Debug output. Note that the $DebugPreference variable has the correct value and the correct type after each step, but Write-Debug only works after some of them.
Can someone explain what is going on here?
Running with "powershell.exe -version 2 ... " gives me this result:
Starting to test return values. Current DebugPreference: SilentlyContinue 1 Obtained the value with return: Continue with Type string 2 Obtained the value from shell with return after: Continue with Type string DEBUG: 2 After Get-GlobalDefaultWithReturnAfterwards 3 Obtained the value from shell with return after: Continue with Type System.Management.Automation.ActionPreference DEBUG: 3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference 4 Obtained the value without return: Continue with Type string DEBUG: 4 After Get-GlobalDefaultWithOutReturn 5 Obtained the value with Write-Output: Continue with Type string 6 Obtained the value with Write-Output: Continue with Type System.Management.Automation.ActionPreference 7 Obtained piped value with Write-Output : Continue with Type string 8 Set the value directly: Continue with Type string DEBUG: 8 After setting the value directly
Running with "powershell.exe -version 5 ... " gives me this result:
Starting to test return values. Current DebugPreference: SilentlyContinue 1 Obtained the value with return: Continue with Type string DEBUG: 1 After Get-GlobalDefaultWithReturn 2 Obtained the value from shell with return after: Continue with Type string DEBUG: 2 After Get-GlobalDefaultWithReturnAfterwards 3 Obtained the value from shell with return after: Continue with Type System.Management.Automation.ActionPreference DEBUG: 3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference 4 Obtained the value without return: Continue with Type string DEBUG: 4 After Get-GlobalDefaultWithOutReturn 5 Obtained the value with Write-Output: Continue with Type string 6 Obtained the value with Write-Output: Continue with Type System.Management.Automation.ActionPreference DEBUG: 6 After Get-GlobalDefaultWriteOutputActionPreference 7 Obtained piped value with Write-Output : Continue with Type string 8 Set the value directly: Continue with Type string DEBUG: 8 After setting the value directly
script:
function Get-GlobalDefaultWithReturn { return "Continue" } function Get-GlobalDefaultWithReturnAfterwards { "Continue" return } function Get-GlobalDefaultWithReturnAfterwardsActionPreference { ([System.Management.Automation.ActionPreference]::Continue) return } function Get-GlobalDefaultWithOutReturn { "Continue" } function Get-GlobalDefaultWriteOutput { Write-Output "Continue" } function Get-GlobalDefaultWriteOutputActionPreference { Write-Output ([System.Management.Automation.ActionPreference]::Continue) } $DebugPreference = "SilentlyContinue" Write-Host "Starting to test return values. Current DebugPreference: $DebugPreference" $DebugPreference = Get-GlobalDefaultWithReturn Write-Host "1 Obtained the value with return: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "1 After Get-GlobalDefaultWithReturn" $DebugPreference = "SilentlyContinue" $DebugPreference = Get-GlobalDefaultWithReturnAfterwards Write-Host "2 Obtained the value from shell with return after: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "2 After Get-GlobalDefaultWithReturnAfterwards" $DebugPreference = "SilentlyContinue" $DebugPreference = Get-GlobalDefaultWithReturnAfterwardsActionPreference Write-Host "3 Obtained the value from shell with return after: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "3 After Get-GlobalDefaultWithReturnAfterwardsActionPreference" $DebugPreference = "SilentlyContinue" $DebugPreference = Get-GlobalDefaultWithOutReturn Write-Host "4 Obtained the value without return: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "4 After Get-GlobalDefaultWithOutReturn" $DebugPreference = "SilentlyContinue" $DebugPreference = Get-GlobalDefaultWriteOutput Write-Host "5 Obtained the value with Write-Output: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "5 After Get-GlobalDefaultWriteOutput" $DebugPreference = "SilentlyContinue" $DebugPreference = Get-GlobalDefaultWriteOutputActionPreference Write-Host "6 Obtained the value with Write-Output: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "6 After Get-GlobalDefaultWriteOutputActionPreference" $DebugPreference = "SilentlyContinue" Get-GlobalDefaultWriteOutput | % { $DebugPreference = $_ } Write-Host "7 Obtained piped value with Write-Output : $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "7 After Get-GlobalDefaultWriteOutput with pipe" $DebugPreference = "SilentlyContinue" $DebugPreference = "Continue" Write-Host "8 Set the value directly: $DebugPreference with Type $($DebugPreference.GetType())" Write-Debug "8 After setting the value directly"