PowerShell Great Debate: PowerShell Versions?

Today's Great Debate is a bonus, offered from former team member June Blender. Take it away, June!

Like several of the excellent debates in our Great Debate series, this debate issue arose during in Scripting Games 2013 when different judges used different selection criteria to evaluate entries.

Some judges, like me, wanted to see evidence that the scripter had studied all features of the newest version of the Windows PowerShell language and selected the best approach for their solution. Other judges wanted the solutions to work on as many computers as possible.

Outside of the Scripting Games, this issue is very practical and very important. If you’re writing a script to work on particular computers in your enterprise, you know which versions of Windows PowerShell are installed and which features you can use. But when you write a shared script or functions for a module, your scripts/functions can run in any environment.

What’s the version best practice?

I think we can all agree that a #Requires statement should appear in any shared script.

 #Requires -Version <N>[.<n>]

In fact, maybe we need a version property of commands that can be queried by using Get-Command, like the PowerShellVersion property of modules?

But, beyond that, should you restrict yourself to features in the oldest supported version of Windows PowerShell, or the most common version, or can you use features in the newest version, even if your scripts don’t run on all computers in all enterprises?

Sometimes, the answers are trivial. The simplified syntax in Windows PowerShell 3.0 that omits curly braces {} and “$_.” is just syntactic sugar for the original syntax. We might decide that it’s best to avoid it unless you’re sure that all computers are running at least 3.0.

At the other extreme are features that don’t have any equivalent in a previous version. What if your module would benefit from using scheduled jobs, CIM commands, or workflows? Must you avoid them?

In the middle are cases where you can use a somewhat equivalent feature. Can you use Get-CimInstance, or are we forever tied to Get-WmiObject? Can you use PSCustomObject or are you committed to Add-Member? Do you need to write Types.ps1xml files when dynamic type data would suffice?

About the Author

Don Jones

Don Jones is a Windows PowerShell MVP, author of several Windows PowerShell books (and other IT books), Co-founder and President/CEO of PowerShell.org, PowerShell columnist for Microsoft TechNet Magazine, PowerShell educator, and designer/author of several Windows PowerShell courses (including Microsoft's). Power to the shell!

8 Comments

  1. At this point, I'm happy if a server HAS PowerShell at all.

    PS 3+ features are fun for me to play around with, and may work if it's something I'm going to run solely on my machine.

    But I have to mostly design for PS 2 out of necessity.

    #Requires commenting is good, it'd actually be nice if that would stop PS execution too.
    If you tried to run a script that requires PS 3+ in PS 2, it's stop the script and say "This was designed for a later version of PS3 and may require functions not present in this version, do you want to continue?"

    • Mark, the #requires comment does stop Powershell executing if it doesn't meet the requirement specified. See the error that got generated below.

      The script 'Version.ps1' cannot be run because it contained a "#requires" statement for Windows PowerShell version 5.0. The version required by the script does not match the currently running version of Windows PowerShell version 3.0.

      My approach to which version to code for are based purely on my own environment and what i will be targeting. We are slowly losing the last of our Windows XP machines so i will still code to version 2 if i need to work with them. I have some scripts that only need to target Windows 7 computers, so they use v3 syntax where needed.

      For scripts that are run on an it technicians computer i will take advantage of the version 3 features. Targeting Exchange? Well we're on 2010 so thats version 2.

      By using the #requires comment i can safely share my scripts knowing that they will only run on the correct version. If i've written for v3 and someone else needs a v2 for their environment then they can adjust it themselves. Personally i'm writing scripts for myself and my company, if anyone else wants to make use of my work then thats fine but compatibility is your problem not mine.

  2. My take:
    1. Use a requires statement if you're using anything above 2.0 (we can assume 2.0, right?)
    2. I don't use the simple where syntax in scripts, since it doesn't buy me anything.
    3. CIM, scheduledjobs, etc. are handled by #1...I still tend to use WMI for most everything since most boxes I hit don't have 3.0 on them. Until they all do I still need to have code that falls back to WMI anyway.
    4. I don't use [PSCustomObject] because new-object with -properties is "good enough" for my uses. I also don't use Type.ps1xml files very often.

  3. Using #Requires -Version is Ok but we have still Windows XP on the run! To use the newes shiny stuff is nothing for stable and lasting work. I have seen too much failing Software because the use the newest stuff. We have not the time to read Logfiles and looking If the Scripts are running propper. No script-sit! The use cases for the ForEach simplyfied Syntax are so smal that it is simply useless. Why I should use (learn) the simplyfied Syntax for Where-Object ? At the end of the day I need the full scriptblock syntax, so I use it every time! Syplified Syntax makes learning PowerShell harder, not simpler and our PS kids are making more syntax errors.
    I have to provide super stable scripts in my daily business. That are the biggest points i earn.
    What do you think the scripting games are for? Learn to Play or learn for real life?
    I try to work serious and do not play with my enterprise!
    Make it so! Kirk out.....

  4. #Requires -Version 3

    For what it's worth: the #Requires statement June mentioned *does* stop execution on systems that don't have the required version, (or module, assembly, etc). In Version 3, it even imports the required modules (but not in Version 2, where it just stops loading).

    Frankly, I think it's a time sink to keep designing scripts that work in PowerShell 2 unless you have active need of them. The only place they're still REQUIRED is XP (and servers running certain versions of older Microsoft Server products). I'm happy to do the work to keep something v2 compatible when I'm being paid to do so, but otherwise, it's not worth the time.

    You have to remember, there are not just new cmdlets in PowerShell 3, but features that save time and typing, and even a lot of these features that people are knocking as "pointless" have side benefits as well:

    Did you know that the "simplified" syntax for Where-Object is faster than normal ScriptBlock syntax? That's not just savings on parse time, with a couple hundred items to filter, you could shave 10% off every call to where-object 😉

    And dot expansion is faster than any other way of expanding (as an example on my system with about 200k files on SSD):

    $names = ls ~ -recurse | Select -Expand Name ## 31s
    $names = ls ~ -recurse | % { $_.Name } ## 22s
    $names = (ls ~ -recurse).Name ## 18s

  5. I have made a suggestion on Microsoft connect:
    Get-Command should be able to detect the needed PowerShell version of a cmdlet/script/function is written
    https://connect.microsoft.com/PowerShell/feedback/details/795816/get-command-should-be-able-to-detect-the-needed-powershell-version-of-a-cmdlet-script-function-is-written-for

    i have forgotten to mention even if a technic looks like new it is not! CIM uses WMI under the hood so CIM is not new! Even the DMTF called this technic never WMI! It was allways called CIM ;-). So I need CIM only to go through Firewalls adn the added stuff. It was Microsofts decision to dont let Windows Vista in the PowerShell 3.0 world. So we have to use PS 2.0 while(-not $AllOSinEnterprise -contains $PowerShellVersion3)
    Yes Sir! No dangerous experiments. Only fuel 2.0 for the enterprise. Riker out...

  6. If a version constraint isn't part of the problem statement then either approach seems valid to me. I'd look for the author's intent and full understanding of the tradeoffs you noted. Maximum points for a design that executes on the latest version yet broadly targets downlevel platforms, e.g. the CIM post referenced from this site pulling WMI data via multiple management paths, WSMAN and DCOM.

    I'm a complete PS novice and have a bunch of downlevel systems so broad targeting is necessary for me.

  7. I'd say write your script for whatever version of PowerShell you are certain your clients are running. For now, I try to make sure everything is V2-compatible, unless someone specifically tells me they're using v3 when I'm tasked with writing a script.