How to expose sub module function, from a module - powershell

How do you expose a function and an alias from a module, from a sub module
profile.ps1:
Import-Module module_one.psm1
module_one.psm1:
Import-Module module_two.psm1:
module_two.psm1:
Set-Alias readme -Value "Read-Me"
function Read-Me() {
Write-Host "Hello..."
}
Export-ModuleMember -Function Read-Me, readme
I want to be able to call this function from the pwsh terminal, function name or alias?
I get (readme or Read-Me):
Read-Me : The term 'Read-Me' 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
+ co
+ ~~
+ CategoryInfo : ObjectNotFound: (co:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Note: it works for functions in module_one.psm1.

Assuming that you call Import-Module module_one.psm1 and later readme / Read-Me in the same scope domain (either outside a module or from the same (other) module), your code works in principle:
The exported elements of the indirectly imported module are (also) imported into the top scope of the caller's scope domain.
Your problem is that you're trying to export alias readme as a function; you need to use the separate -Alias parameter instead:
Export-ModuleMember -Function Read-Me -Alias readme # Note the -Alias parameter
Also note that in the absence of an Export-ModuleMember call it is all (top-level) functions and aliases (but not variables) that are exported by default, so in your particular case simply omitting the call would have fixed the problem too.
In general, though, it is better to be explicit about what elements are exported, and that is best done via a full-fledged module that is not just a single *.psm1 file, but an entire directory named for the module, housing the *.psm1 file with an associated module manifest, which is a *.psd1 file that describes the module, notably also in terms of its exports.
If such a module is placed in one of the directories listed in $env:PSModulePath, its exported commands can be discovered (e.g., with Get-Command or tab-completion) even before the module is imported, via a feature called auto-loading - see about_Modules.
Note that Import-Module -Global is rarely needed and should generally be avoided, because it makes a module's exported elements available to all loaded modules too, which can have unintended side effects (modules should declare their dependencies explicitly).

Related

How to export variables from a PowerShell module

I've defined a variable in a psm1 file but when I try to access it in another script, after importing the module, I'm not seeing the value set in the psm1 file.
globals.psm1
$blah = "hello world"
my-script.ps1
Import-Module "$PSScriptRoot\globals.psm1" -Force -Verbose
Write-Output "blah: ${blah}"
output
PS C:\blah> .\my-script.ps1
VERBOSE: Loading module from path 'C:\blah\globals.psm1'.
blah: ''
I thought all variables get exported by default. I must be interrupting this wrong:
Specifies the variables that the module exports to the caller's session state. Wildcard characters are permitted. By default, all variables ('*') are exported
source: MSFT Docs -> How to write a PowerShell module manifest
(CTRL + F on 'VariablesToExport' to find the quoted text)
And yes, if I export the variable, I can access it but the documentation says: 'By default, all varialbes ('*') are exported so what am I doing wrong or misunderstanding? 🤔
globals.psm1
$blah = "hello world"
Export-ModuleMember -Variable blah
Your module is not using a module manifest (a companion .psd1 file whose RootModule entry points to your .psm1 file in the case of script modules), whereas the documentation you quote pertains to module manifest-based modules.
If a module consists only of a .psm1 file, and that file contains no Export-ModuleMember calls, the following rule applies:
Only functions and aliases are automatically exported.
Conversely, this means: in order to also export variables, you must use an Export-ModuleMember call - and if you do, the slate is wiped clean, so to speak, and you must explicitly specify all definitions you want to export (in the simplest case, use Export-ModuleMember -Function * -Alias * -Variable *).
Also, be sure to place this call at the end of your .psm1 file, to ensure that all definitions to export have already been defined.
Caveat, if a manifest (.psd1) is used:
The manifest's *ToExport keys apply on top of what the .psm1 file - implicitly or explicitly - exports, i.e. you can use it to further narrow what is to be exported, by explicitly enumerating the elements to export, which not only makes the module more self-describing, but also helps performance when PowerShell auto-discovers the commands in available, but not-(yet)-imported modules.
Therefore, if a manifest-based module wants to export variables, it too must have an explicit Export-ModuleMember call in its .psm1 file, with the manifest potentially narrowing down what variables are ultimately to be exported.
Generally, exporting variables from modules is best avoided, because:
it increases the risk of name collisions with variables of the same name defined elsewhere.
discovering which variables are exported by what module isn't as well-known as use of Get-Command is in order to determine what module a given function, cmdlet, or alias comes from. This is because (a) modules that export variables are rare and users generally don't expect it, and (b) the Get-Variable cmdlet - which can tell you what module a variable is defined in - isn't often used in practice.
To see which definitions a given module exports, pass -Verbose to the Import-Module call that imports it. Additionally, pass -Force in order to force re-loading of an already imported module.

Exporting PS Members from Module Fails

I have a psm1 module with several functions. I only want to expose some of the functions, so I used "Export-ModuleMember -Function " and everything was fine.
Now I want to add a manifest, so I removed the Export-ModuleMember cmdlet and put the function names under the FunctionsToExport section of the psd1 file.
When I import the module, I can tab-complete the functions, but when I try to use them, ps says:
Test-Function : The term 'Test-Function' 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.
I also tried putting Export-ModuleMember -Function * into the psm1, but that didn't help.
The psm1 and psd1 files are named the same and are in the root of the module.
Thoughts? Thanks.
I found the answer. I had forgotten to un-comment the RooteModule node. Once I did that (and had module.psm1 as the value), the exported commands showed up in the "Get-Module module" output.
I would suspect that PowerShell caching mechanism is involved here. Try to run:
Get-Module -ListAvailable -Refresh
I recommend very good article on that subject written by PowerShell MVP Tobias Weltner.

Powershell module not loading

I am trying to load a PowerShell module that executes a custom cmdlet function but I can't get it to load... I've applied the solutions of several previous questions, but now I'm just going in circles. Here are the specs of what I have done so far and the specific error that returns. Note that as I am new to PowerShell and programming in general, it wouldn't surprise me that my problem isn't a file path issue but a logic error in my actual function:
I created a profile function for a custom cmdlet that allows me to
open project files from two different paths:
function push-project( $project )
{
pushd ~/Documents/Visual Studio 2015/Projects/$project;
pushd ~/Documents/GitHub/$project;
}
New-Alias -Name pp -Value push-project;
I created a module by saving the function as ProfileFunctions.psm1
in the following directory:
~\Documents\WindowsPowerShell\Modules\ProfileFunctions\ProfileFunctions.psm1
To invoke the function, per its syntax, I type in pp $projectName into the PS console window, but the error that returns is standard not recognized:
pp : The term 'pp' 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
pp MyFirstApp
~~
CategoryInfo : ObjectNotFound: (pp:String) [], CommandNotFoundException
FullyQualifiedErrorId : CommandNotFoundException
I copied and pasted your code into a Windows 8.1 machine I have here. It worked great (apart from the folder names not existing, since I don't have Visual Studio).
Things off the top of my head that might stop your module from working:
The file is actually called ProfileFunctions.psm1.ps1 or ProfileFunctions.psm1.txt or something.
The Modules folder is saved in someone else's documents folder, or the Public documents folder.
You've accidentally put a space in the folder name (it must be WindowsPowerShell, not Windows PowerShell).
I Think your problem is that the Alias pp is not exported from the module.
You either define the alias outside the module, as supposed to or explicitly export it from the module using.
Export-ModuleMember -Function pushproject -Alias pp
Find more details in this article Unable to Create a Powershell Alias in a script Module

Calling a function within module / Powershell

I must be missing something basic here, but i'm new to powershell...
I wrote a function and saved it in a file called "UserSelectionList.psm1", the function is stubbed out like this:
function Global:UserSelectionList([String[]] $UserOptions)
{
...
}
i then try to call it with this script:
Import-module "l:\support downstream\solarc\cngl\powershell scripts\userselectionlist.psm1"
$Options = "a","b","c"
cls
$result = UserSelectionList $Options
echo $result
The resulting error is:
The term 'UserSelectionList' 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:5 char:28
+ $result = UserSelectionList <<<< $Options
+ CategoryInfo : ObjectNotFound: (UserSelectionList:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I'm planning to have more than one function in a module, but this is where I'm at.
thanks in advance
I've encountered the same problem. Steps to reproduce:
Write a PowerShell script with an Import-Module statement
Execute the script at a PowerShell prompt
Add a function to the imported module
Modify the script to call the newly-added function
Execute the script again, in the same PowerShell session
The error went away after I added the -Force argument to Import-Module. The -Force argument can be removed once the function in the imported module is able to be called.
Note that latkin has alluded to this solution in his comment to the question. I'm hoping this will make it more visible.
You only need to Get-Command if you didn't export the method properly from the module.
At the end of your module put this:
Export-ModuleMember -Function UserSelectionList
Note that it also accepts wild cards, so for example if you have 5 different Get-Some-Value functions that follow a naming convention you could just do
Export-ModuleMember -Function Get-*
Side note on -Force: all that does is check if a module is already loaded and, if it is, removes it before continuing with the import. It's the same as saying:
Remove-Module userselectionlist.psm1
Import-Module userselectionlist.psm1
[Edit] I was not doing a Import Module with a -Force option. The answer below is incorrect, but perhaps the Get-Command forced a refresh? Either way, I'm leaving it for completeness of the experience!
Thanks to latkin for pushing me to another path where i found this:
How do I retrieve command from a module
Not only do you have to import a module, you then have to "get" it as well (?)
Import-Module -Name <ModuleName>
Get-Command -Module <ModuleName>
After I issued the Get-Command, everything started to work!
Thanks latkin for quick response!

Unable to create a PowerShell alias in a script module

Steps to reproduce:
Create a TestAlias module in \WindowsPowerShell\Modules\TestAlias\TestAlias.psm1 with the following function and alias:
function foo
{ write-output 'foo' }
New-Alias -name bar -value foo
From a PowerShell session:
import-module TestAlias
bar
The term 'bar' is not recognized as the name of a cmdlet, function, script file, or operable program...
You can use
Export-ModuleMember -Function * -Alias *
to export all functions and aliases.
By default, Windows PowerShell modules only export commands (functions or cmdletS), and not variables or aliases.
I'll go into a little more detail about why this is.
The short answers is that aliases, while convenient when writing one liners, are a barrier to understanding a script or a module. They are icing on the cake of a good cmdlet, but the core thing to expose is the good cmdlet. Aliases make it more difficult for a user reading your script to figure out what you're trying to do (Set-Content is a lot easier to understand than sc). Variables can be even worse to expose, as they can easily be set to unexpected values and as there is very little to help a user of your module figure out that they are there. Because commands are easily discoverable (Get-Command -Module FOO) and easier to explore (with Get-Help), the default that a module will export is only commands. As with most other things in PowerShell, you can override it if you choose, but by default commands are the only thing that are exported from a module.
Hope this Helps
Use Export-ModuleMember in the PSM1 file to export the Alias
Export-ModuleMember -function foo -Alias bar