The PowerShell variable scope

Understanding the concept of the PowerShell variable scope is crucial if you have to write a script that is longer than a few lines. This intro is directed at the PowerShell beginner.

In my last post, I explained how to name variables and introduced the concept of data types. Today, we’ll look at another basic concept: the scope. Its purpose is to restrict read and write access from different locations in a PowerShell session.

Note that the scope is also relevant for aliases, functions, and PowerShell drives (PSDrives). This post focuses on variables; however, after you master the scope concept, it should be easy to apply it to other PowerShell entities.

The scope hierarchy ^

When you first assign a value to a variable (initialize it) in a script, the variable is only available within the script and not on the console where you launched the script. Likewise, if you define a variable in a function, you cannot access its value at the script level where you called the function. The variable’s scope is responsible for this behavior; it determines where the variable is available.

Scopes are organized in hierarchies. At the top sits the Global scope. This scope is created whenever you start a PowerShell session. The Script scope is one level below the Global scope. Next is the scope of a function that you define in your script, and, if you create a function within a function, you also create a new scope that sits below the other scopes. A scope that is located one level above a certain other scope is called the parent scope; the lower-level scope is its child scope.

Variables defined in a child scope are unavailable in the parent scope. However, in the child scope, you have read access to variables initialized in the parent scope. Read access reaches down to all lower levels.

It is not possible to modify a parent scope variable in the child scope. If you assign a value to a variable with the same name as the one that you created in its parent scope, you create a new variable that is only available in the child scope. All levels below the scope of the new variable will then use its value and not the one defined at the higher level.

If you create a variable on a PowerShell console, its scope is the Global scope. From what I wrote above, you can infer that you can also read its value in the Script scope because the Global scope is the Script scope’s parent scope. Likewise, if you define a variable in a script, you can read the variable in the function, but you can only change it in the Script scope. If you create a variable in the function with the same name as that in the Script scope, all sub-functions will use the value of the new variable created in the parent function.

Absolute vs. relative scopes ^

Another important concept is the Local scope. It is not the opposite of the Global scope. Rather, the term refers to the scope where you created a variable. You could say it is the scope where the variable is currently at home, which is why it is also called the variable’s current scope. For instance, if you create a variable on the PowerShell console, its Local scope is the Global scope.

And, last but not least, a variable can be in a so-called Private scope. If you explicitly tell PowerShell that the variable should live in a Private scope, the variable is only available in its Local scope and can’t be read in its child scopes.

Global, Local, Script, and Private scopes are absolute scopes because you can reference them directly by their names in cmdlets and scope modifiers. Scopes also have numbers that are relative to a variable’s Local scope. You can refer to the Local scope with the number 0, to its parent scope with the number 1, to the parent scope’s parent scope with the number 2, and so on.

Scope examples ^

Please don’t try the examples in this post in PowerShell ISE because all its panes are in the same scope. This is helpful for debugging, but it means that the examples here will produce different results than on the PowerShell console. You can edit the examples in PowerShell ISE, but you then have to run them on the console. This scope hierarchy is demonstrated in the example below:

Scope hierarchy

“Script scope” is displayed for the first time in the function where we have read access to the variable defined at the script level, and it is displayed for the second time at the end of the script. Modifying the $Script variable in the function had no effect on the variable in the Script scope. However, within the function, we now have a new $Script variable with a new value.

The $Function variable is unavailable at the script level; therefore, the value we assigned in the function is not displayed at the end of the script.

You can change this default behavior of PowerShell by using scope modifiers. The four available modifiers correspond to the absolute scopes mentioned above: Private, Local, Script, and Global. The command below creates a variable in a Private scope:

This ensures that the variable can only be accessed in its Local scope (the scope where you defined it) and not in its child scopes.

Consequently, if you want a script variable to be available everywhere in your PowerShell session, you can use the Global variable modifier. Note that, by using this scope modifier, you change the Local scope of the variable. Thus, if you create a variable in a script using the Global modifier, the Local scope of the variable becomes the Global scope. This is why you can access the variable on the console after the script terminates. PowerShell drops all scopes when a script ends; however, because you created the variable in the Global scope, it will survive the script’s termination. This can be useful for debugging.

In the example below, both variables are defined in the function, but only the one with the Script modifier is available at the script level:

Script modifier

You will rarely need the Local scope modifier because this is the default scope where PowerShell creates a variable. However, because you can use script modifiers not only to modify the scope but also to refer to a scope, it can sometimes be helpful to use the Local scope modifier for clarity reasons. For example:

Local scope modifier

If you have been working with a scripting language that doesn’t know the scope concept, you might prefer that a variable can be read and modified in your entire script. It is not best practice, but with the help of the Set-Variable cmdlet, you can make the variable available for read and write access in all child scopes (but not in the parent scope).

AllScope option

Note that we are not using a scope modifier here. Rather, the Set-Variable cmdlet ensures that the variable becomes a part of all child scopes in your script. Alternatively, you can use the New-Variable cmdlet to create a variable with the AllScope property:

If you just want to debug a script and access its variables after the script completes, you can launch it in dot-sourced notion with a dot (.) and a space before the path and name of the script. For example:

This will merge the Script scope with the Global scope. You can now display all the script’s top-level variables on the console. However, variables defined in functions will still be unavailable on the console.

In the next installment of this series I will explain how you get a list of all variables in your script and how you search in your variables.

Join the 4sysops PowerShell group!

Your question was not answered? Ask in the forum!

  1. Bryan 4 years ago

    What's the best way to assign each variable's scope so that even when it's script and function are dot-sourced, they still remain 'private' / contained only within their scope?

    I have scripts that I want to dot-source, so that their functions are readily accessible, but I'd prefer to not have all their variables  exposed to / clutter up the Global scope (as most of these variables are really only useful within their respective functions).


  2. John Gibbins 2 years ago

    Your AllScope example doesn't seem to demonstrate what you want as the function is failing to modify the variable and hence outputting "Defined in the script scope".
    If you swap the first two lines so that the scope is set before the variable is initialised it should produced the "Changed at the function level" output which is more useful.
    Maybe worth adding a note that the order is important. It looks like you can't change the scope of an existing variable.
    Also, could you please include an example of using relative scopes? From what I have read I would have thought that $x="abc"; $0:x would display "abc" but it doesn't. Are numbered scopes only available via the -scope parameter of *-variable cmdlets?


  3. Michael 2 years ago

    Hey man! Thanks for the article on scope. I was sitting here “why can’t I access my variables.” It didn’t even dawn on me everything is very separated

    Got it all sorted now. Thanks again!


Leave a reply

Your email address will not be published. Required fields are marked *


© 4sysops 2006 - 2020


Please ask IT administration questions in the forums. Any other messages are welcome.


Log in with your credentials


Forgot your details?

Create Account