Beware of the PowerShell Update-ModuleManifest Function

I recently presented a session for the Mississippi PowerShell User Group on PowerShell Non-Monolithic Script Module Design. While preparing for that session, I discovered that a problem I had previously experienced with Update-ModuleManifest when trying to update the FunctionsToExport section when FormatsToProcess is specified appeared to be resolved in PowerShell version 5.1 (I’m running build 14393). The details of this bug can be found here. I also noticed that Nicholas Getchell had written about this problem being resolved on his blog.

What I discovered is that Update-ModuleManifest does a little too much updating which can cause problems. To demonstrate this problem, I’ll create a script module named MyModule containing the following code:

Get-ChildItem -Path $PSScriptRoot\*.ps1 -Exclude *.tests.ps1, *profile.ps1 |
ForEach-Object {
    . $_.FullName
}

The previous code is saved as MyModule.psm1 in the following folder: $env:ProgramFiles\WindowsPowerShell\Modules\MyModule

A function named Get-MrSystemInfo is created:

#Requires -Modules CimCmdlets
function Get-MrSystemInfo {
    Get-CimInstance -ClassName Win32_OperatingSystem |
    Select-Object -ExpandProperty Caption
}

That function is saved as Get-MrSystemInfo.ps1 in the same folder as the PSM1 file.

And finally a module manifest is created:

New-ModuleManifest -Path $env:ProgramFiles\WindowsPowerShell\Modules\MyModule\MyModule.psd1 -RootModule MyModule -Author 'Mike F Robbins' -Description 'MyModule' -CompanyName 'mikefrobbins.com'

beware-updatemodulemanifest3a.png

When designing modules with a non-monolithic approach and specifying a requires statement for a module in one of the functions, as shown in the Get-MrSystemInfo example, causes all of the functions that are part of the specified module to show up as being part of your module.

In this scenario, the CimCmdlets are showing up as being part of MyModule:

Get-Command -Module MyModule

beware-updatemodulemanifest1a.png

I’ve written about this problem in a previous blog article and it’s easy enough to resolve by removing the * from the CmdletsToExport section and replace the * in the FunctionsToExport section of the module manifest with your actual function names.

The default settings in the module manifest that was previous created in this blog article:

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = '*'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = '*'

# Variables to export from this module
# VariablesToExport = @()

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = '*'

The problem is that when you run Update-ModuleManifest to add the function name to FunctionsToExport:

Update-ModuleManifest -Path $env:ProgramFiles\WindowsPowerShell\Modules\MyModule\MyModule.psd1 -FunctionsToExport 'Get-MrSystemInfo'

beware-updatemodulemanifest2a.png

It not only changes the * in the FunctionsToExport section of the manifest to the function name that was specified, but it also updates CmdletsToExport to contain all of the cmdlets contained in the CimCmdlets module and it removes the ‘*’ from the AliasesToExport section of the manifest.

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = 'Get-MrSystemInfo'

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = 'New-CimInstance', 'Set-CimInstance', 'Get-CimSession',
               'Get-CimInstance', 'Get-CimAssociatedInstance', 'Get-CimClass',
               'New-CimSession', 'Remove-CimInstance', 'Remove-CimSession',
               'New-CimSessionOption', 'Invoke-CimMethod', 'Export-BinaryMiLog',
               'Register-CimIndicationEvent', 'Import-BinaryMiLog'

# Variables to export from this module
# VariablesToExport = @()

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()

Update-ModuleManifest had one action to perform and only one action which was to update the FunctionsToExport section in the module manifest as previous specified.

To workaround this problem in this scenario, you can change CmdletsToExport to @() prior to running Update-ModuleManifest and it won’t be changed. You could also specify the CmdletsToExport parameter with the @() value when creating the module manifest to prevent this problem from occurring.

My recommendation is not to use * anywhere in your module manifest <period>.

µ