0

The below code is annoyingly not just returning the contents of the $t variable, of even if the contents of $t are instead returned like return "!! Quitting...":

function AddADUser
{

....

  $Answer = [System.Windows.Forms.MessageBox]::Show("WARNING: The below account already exists in domain $domain. Continue to update it (yes), or quit and rerun should it be needed after investigation (no)?", "$ScriptFileName", "YesNo")

  if($Answer -eq "no")
  {
    write-host "Before return"
    $t = "!! Quitting script on user cancellation, due to not wanting to update existing user(s)   data with new details provided`r`n"
    write-host "$t"
    return $t
  }
}

Instead the console output before and after the function returns:

Before return

!! Quitting script on user cancellation, due to not wanting to update existing user(s) data with new details provided

 System.Management.Automation.PSCustomObject !! Quitting script on user cancellation, due to not wanting to update existing user(s) data with new details provided

$t contains nothing prior to being assigned after the first write-host

This must be a quirk to Powershell function return, but I can't figure it out where the darned System.Management.Automation.PSCustomObject is coming from, apart from maybe if the function returned at a later stage, it would contain an object (I have checked with a write-host it is not proceeding to that stage though). What is going on!?

6
  • 3
    With the current code is not possible to reproduce the issue. You need to provide a reproducible example of the issue. Commented Aug 14 at 23:13
  • 3
    Any output - be it from a PowerShell command, an operator-based expression, or a .NET method call - that is neither captured in a variable nor redirected (sent to a file or via the pipeline to another command) is implicitly output from a script or function. To simply discard such output, use $null = .... If you don't discard such output, it becomes part of a script or function's "return value" (stream of output objects), which is not limited to what is passed to an - optional - return statement. See this answer for more information. Commented Aug 15 at 1:10
  • 1
    Per @mklement0’s comment, you’ve got a line in your original code which is an expression that results in an uncaptured PSCustomObject value being sent to the function’s output stream. Note that write-host results in immediate console output, but the values from the output stream could potentially be delayed for… reasons… so the offending line could even be in the …. part of your code. Check each line of your original function for uncaptured expressions, and suppress them with $null = … as suggested above. Commented Aug 15 at 4:21
  • 1
    Trouble shooting hint: approach your issue from the same angle as we (base on your "minimal reproducible example") supposed to do this: open a new PowerShell session, paste your function above (without the 3 dots) and invoke it... Commented Aug 15 at 8:42
  • 1
    Ironically, it was this command that I had without write-host that was producing the PSObject `write-host "$($AccountsMatching.gettype())". Like this, the output is no longer polluted! Commented Aug 15 at 13:45

1 Answer 1

2

While the question doesn't contain enough information to diagnose the source of the unwanted output, let's imagine a simple scenario:

function AddUser {
  param(<# ... #>)

  # oops, accidentally leaking a custom object
  [pscustomobject]@{ lol = 123 }

  $t = 'Value we actually want'

  return $t
}

It's important to understand here that PowerShell functions can emit output at any time before the function returns - in fact, any pipeline or value expression that outputs anything during invocation will have its resulting output slipstreamed back up to the caller, as suggested by the inline comment above.

What this means is that if we invoke AddUser as-is, it'll emit the custom object first, followed by $t, so any variable you assign the output of AddUser from will suddenly contain an array with $t in the last slot, rather than just the scalar value stored in $t on its own.

Thankfully, there's a generic workaround you can use in this situation:

  • Call AddUser with the dot-source invocation operator (.)
  • Wrap the call in a scriptblock
  • Return only $t from the wrapper
$onlyT = & {
  $null = . AddUser @paramArgs
  return $t
}

Value of $t is returned and assigned to $onlyT outside the wrapper, whereas anything emitted during execution of AddUser is suppressed with the inner assignment to $null.

The reason this works is that . invokes AddUser in the calling scope, meaning local variables like $t will linger inside the wrapper block.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.