What is this Module Scope in PowerShell that you Speak of?

Last week I posted a blog article about a PowerShell script module that I had written with a few proof of concept commands to manage a Nimble Storage Area Network using their REST API. That module used a command to connect and authenticate to the storage device which needed to share a token with other commands in the module otherwise authentication would have to be performed for each command. I initially placed the token in a global variable even though I mentioned in the blog article that I’m not a big fan of globally scoping variables unless absolutely necessary (which I thought it was at the time).

I used a module named TunableSSLValidator that fellow PowerShell MVP Joel Bennett had created to be able to use Invoke-WebRequest with an untrusted or self-signed certificate. I tweeted that blog article to Joel to show him how I had used his module. He responded with a great tip about scoping variables so they’re shareable between commands within the same module without being globally scoped.

I searched through the help topics prior to contacting Joel once I saw his comment. I could only find one reference to module scope which says that modules do not have their own scope. Here’s what the about_Scopes help topic says about module scope:

“The privacy of a module behaves like a scope, but adding a module to a session does not change the scope. And, the module does not have its own scope, although the scripts in the module, like all Windows PowerShell scripts, do have their own scope.”

After reading that, I contacted Joel since I’m familiar with global, script, and local scope but not module scope. My question to Joel was something like “What is this Module Scope that you Speak of?”. Joel replied and said script scope in a module is module scope. A little testing confirmed that scoping variables to script scope within a module makes them shareable between the commands from that module without exposing them globally.

To test variable scoping within a module, the following functions were saved as a script module file named MrVarTest.psm1:

First, the PSM1 file is imported since I didn’t place it in a location specified in $env:PsModulePath. Next the variable is set from a function in the module without coercing the scope. It’s no surprise that the variable isn’t found from another function in the module or from the current scope outside of the module. Then local scope is tested. The same results are produced. The third option uses script scope which allows another function within the same module to access the variable, but not from the current scope outside of the module. Finally, global scope is tested and it’s accessible from another function within the module and from the current scope outside of the module. There’s no reason for the variable to be accessible from outside of the module and having it accessible in that manner can only lead to trouble.

Best practice is to limit the scope of your variables as much as possible otherwise something else that’s totally unrelated could overwrite the value contained within one of them if they’re globally scoped.

I also confirmed the variables are shared between commands within the same module using script scope regardless of whether or not you’re using one monolithic PSM1 file for your script module or separate PS1 files that are dot-sourced from the PSM1 file (Non-Monolithic script module design).

Here are some other comments related to the tweet posted earlier in this blog article:

The key takeaway from this blog article is as Joel said “script scope in a module is module scope”.

µ

1 Comment

  1. cadayton

    Too bad this is not true for nested modules. Tested that the nest module was able to see the $Global variable set in the parent module, but the same isn’t true for the $Script variable.

    Reply

Leave a Reply

%d bloggers like this: