Powershell module function not visible in Get-Modules - powershell

I have a simple powershell module containing a single function, an abridged version of this is as follows:
function My-Func
{
.
.
.
}
Export-ModuleMember -Function 'My-Func'
My manifest file contain a line to explicitly export this:
FunctionsToExport = "My-Func"
Everything uploads to the powershell gallery via Publish-Module without any problems, then when I come to install this and run Get-Module, I do not see the function in the export commands column of the output, also I when I attempt to call the function powershell tells me it does not exist.
I have a psm1 file for my module and a psd1 manifest, for some reason when I only see Manifest as the module type, I'm guessing I need to see script ?.
Any ideas ?

in your .psm1:
remove the Exported-ModuleMember line
in your .psd1:
RootModule = 'yourmodule.psm1'
FunctionsToExport = #('function1','function2')
the psd1 file FunctionsToExport works like the Exported-ModuleMember command. it's a cleaner way to define things from one centralized file.

Related

Accessing a hash table through a module

I have defined this in module as defblah.ps1
$defblah= #{
first = "aaa";
seconf = "bbb";
}
I also have this in module as Blah.ps1
Function Blah
{
Write-Host $defblah.first;
}
I did Export-ModuleMember and then Import-Module, so everything with module definition (export, import) is fine.
Then, after importing I run Blah, it prints "aaa", which is what i expected.
The problem is when I type in
Write-Host $defblah.first;
it returns nothing. How can I make my last statement work and return "aaa"?
Generally, I suggest not exporting variables from a module, even though it is technically supported.
A module's implicit export behavior indeed precludes variables - in PSv5+, only functions and aliases are exported by default - meaning the absence of an Export-ModuleMember and/or module manifest (*.psd1) controlling the exports.
The general expectation is for a module to export commands (functions / cmdlets and possibly also aliases), not data (variables) - and commands alone present enough potential for name collisions.
If you truly want to export variables from your module, use an Export-ModuleMember call with the
-Variable parameter and/or - if your module comes with a manifest file - export the variables via the VariablesToExport key.

How to override powershell global module functions?

I have a powershell module file C:\Program Files\WindowsPowerShell\Modules\MathModule\MathModule.psm1
I have added this so that I can access the functions in the module easily from anywhere. Lets say this contains a function called Add-Numbers.
I have another project where I have MathModule.psm1 with another implementation of Add-Numbers function. From test.ps1 in the same directory I write
Import-Module ".\MathModule.psm1"
Add-Numbers 1 2 3
I was expecting this to use the Add-Numbers definition from the locally imported MathModule.psm1. However, it continues to use the definition from C:\Program Files\WindowsPowerShell\Modules\MathModule\MathModule.psm1
How can I override Add-Numbers from local MathModule.psm1 in test.ps1 instead of the one in Program Files?
If the MathModule module was previously loaded you'll need to add -Force to truly reload it (even from a different psm1 file).

Import-Module Verbose output implies module is loaded twice

I have a module (MyModule) under a non-standard path, i.e. not under the usual locations listed in $env:PSModulePath -split ";". I have, however, added the "production" path to MyModule to that environment variable while I continue to work on a "development" copy.
Whilst trying to debug something, I loaded the module (with $VerbosePreference = "Continue") using the following command and immediately saw two seemingly contradictory lines of Verbose output:
[D:\Dev\UserA\]> Import-Module D:\Dev\UserA\libs\PowerShell\MyModule
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\MyModule.psd1'.
VERBOSE: Loading module from path 'D:\Dev\usera\MyModule2\MyModule.psm1'.
I would like to understand why Import-Module appears to be loading the module twice, especially as the second path is incorrect.
MORE DETAIL:
The folder structure for the module is:
MyModule\MyModule.psd1
MyModule\MyModule.Test-Module.xml
MyModule\MyModule1\MyModule.psm1
MyModule\MyModule2\MyModule.psm1
Note (1) I retained an older "version 1" of this module in a MyModule1 sub-folder and put my updated "version 2" file in a MyModule2 sub-folder and (2) that the .xml file is used by a custom module-testing script to list test cases. I'm pretty sure that the latter can be ignored.
My module manifest (.psd1) file contains the following, with all other lines being blanks or comments:
#{
RootModule = '.\MyModule2\MyModule.psm1'
ModuleVersion = '2.0.0.0'
GUID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
Author = 'Old Developer (v1.x) & New Developer (v2.x)'
CompanyName = 'MyCompany'
Copyright = '(c) 2013-2015 MyCompany. All rights reserved.'
Description = 'Really useful functions'
FileList = #(
'.\MyModule.psd1'
'.\MyModule.Test-Module.xml'
'.\MyModule1\MyModule.psm1'
'.\MyModule2\MyModule.psm1'
'.\MyModule2\Examples\Archive-FilesWithCompression.ps1'
)
}
Clearly I have used relative paths for the files, esp. the RootModule key; this is necessary as I cannot be sure where the module will be copied when I share it.
Going back to the Verbose output, I can see that the two lines show (1) the correct path to the PSD1 file and (2) an invalid path to the PSM1 file. I did notice that the second path has the username in lower-case, which is how I happened to type it when I Set-Location before testing. So, it looks like the first path is taken by appending MyModule.psd1 to the path given to the Import-Module cmdlet and the second is a concatenation of (Get-Location) and the RootModule path.
This only seems to happen to this module. I have others under the same 'root' folder which don't exhibit this behaviour.
Um. OK. I may have worked it out, at least in part...
I had accidentally made a copy of the MyModule2 sub-folder under the location from where I was running the Import-Module cmdlet. Once I removed that folder the verbose output started to make more sense:
[D:\Dev\UserA\]> Import-Module D:\Dev\UserA\libs\PowerShell\MyModule
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\MyModule.psd1'.
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\.\MyModule2\MyModule.psm1'.
VERBOSE: Exporting function '<function>'.
....
I guess this means that when PowerShell resolves the RootModule in the manifest file it looks under the current path first and then the main module folder if nothing is found. I find this counter-intuitive as I'd expect that any relative paths in the manifest would always be relative to the PSD1 file, not the current location.
If I then try to import the module again immediately I get this:
[D:\Dev\UserA\]> Import-Module D:\Dev\UserA\libs\PowerShell\MyModule
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\MyModule.psd1'.
VERBOSE: Importing function '<function>'.
....
That is, only one "loading module" line, rather than the two lines of verbose output. I then tried the -Force switch and got the following:
[D:\Dev\UserA\]> Import-Module D:\Dev\UserA\libs\PowerShell\MyModule -Force
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\MyModule.psd1'.
VERBOSE: Removing the imported "<function>" function.
....
VERBOSE: Loading module from path 'D:\Dev\UserA\libs\PowerShell\MyModule\.\MyModule2\MyModule.psm1'.
VERBOSE: Exporting function '<function>'.
....
VERBOSE: Importing function '<function>'.
....
So, it seems that the two lines of verbose output appear when the module is first imported, or forcibly re-imported, but if it is already part of the session then you only see the first line.
Also, I finally spotted that the verbose output differentiated between exporting functions and importing them. The functions are exported in the order that they are defined in the PSM1 file, but are imported in alphabetical order. This suggests a one- or two-stage process is used depending on whether the function definitions are being re-read from the PSM1 file, tying in with whether we see one or two lines of verbose output.
Or, to answer the question more directly, it appears that there are two lines saying "Loading module..." when it needs to read the .psm1 file, and only one line when PowerShell is already aware of the module and its contents.

How do I turn a collection of script files into a module?

Do you guys know, whether it's possible to convert PowerShell project consisting solely of functions, into module? What I want to achieve is to create distributable module of all my functions so others can use it. But without spending time of converting all functions into cmdlets.
Each of my functions is in separate file. When I then create *.psd1 and I try to include functions via 'FunctionsToExport', it doesn't work. I can't see my functions after module is loaded.
Is it even possible to export function from module when they're NOT (all of them) inside a .psm1 file? I'm still trying to figure out real differences and use of *psd1 and *psm1 files.
Yes, you can turn a bunch of .ps1 files into a module. Create a new folder in your module directory $env:USERPROFILE\Documents\WindowsPowerShell\Modules and put all the .ps1 files in that folder. Also create two text files <foldername>.psm1 and <foldername>.psd1 in the folder, so that you have a structure like this:
$env:USERPROFILE
`-Documents
`-WindowsPowerShell
`-Modules
`-MyModule
+-MyModule.psd1
+-MyModule.psm1
+-script1.ps1
+-script2.ps1
:
`-scriptN.ps1
Put the following line in the .psm1 file, so that it "imports" all .ps1 files:
Get-ChildItem -Path "$PSScriptRoot\*.ps1" | % { . $_.FullName }
and specify your metadata in the module manifest (the .psd1 file), e.g.:
#{
ModuleToProcess = 'MyModule.psm1'
ModuleVersion = '1.0'
GUID = '6bc2ac1e-2e88-4bc3-ac84-ecd16739b6aa'
Author = 'Matthew Lowe'
CompanyName = '...'
Copyright = '...'
Description = 'Description of your module.'
PowerShellVersion = '2.0'
FunctionsToExport = '*'
CmdletsToExport = ''
VariablesToExport = ''
AliasesToExport = ''
}
A GUID can be generated for instance via [guid]::NewGuid().
Here's a very simple way of doing it, without including your functions through dot sourcing mode, as it's done in the other answer:
Create a folder C:\MyModules.
Inside this folder, create an empty file named MyModules.PSM1.
Append to MyModules.PSM1 file, all functions (they don't need to be advanced) you want in the module.
YOU ARE DONE.
Now, you have a folder ( C:\MyModules ) that you must install in the target machine.
To install it in the target machine (per user), copy the folder C:\MyModule to the user's default module location (i.e. folder): $home\Documents\WindowsPowerShell\Modules.
Now, this user can type in any PowerShell session the first letter(s) of any function included in your module, that PowerShell's IntelliSense will recognize the function from your module (and uggest the completion substring).
If you don't like the name MyModule, you can change it, as long as you change the folder name as well as the PSM1 file name.
You can also opt to install your module for all users: help about_modules.

powershell: run code when importing module

I have developed a powershell module in C#, implemented a few commands.
How can I execute C# code in this module when it's imported by Powershell?
Create a module manifest with the ModuleToProcess (or RootModule in V3) field set to the PSM1 file and the NestedModules set to the DLL e.g.:
RootModule = 'Pscx.psm1'
NestedModules = 'Pscx.dll'
This is what we do in the PowerShell Community Extensions where we do the same thing - fire up a script first. You can see our PSD1 file here.
This is a very basic solution, simply replace code within the {} with your source. (my test below)
add-type 'public class c{public const string s="Hello World";}';[c]::s
enjoy
I'm also writing a binary cmdLet in .NET. I have found that if you create a class that inherits from at least DriveCmdletProvider, that class can implement InitializeDefaultDrives.
This method will get call when import-module is called on your DLL.
You could use this 'feature' to stand up some session (or module session) data.