Functions in PS don’t behave quite like other languages. They keep variable scope local but anything else can easily have global side effects. Biggest rule I’ve learned to follow is to never have a PS function output anything until the end when I’m ready to return a value. So if I wanted to get the result of an operation, within the function I’m catching all output to a variable, and returning that variable only at the end. Simple example, I want the cube of a value- function valCubed ($x) { $y = x * $x return $y} So to call it and capture the value to pass on- $z = valCubed 4 which should result in $z being equal to 64. Where this really gets important is when something can return multiple results or could fail, like a Get-ADuser or Test-Connection. You want to trap the output completely via -ErrorAction SilentlyContinue or a try/catch, so that the function outputs nothing but the final value at the return. Your code will scale a lot better and be easier to debug. (Pardon the poor code formatting, typing on my phone) Answer from abacushex on reddit.com
🌐
Reddit
reddit.com › r/powershell › passing variables between functions
r/PowerShell on Reddit: Passing Variables Between Functions
January 5, 2020 -

Is there a way to pass variables between functions in PowerShell? I did some googling but was surprised to come up short, all of the information was pretty messy.

Is there a way I could get f2 in the below example to actually output the value of $test ?

Function f1 {
    $test = "Hello world"
}

Function f2 {
    echo $test
}

f2
🌐
Reddit
reddit.com › r/powershell › how to pass variable values between functions?
r/PowerShell on Reddit: How to pass variable values between functions?
May 24, 2024 -

Alright gang, bear with me...this is a short question but there's a LOT of supporting documentation. What it boils down to is: if I define a variable's value within a function, how do I pass that to other functions that run subsequently?

I've constructed a rather lengthy script that breaks things down into a series of functions so they can be easily mixed and matched, but I noticed that if I didn't define a specific variable outside the functions (specifically, $nextName, which in this version is defined right at the start of the ForEach-Object loop), it wasn't reaching the last function that was running.

I'd post the script that I have, but it's WAY too long, so we'll have to just talk in theory. This is the sequence of commands I want to run:

$num = 1

$moveThese | ForEach-Object {
$relativePath = $_.FullName.Substring($sourceDir.Length)
$nextName = Join-Path -Path $targetDir -ChildPath $relativePath
$lastSize = $TotalSize;
$TotalSize += $_.Length

ADDSIZE
#HOLDFAST

TOTALSTATS
#HOLDFAST

DISPLAYSTATS
#HOLDFAST

STATSCHECK
#HOLDFAST

IFEXIST
#HOLDFAST

if ($existFile -eq "Y") {
SIZECHECK
HOLDFAST
}

if ($sizeMatch -eq "Y") {
HASHCEHCK
HOLDFAST
}

if ($hashMatch -eq "Y") {
REMOVEITEM
HOLDFAST
}
ElseIf ($hashMatch -eq "N") {
RENAMEITEM
HOLDFAST
}

if ($fileDeleted -ne "Y") {
COPYITEM
#HOLDFAST
}
$num += 1
}

So you see that in this version I'm defining the variable $nextName right at the start of the loop, but originally the first two lines after ForEach-Object were part of the function IFEXIST. Trouble was, when we got to the end and COPYITEM ran, $nextName was empty, so the value wasn't getting passed down.

And the issue is, IFEXIST defines a variable $existFile...and what I'm thinking is that if $nextName wasn't getting passed from IFEXIST, then $existFile isn't getting passed on either.

So...how do I get one function to read a variable defined in another in a setup like this?

EDIT: adding verbiage of function IFEXIST

function IFEXIST
{
Write-Host `n
Write-Host "FUNCTION IFEXIST" -BackgroundColor DarkRed -ForegroundColor Yellow
Write-Host `n

$script:relativePath = $_.FullName.Substring($sourceDir.Length)
$script:nextName = Join-Path -Path $targetDir -ChildPath $relativePath

Write-Host "THISFILE: "
Write-Host $_.FullName -ForegroundColor Yellow
Write-Host "`nTARGETFILE:"
Write-Host $nextName -ForegroundColor Yellow
Write-Host `n

If(Test-Path -Path $nextName) {
$script:existFile = "Y"
Write-Host "This file exists in TARGETDIR."
} else {
$script:existFile = "N"
Write-Host "This file does not exist in TARGETDIR."
}
}
Top answer
1 of 9
14
Functions in PS don’t behave quite like other languages. They keep variable scope local but anything else can easily have global side effects. Biggest rule I’ve learned to follow is to never have a PS function output anything until the end when I’m ready to return a value. So if I wanted to get the result of an operation, within the function I’m catching all output to a variable, and returning that variable only at the end. Simple example, I want the cube of a value- function valCubed ($x) { $y = $x * $x * $x return $y} So to call it and capture the value to pass on- $z = valCubed 4 which should result in $z being equal to 64. Where this really gets important is when something can return multiple results or could fail, like a Get-ADuser or Test-Connection. You want to trap the output completely via -ErrorAction SilentlyContinue or a try/catch, so that the function outputs nothing but the final value at the return. Your code will scale a lot better and be easier to debug. (Pardon the poor code formatting, typing on my phone)
2 of 9
11
Functions have variables inside its own scope and stays locked there. You can use a global scope $global:variablename = value Global function lives in your session until it is closed. You can pop one from somewhere in a function and then access it somewhere else in some other scope. It is very much not recommended to use these everywhere. Your function should be a singular little thing that does something, and spit out data relevant to it. Should work for how you want it though. Try that as a little exercise.
Top answer
1 of 3
13

Variables in powershell are context sensitive. If I define a function like:

$bar = "Hi"
function foo {
   $bar = "Hey!"
}
$bar <-- returns "Hi"

Then the $bar variable is not available to me outside that function. To make variables available outside of functions then you can control the scope of a function. If I set a variable in a function using the script or global prefix then the variable will be available for the whole script or globally in powershell runspace. See here:

function foo {
   $script:fooVar = "world"
}

function bar {
   foo
   $global:barVar = "Hello " + $fooVar
}

The variable $fooVar in the foo function will be available to all other functions within the script due to the scope prefix script of the variable $fooVar. The barVar function will be available globally in the runspace. I.e. when your script has finished the variable is still present back at the command line and even to other scripts.

As you can see in the bar function I first call foo and then use the foovVar variable. When I use the $fooVar variable I don't have to specify the $script:fooVar, I can if I want to but it's not necessary.

These are all valid variable assignments:

script:bbb = 123
$global:ccc = 123 

So in your case use $script:source and $script:target or $global:source and $global:target. For more info run the following command:

Help About_Scope
2 of 3
1

the hackish way is to add this to the top:

$Gobal:source = ""
$Gobal:target = ""

and search and replace $source with $Gobal:source and $target with $Gobal:target - then you can use these new global variables at any point in the script.

As suggested you could protect them in another function but for a simple job\automated task that might be overkill. Depends what it's for.

🌐
PowerShell Forums
forums.powershell.org › powershell help
Passing Variables To/From a Function - PowerShell Help - PowerShell Forums
July 26, 2021 - I cobbled together some things I found on teh interwebs to make sure a directory name is properly formatted for use. I am having trouble getting the corrected variable back from the function. When the script wasn’t “doing right”, I tested it by just adding the " \ " character to the variable in the function.
🌐
Reddit
reddit.com › r/powershell › how do you pass variables from one function to another within a module?
r/PowerShell on Reddit: How do you pass variables from one function to another within a module?
March 23, 2016 -

I created a PowerShell module to make RESTful API calls to an application. For simplicity, let's say that this module has three functions:

  • Function getProperty($name)

  • Function createProperty($name, $value, $description)

  • Function updateProperty($name, $value, $description)

Whenever createProperty or updateProperty functions are called, those functions in their current form will call getProperty before and after execution. I basically want to verify a property exists before attempting to update or verify it doesn't exist before attempting to create a new one. In order to do that, I've created a boolean variable in the script scope, so it would do something like this:

  1. Call createProperty

  2. createProperty calls getProperty and sets $Exist to $true or $false, depending on whether the property is found.

  3. createProperty checks value of $Exist

  4. createProperty creates if $Exists -eq $false, else error out and log it

All that stuff outlined above works great when using just the script. Inside the module, I cannot get the boolean variable to pass from function to function. How do I do that in the module? Am I missing a fundamental best practice here?

I wanted to share my code, but don't want to go through the hassle of sanitizing it just now, but will do it if it becomes necessary. Thanks in advance for any insight.

Top answer
1 of 6
4

To aid future maintenance, I’ve got my user management script calling another script to get the address information for different offices, which should pass back the right info to be used in the AD user management.

So, if add-user.ps1 gets the variable $ADOffice=“New York” it calls get-OfficeAddress.ps1 where a switch statement passes back the correct street address, state, and zip code.

Then add-user.ps1 goes on to make the appropriate changes to the target user in the AD.

It looks like this:

add-user.ps1

Param(
    [Parameter( Mandatory = $false)]
    [string]$ADoffice = "New York" 
    )

&("C:\Scripts\User Mgmt Automation\get-OfficeAddress.ps1") $ADoffice

Then get-OfficeAddress.ps1 picks that up like so:

switch ($Office)
      {
         "Albany" {
         $ADstreetAddress="123 Main Street" 
         $ADcity="Albany" 
         $ADstate="NY"
         $ADpostalCode="123456"
          }
         "New York"{
         $ADstreetAddress="1313 Mockingbird Lane" 
         $ADcity="New York" 
         $ADstate="NY"
         $ADpostalCode="131313"
          }
         }

Write-Host $ADoffice,$ADstreetAddress,$ADcity,$ADstate,$ADpostalCode

The screen output shows the results on the screen fine, but I don’t know how to pass those variables back into ad-user.ps1 just after the call to get-OfficeAddress.ps1

Could you point me in the right direction, please?

2 of 6
2

You need to assign a variable name to the output of your called script. You might be able to call it by using

$address = &("C:\Scripts\User Mgmt Automation\get-OfficeAddress.ps1") $ADoffice

The better way would be to re-write the get-OfficeAddress.ps1 into a grouping of related functions which can be called using ‘dot-Sourcing’

You would add the script and its functions into your calling script as so:

.C:\folder\ScriptWithFunctions.ps1

and then just call the functions inside it the same as you would if you wrote the functions inside the script itself. Try this page for more details/examples.

Microsoft Certified Professional Magazine Online

Exploring Dot-Sourcing in PowerShell -- Microsoft Certified Professional...

Dot-sourcing allows for you to keep your functions modular.

edited for clarity

Top answer
1 of 5
5

Hello

In my form, I declare my radio buttons, textboxes etc.

In my group, I have a series of radiobuttons, so I am using the .Add_Click{whichoption} on the group to indicate that if a group item is clicked (radiobutton), it will run a function.

The function simply says that :

function whichoption{
#Check that there is a computername entered before assigning a value
If($Compname = $txtsetUACComp.Text) {
If($rdoOption1.checked) {$UACoption = 1}
If($rdoOption2.checked) {$UACoption = 2}
If($rdoOption3.checked) {$UACoption = 3}
If($rdoOption4.checked) {$UACoption = 4}
}
}

Once the whichoption function has run, I call another function that requires parameters (-computername and -option). This option also requires credentials So I have use the following in order to prompt for Credentials (inside the function).

$c=Get-credential
$Username = $c.UserName

Im using:

[System.Windows.MessageBox]::Show(($CompName) + ", " + $UACOption)

to see what values have been passed, and it is always blank ($compName and $UACOption do not have values).

So this leads me to believe it is a scoping issue.

I have attempted to use:

Function whichoption() {
If($txtsetUACComp.Text) {
$grpOptions.Enabled = $True #don't enable radiobuttons until a computername is typed in.
$Global:CompName = $Script:txtsetUACComp.Text + $txtsetUACComp.Text
if($rdoOption1.checked) {$Global:UACOption = 1 + $UACOption}
if($rdoOption2.checked) {$Global:UACOption = 2 + $UACOption}
if($rdoOption3.checked) {$Global:UACOption = 3 + $UACOption}
if($rdoOption4.checked) {$Global:UACOption = 4 + $UACOption}
return $UACOption
}
}

But the same results.

The function I am calling has the following parameters:

Param ([String] $Script:CompName, [int] $Script:UACOption)

And the command to call the function is:

$btnUAC.Add_Click({ChangeUAC -Computername $Script:CompName -Option $Script:UACOption})

Any suggestions?

Thank You

Terry

2 of 5
2

By default, variables within a function operate within the scope of the function and do not exist outside of the function. You can change the scope of the variable.

learn.microsoft.com

about Scopes - PowerShell

Explains the concept of scope in PowerShell and shows how to set and change the scope of elements.

Find elsewhere
🌐
PowerShell Test-Path
powershellfaqs.com › share-variables-between-functions-in-powershell
How to Share Variables Between Functions in PowerShell?
September 9, 2024 - To share variables between functions in PowerShell using the global variable method, you can declare a variable with the $global: prefix, making it accessible from anywhere within your script, including inside functions.
🌐
PowerShell Forums
forums.powershell.org › powershell help
Passing multiple parameters to a function and returning multiple values - PowerShell Help - PowerShell Forums
August 26, 2022 - Hi All, I am trying to rewrite an existing program to use a function since I need to run the same steps more than once. I am running into issues passing values in and out of the function. My input file has three columns IDNum,UPN,Role. I am updating the script variables and creating a list ...
🌐
Microsoft Learn
learn.microsoft.com › en-us › powershell › module › microsoft.powershell.core › about › about_ref
about_Ref - PowerShell | Microsoft Learn
December 13, 2024 - Therefore, the value of $var is unchanged outside the scope of the function. Function Test($Data) { $Data = 3 } $var = 10 Test -Data $var $var ... In the following example, a variable containing a Hashtable is passed to a function.
🌐
Stack Overflow
stackoverflow.com › questions › 75595984 › function-of-functions-pass-variable-between-scopes-powershell
Function of Functions - pass variable between scopes - powershell - Stack Overflow
I would do a bit more reading up on how Functions work (e.g. PowerShell 101 Functions), as it will help you complete what you are trying to do. Basically you don't have to resort to global variables. You are missing 2 things from your example. Instead of Write-Host which writes out to the screen, you use Write-Output to "return" values back from the function.
🌐
Microsoft Certified Professional Magazine
mcpmag.com › articles › 2016 › 04 › 07 › powershell-functions-with-parameters.aspx
Getting Input into Your PowerShell Functions with Parameters -- Microsoft Certified Professional Magazine Online
April 7, 2016 - Function Get-Something { Param($VariableHere) Write-Output "I am in the function now. My variable is $VariableHere" } I recommend the latter approach as this gets you in the habit of specifying how parameters are declared in advanced functions. The functions we are using here are called "basic" PowerShell functions.
🌐
4sysops
4sysops.com › home › blog › popular › the powershell function – parameters, data types, return values
The PowerShell function – Parameters, data types, return values
July 28, 2023 - Typically, you want to pass the ... In the simplest case, you go without the declaration of parameters and read the values from the pipeline by iterating over all objects of the predefined variable ......