Scripted cmdlets and compiled cmdlets in a single module? - powershell

I have written compiled modules (i.e. PS modules written in C#), where one specifies the DLL as the RootModule in the manifest, and I have written scripted modules (PS modules written in PowerShell), where one specifies the .psm1 file as the RootModule, but is it possible to include cmdlets of both type in a single module?
As a temporary measure (while I am developing some new code) I specified my DLL as the RootModule and listed my scripted cmdlets in ScriptsToProcess. That makes all the cmdlets accessible, but not properly, in that Get-Command -module MyModule only lists those cmdlets coming from the RootModule as one might expect.

Sure, we do this on the PowerShell Community Extensions like so:
RootModule = 'Pscx.psm1'
NestedModules = 'Pscx.dll'

Related

How does Powershell Auto-Load Modules?

We learn that Powershell introduced Module Auto-Loading in 3.0 :
... PowerShell imports modules automatically the first time that you
run any command in an installed module. You can now use the
commands in a module without any set-up or profile configuration, ...
And this is done via PSModulePath.
What the docs fail to explain is how Powershell can detect which commands are in a module without first loading the module.
That is, when I use Import-Module, I "know" that Powershell will execute (??!) the powershell code in my .psm1 file, exporting all functions, ... or whatever I spec with Export-Modulemember.
However, the Auto-Load feature has to know before hand that a certain command is available via a certain module without actually loading the module.
Since we had some misbehaving third party modules in PsModulePath and since we have a very few modules that we wrote ourselves that we like to anchor in PSModulePath, I would very much like to understand how the files in PSModulePath are processed.
This is a partial answer.
This is done/implemented via Get-Command and this seems to be enabled to "parse" module files without actually executing the PoSh Code there. See below.
From the powershell docs:
Implicitly Importing a Module
... works on any module in a directory that is included in the value
of the PSModulePath environment variable ...
To support automatic importing of modules, the Get-Command cmdlet
gets all cmdlets and functions in all installed modules, even if the
module is not imported into the session. ...
And then:
Get-Command
The Get-Command cmdlet gets all commands that are installed on the
computer ...
Get-Command that uses the exact name of the command, without
wildcard characters, automatically imports the module that contains
the command so that you can use the command immediately. ...
Get-Command gets its data directly from the command code, unlike
...
The docs do not explain how this is implemented, but one could possibly look up how it's done in the source code. (I wasn't so far able to find it, despite browsing the sources for a while.)
Incidentally I find mentioned that Powershell 3 was the first version to expose the AST, so it stands to reason that the posh code does exactly that: Parse the Scripts and inspect their AST in some way to determine if the command is provided.

How do I list all the references of a given PowerShell module?

How to list all the modules that this given module depends upon? (Particularly, I need their names and versions)
To find the formally declared module dependencies - if any - of a given module via its module manifest (*.psd1):
Get-Module $someModule -ListAvailable | ForEach-Object {
$_.RequiredModules, $_.NestedModules.RequiredModules
}
Get-Module returns System.Management.Automation.PSModuleInfo instance(s) describing the specified module(s).
-ListAvailable makes sure that modules that aren't presently imported but are discoverable via auto-loading, are included in the lookup.
$_.RequiredModules lists the direct module dependencies, and $_.NestedModules.RequiredModules those of any nested modules (which is presumably rare).Tip of the hat to Ash.
Note that whether the information returned includes version information depends on whether a version is explicitly specified in the target module's manifest's RequiredModules entry.
Caveat: The above will not discover de facto dependencies that aren't also formally declared, based on the module's runtime behavior. Discovering such dependencies would be both nontrivial and brittle.
That said, if interactive discovery is an option, use the following as the first command from a pristine session started with -NoProfile, which may at least provide clues as to what dependent modules are de facto being imported at import time:
Import-Module -Verbose -Force $someModule
However, there could still be additional dependent modules that the module's commands import on demand, when they're first called.
Set-PSDebug with its -Trace parameter offers general tracing of code execution.

Can I run powershell commands without installing module?

I would like to run a script that requires to use commands from the GroupPolicy module. However, this module is not guaranteed to be installed on every machine and some machines might be stand alone. Can I have have the module copied over with my scripts and run commands from it, without installing the module?
Can I have have the module copied over with my scripts and run commands from it, without installing the module?
Yes. Installing a module in PowerShell simply means placing it one of the directories listed in the $env:PSModulePath environment variable, which allows PowerShell to discover it and import (load) it on demand, as part of the module auto-loading feature
However, you're free to copy modules elsewhere, as long as the code that relies on them knows their path and loads them with an explicit Import-Module call.
For instance, if you bundle your script with a GroupPolicy subdirectory containing that module, the script could import it as follows:
# $PSScriptRoot contains the calling script's directory.
Import-Module $PSScriptRoot\GroupPolicy

How do I import a new PowerShell cmdlet?

I've written a PowerShell cmdlet in C#.
Where do I copy the library at this point?
And how do I import it into PowerShell so that I can use it?
There are two ways to load your new cmdlet.
Import Cmdlets Using Modules. Here you either put your cmdlet DLL into a system-recognized path that will allow you to load a module with a simple name (e.g. Import-Module MyModule), or you can put it in an arbitrary directory for which you need to specify a complete path (e.g. Import-Module C:\code\MyModule.dll). If you have only a single DLL and no dependencies, you can actually give the DLL as shown. Typically, though, you will also want to create a manifest using New-ModuleManifest (creating, e.g., a MyModule.psd1 file) then pass that psd1 file rather than the dll to Import-Module.
Create a Windows PowerShell Snap-in. This requires writing one additional C# class, quite small, that provides the glue necessary to treat your cmdlet as a snap-in. Then you have to register the snap-in with the installutil program and finally load the snapin with Add-SnapIn. (See also How to Register Snap-ins...)
Curiously, almost all articles that talk about writing cmdlets suggest the snap-in approach, but this is simply because that technique has been available since PowerShell version 1, while modules did not come along until version 2. Everything I have read, though, suggests essentially that the snap-in approach is deprecated to the simpler--and more flexible--module approach.

What are .ps1 files for in PowerShell modules?

I'm currently learning about PowerShell modules. If you're using a .psd1 manifest file, you have the option to use .ps1 script files as well as .psm1 manfiest files. Why do you need both?
I created a module with both, with the .psm1 set as RootModule and the .ps1 set in ScriptsToProcess and I've noted some differences, but I'm not sure what they add up to.
If I add Write-Output statements to both, on import the output is displayed for .ps1, but suppressed for the .psm1. Write-Warnings are displayed for .psm1.
If I run the Get-Command prefix for the module prefix, functions from the .psm1 are listed with the module name whereas functions from the .ps1 file are listed with a blank module name.
The section of your manifest in which you place the references to the Ps1 files determines how they are executed.
In your case:
The ScriptsToProcess will execute the listed PowerShell scripts in the caller's environment prior to importing the module. This makes me think of them as prep scripts.
This is because files listed here are not meant to contain functions; it's meant to be a script. If you want additional functions accessible by your module you have a few options:
List them in NestedModules
Include them in your module
Try listing them in the functions to the export section of the manifest. (I have not tried this method, but it's my understanding that it will work the way you want no matter where the function is located.)