Enforce Better Script Practices by Using Set-StrictMode


Summary: Boe Prox shows how to use Set-StrictMode to write better scripts.

Honorary Scripting Guy and Windows PowerShell MVP, Boe Prox, here today filling in for my good friend, The Scripting Guy. This is the third part in a series of five posts about troubleshooting Windows PowerShell scripts and functions. The series includes:

  1. Provide Support by Using Verbose and Debug Streams
  2. Troubleshoot by Using Set-PSDebug
  3. Enforce Better Script Practices by Using Set-StrictMode (this post)
  4. Trace Your Commands by Using Trace-Command
  5. Use the PowerShell Debugger

If you remember back in the VBScript days, if you wanted to enforce better practices with the handling of undeclared variables in your code, you would use Option Explicit. If you tried to throw in a variable that wasn’t defined at the top of the script, it would throw an error message and stop executing. Well, we’ve come further along with Windows PowerShell.

By using the Set-StrictMode cmdlet, we can enforce some common “best practice” coding techniques that you can use with your scripts. In yesterday’s post, I used the –Strict parameter with Set-PSDebug. This enforces the variable declaration in a script. But with Set-StrictMode, we get that plus a little more. The scope is limited to the current scope and child scopes. By default, Set-StrictMode is turned off to prevent error messages while you are writing the script.

You only have two parameters to deal with: -Off and -Version.

 Off

 Turns off StrictMode.

 Version

 There are three possible values that can be supplied with this parameter:

 -Version 1.0
 Prevents use of variables that have not been initialized (think Option Explicit in VBScript)

 -Version 2.0

  • Prevents use of variables that have not been initialized (think Option Explicit in VBScript)
  • Cannot call non-existent properties on objects
  • Disallows calling a function like a method, for example, Do-Something(1 2)
  • Prohibits creating variables without a name

 -Version Latest
 Selects the latest StrictMode version available and uses that. This ensures that regardless of
 which version  of Windows PowerShell you are using, the latest version will be used.

Let’s start by showing what happens when you set the StrictMode version to 1.0. We can verify that if we use an uninitialized variable, it will not throw any error messages and act as though it might actually have something.

PS C:> $i -gt 0

False

It returned $False, which is what I expected. Now let’s set the version to 1.0 and see what happens:

PS C:> Set-StrictMode -Version 1.0

$i -gt 0

The variable ‘$i’ cannot be retrieved because it has not been set.

At line:2 char:1

+ $i -gt 0

+ ~~

    + CategoryInfo         : InvalidOperation: (i:String) [], RuntimeException

    + FullyQualifiedErrorId : VariableIsUndefined

Well, that definitely played out differently. Instead of returning a Boolean value, I am greeted with an error message telling me that I should have initialized the variable first.

Another good use of this version is to make sure that you do not mistype a variable that might have been declared earlier and might not otherwise be caught.

I would usually turn this off by using the –Off parameter, but instead, I am going to move to the next part of Set-StrictMode by running a few commands that will still succeed with version 1.0 enabled:

PS C:> Function Invoke-Test {

>>     Param ($Item1, $Item2)

>>     $Item1 + $Item2

>> }

>> 

PS C:> Invoke-Test(1,4)

1

4

Calling the function like we would call a method worked—even with the version set to 1.0. This, of course, is not how a function should be called. It can cause undesirable problems if it happens to “work” because it treats the items separated by a comma as a collection. Setting the version to 2.0 (or to Latest) helps us understand that this is not an acceptable practice:

PS C:> Set-StrictMode -Version 2.0

PS C:> Invoke-Test(1,4)

The function or command was called as if it were a method. Parameters should be separated by spaces. For information about parameters, see the about_Parameters Help topic.

At line:1 char:1

+ Invoke-Test(1,4)

+ ~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException

    + FullyQualifiedErrorId : StrictModeFunctionCallWithParens

That is more like it! After setting the version to 2.0 (or to Latest), my attempt to run the function like a method is met with an error message that explicitly says it was being called like a method. Remember that parameters are positional, and they should be separated by a space instead of a comma.

Now I’ll turn off StrictMode to demonstrate another example where something that shouldn’t be used doesn’t throw an error message. Then I’ll repeat the process with StrictMode enabled and set to Latest.

PS C:> Set-StrictMode –Off

Now I will attempt to call a property on an object that doesn’t exist:

PS C:> $Item = ‘test’

PS C:> $item.date

Nothing happens. The property doesn’t exist, but no error is thrown to let us know this.

Now I enable StrictMode and observe what happens:

PS C:> Set-StrictMode -Version Latest

PS C:> $item.date

The property ‘date’ cannot be found on this object. Verify that the property exists.

At line:1 char:1

+ $item.date

+ ~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [], PropertyNotFoundException

    + FullyQualifiedErrorId : PropertyNotFoundStrict

As with my last demo, an attempt to use a property that doesn’t exist on the object is rejected, and an associated error message shows that you are not calling a valid property name.

Join me tomorrow when I discuss using Trace-Command to help in troubleshooting the pipeline and parameter binding.

We invite you to follow the Scripting Guy on Twitter and Facebook. If you have any questions, send email to the Scripting Guy at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, see ya!

Boe Prox, Windows PowerShell MVP and Honorary Scripting Guy 

Comments (7)

  1. David Wyatt says:

    One other thing that strict mode does: it produces errors if you try to access a non-existent property of an object. Without strict mode enabled, PowerShell just gives you a result of $null when you try to do that. For example:

    Set-StrictMode -Off

    $object = [pscustomobject] @{ Property = ‘Value’ }
    $object.DoesntExist

    Set-StrictMode -Version Latest

    $object.AlsoDoesntExist

  2. Mike here says:

    Know it’s going to cause more issues :-0
    But went and Set-StrictMode -Version Latest in my PoSh Profile
    Get my butt to use better coding practices 🙂

  3. OzThe2 says:

    Excellent – Will definitely be using this in my future scripts. Thanks for sharing.

  4. agnes says:

    # Beginning of the script
    Set-StrictMode -Version Latest

    # End of the script
    Set-StrictMode -Off

    http://goo.gl/UF3TDC |
    http://goo.gl/Z7yG70

  5. olivia says:

    You’ll want to ask over at PowerGUI.org. They wrote those cmdlets and know a ton more about them than I do! But my guess is probably not without reprogramming the cmdlet.
    http://goo.gl/Z7yG70 |
    http://goo.gl/8aE6LK

  6. asenath says:

    I put this in my PS profile, as I was sick on mispelling my variables and having to debug my scripts. That was great for about 10 minutes….

    as a result, my script for Exchange mailbox sizes stopped running due to a .count property which did not exist on a single user. I run $myUsers get-qaduser (scripts param) and I then check for $myUsers.count. if this is > 1 then I give the user a list of users
    return to select from. This now fails as a single user does not have a count property.

    Are you aware of any way to ensure that a get-qaduser command ALWAYS returns collection and not a sinlge object ?
    http://goo.gl/UF3TDC |
    http://goo.gl/Z7yG70

Skip to main content