1

I have a script that I would like to migrate to PS5 and Classes for a number of reasons. It's a big script, so it would need to have a few Modules for the various classes. But, I have customers who are still using Windows 7 and PS2. My initial thought was to provide a way to automate the update of PowerShell, but so far as I can tell the only way to use Classes in Modules involves the Using Module statement, which has to be the very first statements in the script. And also as far as I can tell, anyone trying to run said script on Windows 7/PS2 is going to get a nasty error and I have no mechanism for catching that error and providing a log or other meaningful way of communicating what is going on. Is this a correct assessment, and perhaps Classes in PS 5 are just not yet ready for this kind of situation? I had hoped to move to much better logging with the help of Classes, but from the looks of it either things work, or my script shits the bed and looks really unprofessional. :(

Gordon
  • 6,257
  • 6
  • 36
  • 89

2 Answers2

2

If using two versions of the script is the direction you head, but you still want to avoid code duplication, perhaps it would be possible to refactor your script as such:

Have three files, script_main.ps1, script_using_imports.ps1, and script_legacy.ps1.

In script_main.ps1, do something like:

$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
if (getPowershellVersion() > $VersionNeeded) { #Pseudocode function names
    $scriptFile = "script_using_imports.ps1"
} else {
    $scriptFile = "script_legacy.ps1"
}

get-content -path "$scriptPath\$scriptFile" -raw | invoke-expression

# The rest of main goes on assuming imports worked.

Then in script_using_imports.ps1 and script_legacy.ps1, you handle the module/class imports as you want.

Not sure if that's helpful for your use case, but it's an idea!


I had to reference this for figuring out how to do the path name stuff.

Josh Desmond
  • 640
  • 2
  • 10
  • 19
  • Interesting approach. I have avoided invoke-expression in general, mainly due to security concerns, but if I am checking code signing before hand perhaps that isn't an issue. More to explore this weekend. – Gordon Sep 07 '19 at 06:28
  • 1
    One caveat with this approach when also using functions, you lose the ability to control which functions are exported and which remain local. Perhaps not an issue to have all my functions exposed, but something I just noticed. – Gordon Sep 07 '19 at 06:48
  • Also, I think this has potential even without having two versions. With this approach I can have some code that tests for PS5, and not finding it initiates that install and relaunches the script. When PS5 is present, the modules can be "made available" after the conditional. Why MS didn't just allow Using Module to work this way natively I have no idea. It sure feels like Using Module is crippled, either on purpose or due to lack of foresight. :( – Gordon Sep 07 '19 at 06:52
0

You cannot use classes or the USING statement in PowerShell versions earlier than 5, period - the statements simply do not exist. You will need to

(1) maintain two versions of your script, one for PowerShell 5+ and one for PowerShell 4-,

(2) eschew the use of any cmdlets or syntax that are not in PowerShell 2, or

(3) mandate that your customers upgrade WMF and .NET to versions that will support PowerShell 5 or later.

Jeff Zeitlin
  • 9,773
  • 2
  • 21
  • 33
  • That's.... ugly. But I have been thinking about a wacky solution. A PS2.0 compliant "launcher" that takes the arguments needed, validates that things will work, and passes the arguments on to the real script if so. If not, it can handle the logging. I may play with that tomorrow and see how ugly it gets. :) – Gordon Sep 06 '19 at 19:34