Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell 5.1 module manifest practical difference of declaring FunctionsToExport vs CmdletsToExport

Tags:

powershell

I understand the difference between functions and cmdlets. This question is about the practical implications of when to declare a function in one or the other group of a powershell manifest file (psd1).

FunctionsToExport = @(
    'Get-BuildInformation'
)

CmdletsToExport @(
    'Get-BuildInformation'
)

One thing I noticed is that on importing a module, comment based help does not work if the cmdlet is only declared in CmdletsToExport - it has to be declared in FunctionsToExport. Should I be adding to both arrays? What am I getting by adding to CmdletsToExport?

like image 399
Avner Avatar asked Oct 31 '25 11:10

Avner


1 Answers

Functions - i.e. units of functionality written in and executed by PowerShell - and (binary) cmdlets - i.e. units of functionality created by up-front compilation of (typically) C# code into .NET assemblies - are technically distinct command forms, and must be referenced separately in a module manifest.

When a caller imports a module - whether explicitly via Import-Module or implicitly via module auto-loading - that has a manifest, functions and cmdlets are only imported if they're referenced in the command form-appropriate manifest entry, i.e. FunctionsToExport and CmdletsToExport, respectively.

Mistakenly referencing a function in CmdletsToExport or vice versa is quietly ignored, though note that a module's functions / cmdlets may still be imported, namely if the FunctionsToExport / CmdletsToExport entry is either missing or set to * (neither is recommended, for performance reasons).

Use the -Verbose switch with Import-Module to see which functions and cmdlets (and possibly also aliases and variables) are in effect imported from a given module: look for the (final) block of lines that look like VERBOSE: Importing <command-form> <name>, e.g. VERBOSE: Importing function Get-BuildInformation

Note:

  • It is only the last or only block of verbose Importing ... lines that reflect what is actually imported, as detailed below.

  • Exporting ... lines alone do not guarantee import, because the module import process has two stages:

    • First, the set of candidate import elements is determined, as implied by the .psm1 module / a referenced assembly (.dll) containing cmdlets.

      • In a .psm1 (script) module, all functions and aliases are exported by default, unless constrained (or expanded to include variables) by an explicit call to Export-ModuleMember.

        • The resulting candidate elements are reflected in verbose Exporting ... lines (requested via -Verbose or via $VerbosePreference = 'Continue').
      • In a .dll (binary) module, it is invariably all contained cmdlets that are candidate elements, i.e. all public classes that derive (ultimately) from the System.Management.Automation.Cmdlet base class.

        • In a hybrid module - one who combines a script module with binary modules - the resulting candidate cmdlets are - unfortunately and confusingly - reflected in verbose Importing ... lines, albeit in a separate block that precedes the Loading module from path '.../*.psm1' line, which is (eventually) followed by another block of Importing ... lines, which are the ones that matter - see below.

        • In a binary-only module, no verbose lines reflecting the candidate cmdlets are emitted.

    • Then, the set of candidate elements is typically constrained further for actual import, based on the relevant entries in the module manifest file (.psd1).

      • It is this constrained set of actual import elements that is reflected in the last or only block of verbose Importing ... lines.

      • As noted, referencing an element in the wrong entry (e.g. referencing a function in CmdletsToExport) is quietly ignored.

      • If you were to bypass a module's .psd1 file by importing a .psm1 or .dll file directly, it is the candidate elements that would directly become the effectively imported elements.
        However, using a full-fledged module (one that lives in its own directory and includes a manifest file (.psd1)) is always advisable, for two reasons:

        • It allows you to associate metadata, notably a version number, with your module.

        • (Preferably) explicitly enumerating all exported elements in the manifest not only allows you to select the desired subset of candidate elements to export/import, but also helps speed up PowerShell's module discovery, which, notably, the module auto-loading feature is built on.

          • This is the reason why omitting manifest entries or setting them to * is discouraged.

As far as I can tell, none of this is related to comment-based help, which inherently only applies to functions and script files.

like image 164
mklement0 Avatar answered Nov 03 '25 19:11

mklement0