Star InactiveStar InactiveStar InactiveStar InactiveStar Inactive

I've been doing a presentation called Avoiding Version Chaos in a Multi-Version World. I start by explaining that you shouldn't avoid our new multi-version world -- you'll miss out on too much innovation -- but you need to be keenly aware of how it works and understand how to manage it.

For example, your shared code should run perfectly even when the end-user's session includes multiple versions of commands and modules, and commands with the same name in different modules and module versions. There are lots of ways to avoid conflicts, including knowing which commands run by default, understanding what Import-Module imports, removing conflicting module versions (Get-Module Foo | Remove-Module), and using module-qualified commands, version parameters, and the amazing ModuleSpecification object.


But, there's one situation that you can't fix in a session. If a module loads an assembly, like a .dll file, into a PowerShell session, you cannot remove it and you cannot load a different version of the assembly with the same name into the session. Remove-Module and Import-Module can try, but it won't work. Let's explore the consequences and see what we can do about this.

Special thanks to David Wilson and Joel Bennett for their help on this subject.


Problem: Remove-Module can't unload an assembly

Here's the problem. I import PSScriptAnalyzer (love that module!) version 1.10.0. Then, I remove the module from the session and import version 1.11.0. But, the assemblies that 1.10.0 loads never leave. The same thing happens if I import 1.11.0 first.

Normally, I wouldn't notice, but in PSScriptAnalyzer 1.10.0, this line...

function TestBraceOneLine () { "TestBraceOneLine" }

... violates the PSPlaceOpenBrace and PSPlaceCloseBrace rules.

PS C:\ > Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () { "TestBraceOneLine" }' -Settings $home\documents\WindowsPowerShell\Modules\PSScriptAnalyzer\1.10.0\Settings\CodeFormatting.psd1

RuleName                Severity     ScriptName Line  Message
--------                --------     ---------- ----  -------
PSPlaceOpenBrace        Warning                 1     There is no new line after open brace.
PSPlaceCloseBrace       Warning                 1     Close brace is not on a new line.

In PSScriptAnalyzer 1.11.0, it doesn't violate those rules. Also, in 1.11.0, you don't have to specify the full path to a settings file in the Settings directory. Just specify the settings file name. (Good work, everyone!)

PS C:\ > Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () { "TestBraceOneLine" }' -Settings CodeFormatting
PS C:\ >

But, if I import PSScriptAnalyzer 1.11.0 (or any other version) then remove it from the session and import a different version into the session, the assemblies that the first version loaded, and the behavior associated with those assemblies, is still there.

Here, I import v.1.10.0.

# Import 1.10.0
PS C:\> Import-Module PSScriptAnalyzer -RequiredVersion 1.10.0 -PassThru

ModuleType Version    Name                 ExportedCommands
---------- -------    ----                 ----------------
Script     1.10.0     PSScriptAnalyzer     {Get-ScriptAnalyzerRule, Invoke-ScriptAnalyzer}

Run the test command:

PS C:\> Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () { "TestBraceOneLine" }' -Settings $home\Documents\WindowsPowerShell\Modules\PSScriptAnalyzer\1.10.0\Settings\CodeFormatting.psd1

RuleName                Severity     ScriptName Line  Message
--------                --------     ---------- ----  -------
PSPlaceOpenBrace        Warning                 1     There is no new line after open brace.
PSPlaceCloseBrace       Warning                 1     Close brace is not on a new line

Remove all versions of PSScriptAnalyzer from the session. Then, turn off module autoloading so the module doesn't sneak back into the session.

PS C:\> Remove-Module PSScriptAnalyzer 
PS C:\> Get-Module PSScriptAnalyzer 
PS C:\>    # None found
PS C:\> $PSModuleAutoLoadingPreference = 'None'

However, even after removing all instances of PSScriptAnalyzer, the assemblies that PSScriptAnalyzer loaded into the session are still there. To test, we'll look for one of the types that the assembly defines.

PS C:\> [Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.GetScriptAnalyzerRuleCommand].Module.Assembly.FullName
Microsoft.Windows.PowerShell.ScriptAnalyzer, Version=, Culture=neutral, PublicKeyToken=null

And, just to be certain, let's make sure that the module didn't sneak back into the session.

PS C:\> Get-Module PSScriptAnalyzer 
PS C:\>

Next, let's see what happens when you load a different, in this case, newer, version of the module. Is the assembly replaced?

PS C:\> Import-Module PSScriptAnalyzer -RequiredVersion 1.11.0 -PassThru

ModuleType Version    Name                  ExportedCommands
---------- -------    ----                  ----------------
Script     1.11.0     PSScriptAnalyzer      {Get-ScriptAnalyzerRule, Invoke-ScriptAnalyzer}

Now, run the test command. The first hint is that it won't accept the settings file name without the full path. That's 1.10.0 behavior.

PS C:\> Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () { "TestBraceOneLine" }' -Settings CodeFormatting
Invoke-ScriptAnalyzer : Cannot find file 'CodeFormatting'.
At line:1 char:1
+ Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (CodeFormatting:String) [Invoke-ScriptAnalyzer], FileNotFoundException
    + FullyQualifiedErrorId : SettingsFileNotFound,Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.InvokeScriptAnalyzerCommand 
WARNING: Cannot parse settings. Will abort the invocation.


Then, when you run the command with the full path to the settings file, it triggers the rule violation that was eliminated in 1.11.0.

PS C:\> Invoke-ScriptAnalyzer -ScriptDefinition 'function TestBraceOneLine () { "TestBraceOneLine" }' -Settings $home\Documents\WindowsPowerShell\Modules\PSScriptAnalyzer\1.11.0\Settings\CodeFormatting.psd1

RuleName             Severity     ScriptName Line  Message
--------             --------     ---------- ----  -------
PSPlaceOpenBrace     Warning                 1     There is no new line after open brace.
PSPlaceCloseBrace    Warning                 1     Close brace is not on a new line.

The module version in the session reports that it's 1.11.0.

PS C:\> (Get-Module PSScriptAnalyzer).Version

But, that's not completely true. It's actually the 1.11.0 module with the 1.10.0 assembly.

PS C:\ps-test> [Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.GetScriptAnalyzerRuleCommand].Module.Assembly.FullName
Microsoft.Windows.PowerShell.ScriptAnalyzer, Version=, Culture=neutral, PublicKeyToken=null
                                             # Look here ^^^

I'll spare you the example proof, but if you import 1.11.0 before 1.10.0, you get the 1.11.0 behavior. In fact, whichever version is installed first never leaves the session, until you close the session.


You cannot unload an assembly

Before you bother the PSScriptAnalyzer team (I did already -- sorry!), this isn't their fault. It happens because you cannot unload a .NET assembly from a PowerShell session. In fact, there are very good reasons for not unloading assemblies.

Of course, this issue is not limited to PSScriptAnalyzer. It affects any module that loads an assembly into your session. And, it's difficult to detect unless you're very familiar with the behavioral differences between module versions, like those described in release notes.

The PSScriptAnalyzer team is considering writing a warning if you try to load a version of PSScriptAnalyzer into a session that already has a PSScriptAnalyzer assembly. That's a great idea. However, if your code requires particular versions of a module or is incompatible with a particular assembly, remember that removing other versions of the module and importing the version you need might not be enough. You might need to refuse to run or open a new session. 


June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Microsoft Cloud and Datacenter MVP. You can reach her at This email address is being protected from spambots. You need JavaScript enabled to view it. or follow her on Twitter at @juneb_get_help.

If you have questions about our products, please post in our support forum.
For licensed customers, use the forum associated with your product in our Product Support Forums for Registered Customers.
For users of trial versions, please post in our Former and Future Customers - Questions forum.
Copyright © 2024 SAPIEN Technologies, Inc.