- Create a certificate-signed RDP shortcut via Group Policy - Fri, Aug 9 2019
- Monitor web server uptime with a PowerShell script - Tue, Aug 6 2019
- How to build a PowerShell inventory script for Windows Servers - Fri, Aug 2 2019
Traditionally, there were two primary ways to export a function from a module in PowerShell: to use the module manifest's FunctionsToExport key or use the Export-ModuleMember cmdlet from functions inside the PSM1 file. Either method would allow you to export all or certain functions of a module selectively and make them available to the user.
Both of these methods required the module's PSM1 file to contain all the functions, which can sometimes be problematic. What if you'd like to separate functions by their own PS1 files? Using this method, it's impossible. Instead, we can implement another method.
For example, perhaps I have a module called Foo with two functions inside; it has a function called Get-Thing and one called Get-ThingHelper. The Get-ThingHelper is just an internal function Get-Thing calls and should not be available to the user. I'd only like to make Get-Thing available to the user. I only want to export Get-Thing.
I could create a manifest and place it into Foo's module folder ensuring the FunctionsToExport key is defined like below.
@{ RootModule = 'Foo.psm1' ModuleVersion = '1.0' GUID = '63ff946a-8c58-419c-8bbd-5c0c6cc8bd7c' Author = 'Adam Bertram' CompanyName = 'Adam the Automator, LLC' CmdletsToExport = @() VariablesToExport = '*' AliasesToExport = @() }
I'll then import the module and see that Get-Thing is the only function exported.
This is all well and good, but I've still got the Get-ThingHelper function in the PSM1 file. I'd like to separate my functions into different PS1 files so I can manage them easier. To separate the functions, we'll have to figure out how to dot-source these PS1 files into the session yet still make them part of the module.
First, I need to create separate PS1 files and cut/paste each function inside the PSM1 into each file creating two files: Get-Thing.ps1 and Get-ThingHelper.ps1. I'll put these files inside the root module folder in C:\Files. I'll also go ahead and remove the FunctionsToExport manifest key as well since we won't be using that anymore. I now should have a blank PSM1 file, a PSD1 file with no FunctionsToExport key, and two PS1 files inside of my module folder.
I now need to make these functions part of the module somehow through dot-sourcing. All I have to do to make that happen is to dot-source the files inside the PSM1 file. If scripts are dot-sourced inside the module itself and those scripts contain functions in them, PowerShell will automatically make them part of the module.
Here's a great example you can use to differentiate the functions you'd like to export and the ones you don't.
$NoExport = 'Get-ThingHelper' $ModuleFunctions = @(Get-ChildItem -Path $PSScriptRoot\*.ps1 -ErrorAction SilentlyContinue) $ToExport = $ModuleFunctions | Where-Object { $_.BaseName -notin $NoExport } | Select-Object -ExpandProperty BaseName # Dot-source the files. foreach ($import in $ModuleFunctions) { try { Write-Verbose "Importing $($import.FullName)" . $import.FullName } catch { Write-Error "Failed to import function $($import.FullName): $_" } } Export-ModuleMember -Function $ToExport
You can now import the Foo module. Notice that Get-ThingHelper is not exported as it should be while the Get-Thing function is. You'll also notice that any references to Get-ThingHelper inside of Get-Thing will work since Get-ThingHelper is part of the module. It's just not been exported.
Subscribe to 4sysops newsletter!
Adding functions to a module like this allows you to manage your functions better as well as prevent merge conflicts if you're working on a team with modules checked into source control.
Actually, this information is slightly off:
There is nothing that requires the function to be defined in the psm1 file, in order for the manifest’s FunctionsToExport node to export them. Simply add the node and drop the final line in your snippet and it works well. Furthermore, including them explicitly in the manifest improves performance of command discovery (the difference of which admittedly is hard to notice in smaller modules).
My recommendation is to have a single function per file, but separate public versus internal commands by folders within your module. Then you can update the manifest automatedly as part of the build process. For a simple implementation example, you could check out the PSUtil module, for a highly complex import the dbatools module.