Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

powershell

If I create a manifest module with nested modules, exported functions from all nested modules after the first do not appear in the list of available commands and don't trigger the module to autoload.

They also do not appear when I run "Get-Module -ListAvailable".

Only the exported functions from the first nested module appear in the list of commands.

If I explicitly import the module, all exported functions are available.

In the example below, Update-LegacyServices is not available until the module has been explicitly imported.

The only way I can make it work it to rename my module files to end with ps1 instead of psm1 and include them in ScriptsToProcess, which seems like a bad idea.

Module manifest (psd1)

@{

# Script module or binary module file associated with this manifest.
# RootModule = ''

# Version number of this module.
ModuleVersion = '1.0.0.1'

# ID used to uniquely identify this module
GUID = 'c11d6aca-d531-4d06-a732-5fb95113357f'

# Author of this module
Author = 'luke'

# Company or vendor of this module
CompanyName = ''

# Copyright statement for this module
Copyright = ''

# Description of the functionality provided by this module
# Description = 'MyBudget Developer Powershell Module'

# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '4.0'

# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''

# Minimum version of the Windows PowerShell host required by this module
# PowerShellHostVersion = ''

# Minimum version of the .NET Framework required by this module
DotNetFrameworkVersion = '4.5.0'

# Minimum version of the common language runtime (CLR) required by this module
CLRVersion = '4.0.30319.18444'

# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''

# Modules that must be imported into the global environment prior to importing this module
RequiredModules = 'BitsTransfer'

# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()

# Script files (.ps1) that are run in the caller's environment prior to importing this module.
ScriptsToProcess = @()

# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()

# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
NestedModules =  @('database\Database.psm1', 'build\Build.psm1')

# Functions to export from this module
#FunctionsToExport = '*'

# Cmdlets to export from this module
CmdletsToExport = '*'

# Variables to export from this module
VariablesToExport = '*'

# Aliases to export from this module
AliasesToExport = '*'

# List of all modules packaged with this module.
ModuleList = @('database\Database.psm1', 'build\Build.psm1')

# List of all files packaged with this module
# FileList = @()

# Private data to pass to the module specified in RootModule/ModuleToProcess
# PrivateData = ''

# HelpInfo URI of this module
# HelpInfoURI = ''

# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''

}

Module 1 (Build\Build.psm1)

function Update-LegacyServices()
{
    echo "Update"
}

Export-ModuleMember -Function Update-LegacyServices

Module 2 (database\Database.psm1)

Function Get-Backup($directory, $name)
{
    echo "Get-Backup"
}

Export-ModuleMember -Function Get-Backup
like image 680
LukeN Avatar asked Oct 08 '14 06:10

LukeN


People also ask

What are nested modules?

»Nesting modules. A nested module is a reference to invoke another module from the current module. Nested modules can be located externally or embedded inside the current workspace. Being able to nest modules is powerful feature; however, you should exercise caution to limit introducing errors.

What are you doing when you use the Import-Module cmdlet?

Description. The Import-Module cmdlet adds one or more modules to the current session. Starting in PowerShell 3.0, installed modules are automatically imported to the session when you use any commands or providers in the module. However, you can still use the Import-Module command to import a module.

How do I load a PowerShell module?

To import the module into all sessions, add an Import-Module command to your PowerShell profile. To manage remote Windows computers that have PowerShell and PowerShell remoting enabled, create a PSSession on the remote computer and then use Get-Module -PSSession to get the PowerShell modules in the PSSession.

How do I manually import a PowerShell module?

To install PowerShell modules manually, you first need to determine your current PowerShell module directory path, download your new module to that path, and invoke the import-module command to let windows know it's there.


2 Answers

I had this line in my .psd1 file

FunctionsToExport = 'FuncFromMainPsm1 FuncFromSecondPsm1'

which was what I inferred from the line generated by Powershell Tools for Visual Studio:

# Functions to export from this module
FunctionsToExport = '*'

This caused my Get-Module -ListAvailable to show what looked like the right thing

Script     1.0        MyMModule                  FuncFromMainPsm1 FuncFromSecondPsm1

But when I called FuncFromSecondPsm1 I'd get "The term 'FuncFromSecondPsm1' is not recognized...".

So I changed my export line to

FunctionsToExport = @('FuncFromMainPsm1', 'FuncFromSecondPsm1')

And now it all works. I can call both functions after my module loads, whether via autoload or Import-Module.

I have tried this with and without both ModuleList and FileList set. They make no difference.

like image 165
Paul Hicks Avatar answered Oct 02 '22 17:10

Paul Hicks


Hy Paul, Hy Luken,

After struggling for a few hours on this topic (with the implicit and declared manifest approach), I opted for the auto generated manifest. In essence, I wrote a ps1 script that generates the manifest, in two steps:

  1. with some 'business' logic, discover what you want to expose
  2. generate the module manifest

IMHO, Advantage of that approach: I 'fully' understand and master the exposure of the module content.

below you can find a PS code snippet that follows this approach, hoping it will benefit to other people.

# module discovery
$rootModule = "WdCore";
$modules = Get-ChildItem *.psm1;
$nestedmodulesNames = $modules | where { $_.BaseName -ne "WdCore"} | % { $_.BaseName } ;

# functions discovery
$modulesLines = $modules | Get-Content;
$functionsLines = $modulesLines | where { $_.contains("Function") };

$functionNamePattern = '^Function\s+(?<name>([a-z][0-9|A-Z|-]+))\s?{.*$';
$functionNames = $functionsLines | where { $_ -match $functionNamePattern } | select { $Matches['name'] } | % { $_.' $Matches[''name''] '};

# generate manifest
New-ModuleManifest `
    -Path ./WdTools.psd1 -RootModule $rootModule `
    -ModuleVersion '1.0' `
    -NestedModules $nestedmodulesNames `
    -FunctionsToExport $functionNames `
    -Guid '57D7F213-2316-4786-8D8A-3E4B9262B1E5' `
    -Author 'Blaise Braye' `
    -Description 'This module provides working directory tooling' `
    -PowerShellVersion '3.0' -ClrVersion '4.0';
like image 33
Blaise Avatar answered Oct 02 '22 15:10

Blaise