user3654055
user3654055

Reputation: 178

Why isn't my PowerShell module exporting members correctly and why isn't it exporting Nested Modules?

I'm working on a Quick Module CLI builder. The idea is you have my Module, which has commands like Add-QuickFunction, Add-QuickAlias, New-QuickModule, Remove-QuickCommand, etc.

As you build out the functions, your Modules continue to get built out. The plan is to create an Export-QuickModule and Export-QuickModule command, so that when you're ready to Export your module it's ready to be published, to your organization, or to a public gallery.

So the problem is two-fold.

  1. The exported members do not match the commands in the module
PS C:\WINDOWS\system32> Get-Module -Name QuickModuleCLI

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.1.3      QuickModuleCLI                      {New-QuickModule, Update-QuickModule}

Notice how it only includes 2 Exported commands. This is after I manually added Export-ModuleMember to my local system. Without having Export-ModuleMember, only New-QuickModule appears as exported.

But compare it to the actual list of Commands:

PS C:\WINDOWS\system32> Get-Command -Module QuickModuleCLI

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Add-QuickAlias                                     0.1.3      QuickModuleCLI
Function        Add-QuickFunction                                  0.1.3      QuickModuleCLI
Function        ConvertTo-PowershellEncodedString                  0.1.3      QuickModuleCLI
Function        Edit-QuickCommand                                  0.1.3      QuickModuleCLI
Function        New-QuickModule                                    0.1.3      QuickModuleCLI
Function        Remove-QuickCommand                                0.1.3      QuickModuleCLI
Function        Rename-QuickCommand                                0.1.3      QuickModuleCLI
Function        Reset-QuickCommand                                 0.1.3      QuickModuleCLI
Function        Set-Env                                            0.1.3      QuickModuleCLI
Function        Update-QuickModule                                 0.1.3      QuickModuleCLI
  1. The Nested Module isn't getting imported into the PowerShell session until after I've imported the Module. I can deal with it by adding Import-Module to my $Profile if I have to, but I just want to make sure I'm not doing anything wrong.

What this means is, after opening a new session, I see the following:

PS C:\WINDOWS\system32> Write-Test
Write-Test : The term 'Write-Test' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ Write-Test
+ ~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Write-Test:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS C:\WINDOWS\system32> Import-Module QuickModuleCLI
PS C:\WINDOWS\system32> Write-Test
Hello

I use a dynamic self-rolled PowerShell generator to update the psd1 file and add to the FunctionsToExport. You can verify that when I Import-Module, it calls the NestedModules and imports it properly. However, on the computer I use to develop this Module in, I don't need to Import-Module in order for Write-Test to appear in my PowerShell Options. Is this an environmental thing that I have to just "deal with" or is this something you that can be fixed?

The code can be found here: https://github.com/EdLichtman/QuickModuleCLI And you can try it out yourself with Import-Module QuickModuleCLI

Upvotes: 2

Views: 1443

Answers (2)

user3654055
user3654055

Reputation: 178

So to follow up on part 2, it turns out the reason why is related to, in fact it's exactly the same behavior described in this answer:

Invoking functions from nested modules in a script module do not always trigger a module to autoload

My workaround was to add to the FunctionsToExport list, the functions within the NestedModules. Initially I did not think I could export functions that were not directly part of the parent module.

Upvotes: 0

mclayton
mclayton

Reputation: 10125

Part 1 - ExportedCommands

Here's a minimal repro for your issue with the ExportedCommands list not matching the result from Get-Command. I distilled it down to this by progressively removing chunks of code from your repo at https://github.com/EdLichtman/QuickModuleCLI until there was nothing left to remove:

MyModule.psd1

@{

RootModule = "MyModule.psm1"

ModuleVersion = "0.1.4"

FunctionsToExport = @(
    "Add-MyAlias",
    "New-MyModule"
)

}

MyModule.psm1

function global:Add-MyAlias {
    write-host "global:Add-MyAlias"
}

function New-MyModule {
    write-host "New-MyModule"
}

Interactive

PS> Import-Module .\MyModule\MyModule.psd1 -Force

PS> Get-Module MyModule

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.1.4      MyModule                            New-MyModule

PS> Get-Command -Module MyModule

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Add-MyAlias                                        0.1.4      MyModule
Function        New-MyModule                                       0.1.4      MyModule

I don't know what the exact implication is when you use global: on an exported function (I'm assuming the function gets imported into the global scope rather than the module scope), but if you remove global: the function will suddenly appear in the ExportedCommands list again:

Part 2 - Importing Modules

PowerShell has a feature that will cause it to search for unknown commands in a set of predefined folder locations. This is called "Module Auto-Loading" and you can enable or disable the feature using the $PSModuleAutoLoadingPreference variable as per About Preference Variables (hint: it's enabled by default).

The locations it searches are defined in an environment variable PSModulePath - see About PSModulePath.

If you want the commands in your module to be discoverable and auto-loaded on first invocation, either install your module in one of the paths already added to the PSModulePath environment variable, or add your module's location to the path.

Upvotes: 2

Related Questions