2

I have a module, let me call it xyz.ps.core. It exports a function - Get-PullRequestsFromCommitIds

I fixed a bug in the function, republished the module, reinstalled and reimported it and yet the function still refers to the old version of the module.

Please, observe:

C:\xyz\tip [master ≡]> Get-Command Get-PullRequestsFromCommitIds | ft -AutoSize

CommandType Name                          Version     Source
----------- ----                          -------     ------
Function    Get-PullRequestsFromCommitIds 1.0.19107.4 xyz.ps.core

As you can see, the function is from version 1.0.19107.4

C:\xyz\tip [master ≡]> get-module xyz.ps.core | ft -AutoSize

ModuleType Version     Name             ExportedCommands
---------- -------     ----             ----------------
Manifest   1.0.19107.7 xyz.ps.core {Assert-ExtractionDestFolder, Assert-PullRequestMatchesFolder, Backup-Database, Connect-OctopusToTfs...}


C:\xyz\tip [master ≡]> get-module xyz.ps.core -ListAvailable | ft -AutoSize


    Directory: C:\Users\mkharitonov\Documents\WindowsPowerShell\Modules


ModuleType Version     Name             ExportedCommands
---------- -------     ----             ----------------
Manifest   1.0.19107.7 xyz.PS.Core {Assert-ExtractionDestFolder, Assert-PullRequestMatchesFolder, Backup-Database, Connect-OctopusToTfs...}

But the module version is already on 1.0.19107.7. But OK, I have a function that refreshes the module, even if it is already installed to the same version:

C:\xyz\tip [master ≡]> (get-command Use-Module).ScriptBlock
param([Parameter(Mandatory)]$Name)

    if ($VerbosePreference -ne 'Continue')
    {
        Write-Host -ForegroundColor Cyan -NoNewline "Using the latest version of $Name ... "
    }

    Write-Verbose "Uninstalling all the versions of $Name ..."
    Uninstall-Module $Name -AllVersions -Force -ErrorAction SilentlyContinue
    Remove-Module $Name -Force -ErrorAction SilentlyContinue

    Write-Verbose "Installing the latest version of $Name ..."
    Install-Module $Name -Scope CurrentUser -Force

    Write-Verbose "Importing $Name into the current session ..."
    Import-Module $Name -Force

    if ($VerbosePreference -ne 'Continue')
    {
        Write-Host -ForegroundColor Cyan (Get-Module $Name).Version
    }

Let us use it now:

C:\xyz\tip [master ≡]> use-module xyz.ps.core
Using the latest version of xyz.ps.core ... 1.0.19107.7

Let us check the function source:

C:\xyz\tip [master ≡]> Get-Command Get-PullRequestsFromCommitIds | ft -AutoSize

CommandType Name                          Version     Source
----------- ----                          -------     ------
Function    Get-PullRequestsFromCommitIds 1.0.19107.4 xyz.ps.core

Still the old one. Notice, that in a new Powershell window the function is taken from the current version of the module.

Is it possible to refresh the function without closing powershell?

mark
  • 59,016
  • 79
  • 296
  • 580

1 Answers1

3

The behavior is all about scopes. The TLDR:

Sessions, modules, and nested prompts are self-contained environments, but they are not child scopes of the global scope in the session.

Basically, since modules are self-contained environments and not a child scope, they can't import modules into the "parent" script scope. Even if you use -Force.

Let's test scopes inside a Module:

sampleModule.psm1

Function Test-Import { 
param([Parameter(Mandatory)]$Name)
    Write-Host "List Loaded modules before"
    Get-Module

    Write-Host "Importing $Name into the current session ..."
    Import-Module $Name -Force

    Write-Host "Module Version $((Get-Module $Name).Version)"

    Write-Host "Loaded Modules After"
    #List Loaded modules after
    Get-Module
}

#Only present desired functions
Export-ModuleMember -Function Test-Import

If we start out with a simple blank slate test (I have removed the extraneous modules for brevity):

PS C:> #Clean state - Nothing Loaded for demonstration
PS C:> Get-Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------

PS C:> Import-Module .\sampleModule.psm1
PS C:> Get-Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Test-Module                         {Test-Import}

PS C:> Test-Import ActiveDirectory
List Loaded modules before

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Test-Module                         {Test-Import}

Importing ActiveDirectory into the current session ...
Module Version 1.0.1.0

Loaded Modules After

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   1.0.1.0    ActiveDirectory                     {Add-ADCentralAccessPolicyMember, Add-ADComputerServiceAccount, Add-ADDomainControllerPasswordReplicationPolicy, Add-ADFineGrainedPasswordPolicySubject...}
Script     0.0        Test-Module                         {Test-Import}      

Here we notice that the ActiveDirectory module was not present in the start of the function, but was indeed loaded at the end of the function, and reported the correct version. Now let's see if it loaded:

PS C:> Get-Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Test-Module                         {Test-Import}

As we can see, since Modules run in their own self contained environment, we successfully imported the module (ActiveDirectory in this example) into the Module scope, but not into the local scope like you were expecting.

The only way to get around this scope issue is to import the module into the global scope by adding -Global like:

Import-Module $Name -Force -Global

Changing that one line in the sample script, and re-importing:

PS C:> Import-Module .\sampleModule.psm1 -Force

PS C:> Get-Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Test-Module                         {Test-Import}

PS C:> Test-Import ActiveDirectory
List Loaded modules before

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Test-Module                         {Test-Import}

Importing ActiveDirectory into the current session ...
Module Version 1.0.1.0

Loaded Modules After

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   1.0.1.0    ActiveDirectory                     {Add-ADCentralAccessPolicyMember, Add-ADComputerServiceAccount, Add-ADDomainControllerPasswordReplicationPolicy, Add-ADFineGrainedPasswordPolicySubject...}
Script     0.0        Test-Module                         {Test-Import}      

Same as before... Now let's check if it loaded correctly:

PS C:> Get-Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   1.0.1.0    ActiveDirectory                     {Add-ADCentralAccessPolicyMember, Add-ADComputerServiceAccount, Add-ADDomainControllerPasswordReplicationPolicy, Add-ADFineGrainedPasswordPolicySubject...}
Script     0.0        Test-Module                         {Test-Import}      

Success!

HAL9256
  • 12,384
  • 1
  • 34
  • 46