Commandlets from a custom Module not being recognised - powershell

I have a custom Powershell module I have been working on for the past few days, I am now trying to integrate it into powershell as a module that always gets auto loaded.
My module is placed under the C:\Users\...\Documents\PowerShell\Modules , which is one of the places Powershell expects modules from.
This is the structure for the module:
C:\Users\...\Documents\PowerShell\Modules\MetaData-System\MetaData-System.psm1
C:\Users\...\Documents\PowerShell\Modules\MetaData-System\MetaData-System.psd1
The MetaData-System.psd1 files contents are:
#{
ModuleVersion = '0.0.1'
GUID = '9e976eac-1010-4e0b-95e4-76c8bfc1ece1'
Author = '...'
CompanyName = 'Unknown'
Copyright = '(c) .... All rights reserved.'
FunctionsToExport = #( 'Set-Metadata', 'Clear-Metadata')
CmdletsToExport = #()
VariablesToExport = '*'
AliasesToExport = #()
PrivateData = #{
    PSData = #{
    }
}
}
The MetaData-System.psm1 files contents consists of two functions called Set-MetaData and Clear-MetaData.
In Terminal running Get-Module -ListAvailable lists:
Directory: C:\Users\...\Documents\PowerShell\Modules
ModuleType Version PreRelease Name PSEdition ExportedCommands
---------- ------- ---------- ---- --------- ----------------
Manifest 0.0.1 MetaData-System ... {Set-Metadata, Clear-Metadata}
But when I try to use one of the functions from the module by typing "Set-Met" no autocomplete occurs, neither do any parameters get suggested. If I write it all down such as:
Set-Metadata -path "C:\Users\...\Documents\test.txt" -Flags "Hello World
I get this error:
Set-Metadata: The term 'Set-Metadata' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
I have searched everywhere to see what I am doing wrong and
out of ideas now.
I have taken the liberty to cross post this question on other forums as well.
Any help would be greatly appreciated!
EDIT: As suggested by #Abraham Zinala, I tried Get-Module:
ModuleType Version PreRelease Name ExportedCommands
---------- ------- ---------- ---- ----------------
Manifest 0.0.1 MetaData-System
Manifest 7.0.0.0 Microsoft.PowerShell.Management {Add-Content, Clear-Content, Clear-Item, Clear-ItemProperty…}
Manifest 7.0.0.0 Microsoft.PowerShell.Utility {Add-Member, Add-Type, Clear-Variable, Compare-Object…}
Script 2.1.0 PSReadLine {Get-PSReadLineKeyHandler, Get-PSReadLineOption, Remove-PSReadLineKeyHandler, Set-PSReadLineKeyHandler…}
No commands are present for the MetaData-System module.

Your module manifest is missing the following entry:
RootModule = 'MetaData-System.psm1'
This is necessary for the implementing script module file (*.psm1) to be discovered.
In the absence of it, it is only the manifest file (.psd1) that is discovered, and the implementation of the exported commands is effectively missing.
This is evidenced by your sample Get-Module output listing the ModuleType as Manifest rather than Script.

Related

Get-Module -ListAvailable: Why or how are modules printed in sections divided by Directory?

When I do "Get-Module -ListAvailable", powershell will print 169 modules. For example:
Directory: C:\Program Files (x86)\Microsoft SQL Server\150\Tools\PowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 15.0 SQLPS {Backup-SqlDatabase, Save-SqlMigrationReport, Invoke-PolicyEvaluation, Resto...
Directory: C:\Users\user\Documents\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 3.0.1 DotNetVersionLister Get-STDotNetVersion
Script 1.4.7 PackageManagement {Find-Package, Get-Package, Get-PackageProvider, Get-PackageSource...}
Script 2.2.5 PowerShellGet {Find-Command, Find-DSCResource, Find-Module, Find-RoleCapability...}
Script 2.2.16 VSSetup {Get-VSSetupInstance, Select-VSSetupInstance}
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.3.1 Configuration {Import-Configuration, Export-Configuration, Get-StoragePath, Add-MetadataCo...
When I capture this in an array: "$m = Get-Module -ListAvailable"
It seems like just a simple array, yet it also prints in these sections.
How is this done?
There doesn't even seem to be a "Directory" property on the PSModuleInfo objects.
Powershell have its own formatting engine. Whenever you use that cmdlet, you output a list of System.Management.Automation.PSModuleInfo objects.
Before printing the object "raw", Powershell check if there is a predefined formatting available for the type and if so, apply it. What you see is the result of that transformation.
Up to PS 5.1, this was done through formatting configuration file, defined as *.ps1xml files. From PS6.0 and newer, predefined formats are now included directly in the source code but you can still create additional format files as needed.
You can view the loaded format type using the Get-FormatData cmdlet.
If you are interested in the Get-Module cmdlet specifically, check out (Get-FormatData -TypeName System.Management.Automation.PSModuleInfo).FormatViewDefinition. You will see something like this:
Name Control
---- -------
Module System.Management.Automation.TableControl
Module System.Management.Automation.WideControl
Module System.Management.Automation.ListControl
This mean that any objects of that type have special instructions concerning the way that it should output its object. In that case, it includes grouping by path and displaying the specific columns (ModuleType, Version, Name, ExportedCommands). Powershell did not chose to display those properties by itself, it got its instructions from the predefined type on what to display.
In the case of PSModuleInfo type, we can see that there is 3 custom views for the type. One for table view (which is the default shown), one for list and wide, which instruct what to show when you use Format-List & Format-Wide.
From MS doc
The display format for the objects that are returned by commands
(cmdlets, functions, and scripts) are defined by using formatting
files (format.ps1xml files). Several of these files are provided by
PowerShell to define the display format for those objects returned by
PowerShell-provided commands, such as the System.Diagnostics.Process
object returned by the Get-Process cmdlet. However, you can also
create your own custom formatting files to overwrite the default
display formats or you can write a custom formatting file to define
the display of objects returned by your own commands.
PowerShell uses the data in these formatting files to determine what
is displayed and how the displayed data is formatted. The displayed
data can include the properties of an object or the value of a script.
You can create your own files (*.ps1xml) and include them in your modules or load them in your sessions to modify the way the output is displayed.
You can also add formatting to the output of your functions by defining a default display set (aka what properties should be displayed).
For instance, take this simple function:
Function Get-EmployeesInfos() {
$Output = #(
[PSCustomObject]#{
FirstName = 'RObert'
LastName = 'Samson'
SocialSecurityNumber = '123 344 555'
Age = '32'
Salary = '100000'
},
[PSCustomObject]#{
FirstName = 'Pablo'
LastName = 'Morrison'
SocialSecurityNumber = '123 345 555'
Age = '22'
Salary = '10000'
}
)
# Default display set
$defaultDisplaySet = 'FirstName', 'LastName'
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet', [string[]]$defaultDisplaySet)
$Output | Add-Member MemberSet PSStandardMembers ([System.Management.Automation.PSMemberInfo[]]#($defaultDisplayPropertySet)) -Force
return $Output
return $Output
}
Without any default display set, you would get your standard output with all the properties listed.
With the default display set added, here is the new output.
Both outputs do contains the same information, but the console have a special formatting applied to it to show only what is most important, useful, etc...
You can use formatting views to:
Colorize output
Create trees
Change output based on condition
Add virtual properties
define column width
define displayed column title
etc...
References:
Formatting File Overview
4Sysops - Formatting object output in Powershell with Format.ps1xml files
Update-FormatData
The reason that Get-Module is showing the result in groups is because that is the default format for Module objects whenever PowerShell shows them to the user. It's not a specific feature of the Get-Module cmdlet as such.
This is convenient facility in general because you can then use cmdlets such as Sort-Object and Where-Object to sort and filter the results and then have the results shown in groups afterwards.
In the following example, the results are filtered and then shown in groups. The significance is that neither Get-Module nor Where-Object is aware that the end result will be shown in groups; they just deal with objects.
PS> Get-Module -ListAvailable | Where-Object Name -Match Read
Directory: C:\program files\powershell\7\Modules
ModuleType Version PreRelease Name
---------- ------- ---------- ----
Script 2.1.0 PSReadLine ...
Binary 2.0.3 ThreadJob ...
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version PreRelease Name
---------- ------- ---------- ----
Script 2.0.0 beta2 PSReadline ...
You can see what PowerShell is doing in this specific case by looking at the default formatting code for modules on GitHub. The relevant part is the GroupByScriptBlock call (with minor reformatting to reduce line length):
yield return new FormatViewDefinition("Module",
TableControl.Create()
.GroupByScriptBlock(#"
Split-Path -Parent $_.Path | ForEach-Object {
if([Version]::TryParse((Split-Path $_ -Leaf), [ref]$null)) {
Split-Path -Parent $_
} else {
$_
}
} | Split-Path -Parent", customControl: sharedControls[0])
.AddHeader(Alignment.Left, width: 10)
...
When PowerShell shows an array of module objects to the user using the default format, it will run the script block in GroupByScriptBlock on each object first to work out the grouping.

PowerShell custom module manifest does not expose functions declared

I've created a PowerShell module. The module exposes 3 functions. When I install it without a manifest directly the output will be:
> Import-Module AzureDD
> Get-Module | Where { $_.Name -eq 'AzureDD' }
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0 AzureDD {New-AzureDDAppPermission, New-AzureDDAppRegistration, Sync-AzureDDStorageContainer}
This works because my last line in the psm file is:
Export-ModuleMember -Function New-AzureDDAppRegistration, New-AzureDDAppPermission, Sync-AzureDDStorageContainer
Now I wanted to add versionning and more meta data and went on with
> New-ModuleManifest -Path .\AzureDD.psd1 -ModuleVersion "2.0"
which creates a new file AzuerDD.psd1. In here I edited a lot of stuff. Besides other changes I also defined the exported functions as follows:
FunctionsToExport = #('New-AzureDDAppPermission', 'New-AzureDDAppRegistration', 'Sync-AzStorageContainer')
I can test this successfully:
> Test-ModuleManifest .\AzureDD.psd1
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 2.0 AzureDD {New-AzureDDAppPermission, New-AzureDDAppRegistration, Sync-AzStorageContainer}
But when I actually import this it will not show any exported command:
> Import-Module .\AzureDD.psd1
> Get-Module | Where { $_.Name -eq 'AzureDD' }
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 2.0 AzureDD
See how it changed to Manifest compared to my very first snippet! I've ensured that I did Remove-Module AzureDD -Force all the time before I re-imported it.
FunctionsToExport is like a sieve - it just allows nested modules to export their functions via the manifest, but they still have to be defined somewhere.
Make sure the script module (the .psm1 file) is specified as the RootModule (or at least a NestedModule) in the manifest:
#{
# Script module or binary module file associated with this manifest.
RootModule = 'AzureDD.psm1'
# ...
}

remote IBM MQ monitoring from power-shell commands / scripts

I am trying to get queue depth for remote IBM MQ using PowerShell script/commands. Seems it is not working correctly, please help.
{
$myremoteconns = New-WMQQmgrConnDef -Name T.test.TEST.QM1 -Hostname abcd_testhost01 -Port 1111 -Channel T.test.MQMQ.TESTCHN
$qm = Get-WMQQueueManager -Connections $myremoteconns | where {$_.Name -like 'T.test.TEST.QM1'}
Error New-WMQQmgrConnDef the term 'New-WMQQmgrConnDef' is not recognized
Already installed WebSphere MQ - Windows PowerShell Library from below.
http://www-01.ibm.com/support/docview.wss?uid=swg24017698
Thanks
This error means that PowerShell cannot find this cmdlet. You need to check if the module you installed is properly found by PowerShell. Check this first:
Get-Module -ListAvailable
The result will be like:
PS C:\Users\username\PowerShell> get-module -ListAvailable
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 3.0.1 ImportExcel {Import-Html, ConvertFrom-ExcelSheet, PieChart, Import-UPS...}
If you don't find the module on the list you can import it manually using:
Import-Module -Name 'C:\path\to\module.psm1'
In PowerShell (starting at V3) modules should be imported automatically if they are inside on of the path specified in environment variable PSModulePath. You can check its value by using:
$env:PSModulePath
Once you know which folders are included in this variable you can either move the module there or just add another path using
$env:PSModulePath = $env:PSModulePath + ";c:\path\to\module"
This will work for current session. If you want to modify it for all session you can add the line below to PowerShell profile.
More info:
Importing a module
Modifying PSModulePath
PowerShell profiles

Import-Module works only when piped from Get-Module

I wrote a simple PowerShell module. I need to keep more versions of the module. All paths to versions are added to $env:PSModulePath. I'm facing strange problem when importing the module to my session.
This fails:
Import-Module Contoso.PowerShell -RequiredVersion "0.0.2"
Import-Module : The specified module 'Contoso.PowerShell' with version '0.0.2'
was not loaded because no valid module file was found in any module directory.
At line:1 char:1
+ Import-Module Contoso.PowerShell -RequiredVersion "0.0.2"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (Contoso.PowerShell:String) [Import-Module], FileNotFoundException
+ FullyQualifiedErrorId : Modules_ModuleWithVersionNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
And now the strange thing - the module with the "0.0.2" version exists. I can successfully list it (with Get-Module -ListAvailable). I can even import it and work with it, but the only way how to do it is this:
Get-Module Contoso.PowerShell -ListAvailable |
? { $_.Version -eq "0.0.2" } |
Import-Module
Everything works like a charm then. The question is: WHY? I'd like to be able to import the module with the first simple command.
EDIT:
Here is how I store the versions of the module:
Get-Module Contoso.PowerShell -ListAvailable
Directory: C:\Program Files\WindowsPowerShell\Modules\Contoso.PowerShell\Contoso.PowerShell.0.0.1
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0.1 Contoso.PowerShell
Directory: C:\Program Files\WindowsPowerShell\Modules\Contoso.PowerShell\Contoso.PowerShell.0.0.2
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0.2 Contoso.PowerShell
And sorry for confusion - I do NOT have paths to each version in the PSModulePath environment variable.
The reason Import-Module works is because it uses a different parameter set; one where it accepts one or more [PSModuleInfo] objects, which are what Get-Module returns.
Likely, it uses the work already done by Get-Module to determine which file to load.
The next question then is "why doesn't Import-Module find the version the same way Get-Module does?" and the answer to that is "I don't know."
While they should be consistent in any case, a possible cause for trouble is your directly structure. How are you storing multiple versions?
It looks to me like your module paths are incorrect.
Your structure should be:
Contoso.PowerShell\0.0.2
Contoso.PowerShell\0.0.3
etc.
The module files go directly in the version number folder, and it shouldn't additionally have the name inside it.
You can see this structure by using Install-Module to install one from a repository and taking a look at how it handles it.

Powershell not importing functions from module

I'm trying to setup a NuGet Feed here, and that worked ok. I installed a module from my feed via
Install-Module -Name MyCmdlets -Repository $RepoName -Scope CurrentUser -Force
Import-Module -Name MyCmdlets
However when I run Get-Module, I get no functions and it's a manifest?
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0 MyCmdlets
If I manually go to the installed location and import manually
Import-Module <my-path>\1.0\MyCmdlets.psm1
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 0.0 MyCmdlets {Create-Project, Get-AuditLogs, Get-..
My manifest file does have these lines so I don't understand why Import-Module isn't working correctly.
FunctionsToExport = '*'
CmdletsToExport = '*'
I guess you haven't set the root module in your .psd1 like so
#
# Module manifest for module 'YourModule'
#
#{
# Script module or binary module file associated with this manifest
RootModule = 'YourModule.psm1'
# Version number of this module.
ModuleVersion = '1.0.0'
...
This is necessary so that when you import your manifest module it also loads the script module
For anyone coming across this looking for why their module wont import check that RootModule = 'YourModule.psm1' isn't commented out.
By default when creating a new manifest using New-ModuleManifest it throws a hash in front of this line..
ugh I feel so stupid.