How can I ensure that a 3rd party module is loaded in my custom script module? - powershell

I have a custom script module and one of the functions depends on the AWSPowerShell module. I attempted to put Import-Module AWSPowerShell inside that function, but when this runs on the server it fails and says no valid module is found. If I try Import-Module AWSPowerShell from the command line on the same server running as the same user it works fine. There seems to be something wrong with calling Import-Module from inside another module.
I saw that NestedModules can be used to specify dependencies so I added NestedModules #('AWSPowerShell') to the module manifest and removed the Import-Module AWSPowerShell from the function that needs it.
Now the error about AWSPowerShell happens at the point where I import my own custom module from the calling script.
The exact error is: Import-Module : The module to process 'AWSPowerShell', listed in field 'NestedModules' of module manifest 'C:\Program Files\WindowsPowerShell\Modules\...MyModule.psd1' was not processed because no valid module was found in any module directory.
I did notice that the AWSPowerShell module is installed in a different folder path than my custom module. My module is in C:\Program Files\WindowsPowerShell\Modules and AWSPowerShell is in C:\Program Files (x86)\AWS Tools\PowerShell.
How can I set this up so that AWSPowerShell can be loaded for use inside my module?
Update
I made a copy of the AWSPowerShell module folder under C:\Program Files\WindowsPowerShell\Modules and now the module imports successfully using the NestedModules method. Everything I saw about NestedModules appeared to be for combining modules that were being developed for a bigger solution. I'm not sure about using this technique for importing a 3rd party module like this. I may be misusing this feature.

The NestedModule module-manifest (.psd1) entry is for modules that are bundled with your module and available in your module's code only.
RequiredModules is for importing independently pre-installed modules, which are imported into the global scope when your module is imported, which means that their commands are also available to the caller.
Note: This answer shows how to specify RequiredModules modules to be installed on demand by declaring them as external dependencies, but the limitation appears to be that both the module and its dependency must come from the same repository and must be installed with Install-Module.

Related

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

Unable to import installed PowerShell module

I've created an Azure Artifacts feed to host my PowerShell modules and I'm able to install them locally to a directory listed in the PSModulePath list.
My issue is that when I come to use the module it is not available in active memory and I get an error when trying to import the module by name:
Import-Module : The specified module 'module-name' was not loaded because no valid module file was found in any module directory.
If I use Get-Module, the module is not listed.
If I use Get-InstalledModule, it appears in the list.
If I import the module using the full path then I am able to use the module, but I don't want to specify the full path as I will eventually be using the module in a DevOps release pipeline.
In this scenario, how can I import the module into my script?
Using Test-ModuleManifest identified that the ModuleVersion didn't match the version number in the module file path. Updating the ModuleVersion resolved the issue.

Get-Module -ListAvailable doesn't display my module

I've created a module manifest .psd1 file and I've used Test-ModuleManifest on it which returns nicely and shows the two CmdLets in the binary C# .dll.
The manifest file is in a folder under c:\Program Files\WindowsPowerShell\Modules but when I run Get-Module -ListAvailable it is not showing.
If I run Get-Module it is not showing.
If I run one of the CmdLets then the module is automatically imported and shows when I next run Get-Module.
What am I misunderstanding?? Why doesn't it show as available?
Update
Just done this again with a script module, psm1 and psd1 on a different machine, and same problem. It doesn't show as available and yet posh-git which seems to be setup in a similar way, does.
Okay, the script module does now show up after I fixed a version inconsistency between the version in the manifest and the subfolder name I'd placed it in.
Came in handy:
Test-ModuleManifest

Create pure powershell Nuget module for PowerShellGet

What I want
I want to publish number of PowerShell scripts as Nuget package to be used on build systems.
I want to use PowerShellGet to do installation work for me and version management.
I don't want those scripts to be part of any Visual Studio solution, but as standalone scripts.
Usage scenario
On any system, with configured Nuget provider user executes:
Install-Module MyModule
From that moment all exports from that module permanently available for this user.
Also user can call that command again to update version of those scripts.
What I've done
You can find current state of package here: GitHub
I've added and configured Nuget provider to our local Nuget server
To do this call Get-PackageProvider -Name NuGet -ForceBootstrap and Set-PSRepository -Name My_Nuget_Repo -SourceLocation http://my-nuget/api -InstallationPolicy Trusted
Created proper module, which can be imported locally by Import-Module
Created and published Nuget package with that module
Problem
I can install that package by Install-Module cmdlet and I can see it later in Get-InstalledModule list.
But, no functions are available.
Also, no matter what, but Install-Module not calling any of scripts from my package:
Not calling ScriptsToProcess from MyModule.psd1
Not calling Install.ps1 from tools folder
Not calling Init.ps1 from tools folder
Cmdlets exported by module not available and module can't be imported by Import-Module
(Same package works properly when installed from Visual Studios Install-Package MyModule, scripts are called, PowerShell module is imported).
Investigation
Since PowerShellGet is based on OneGet it seems that problem is in Install-Package cmdlet (which is called inside Install-Module cmdlet).
When I'm executing Install-Package MyModule from Visual Studio Install.ps1 and Init.ps1 are called. But same command from pure PowerShell doing nothing.
After long reverse engineering I've found the root cause
Technical reason
Magical tag PSModule has to be added to <Tags> in nuspec file.
Real reason
You shouldn't create nuspec file and pack nuget package manually at all. Use Publish-Module cmdlet instead.
How to do it properly
I've updated powershellget-module GitHub with:
Example of minimal module which can be published
A way how to use local folder as Nuget feed
Publishing, installation and usage of that module
Reference script with no dependencies which does it all locally, so you can study it
Check it out.

PowerShell Module Manifest - automatically loading a Required Module

I am creating a module that depends on several other modules that I need loaded into the global environment. I tried creating a script and using ScriptsToProcess to import the modules, but it looks like the check for RequiredModules happens before ScriptstoProcess runs.
Is there a nice, clean way in a Module Manifest to both Require a module and load it automatically if its not already loaded? If the module could not be loaded, then the RequiredModule would throw an error.
In PowerShell 3 and up, RequiredModules are automatically loaded
It's also the only way to make sure people using PowerShellGet (i.e. the PowerShell Gallery) install your dependencies, if you're going to distribute the module.
It would still fail if the required modules are missing, but otherwise works exactly the way you'd wish it to.
In PowerShell 2, there's no way to automatically load RequiredModules
In either case, users can manually load the requirements by typing Import-Module RequiredModule, YourModule -- they won't get a second instance if it's already imported ...
You can also specify the module in NestedModules instead. Even in PowerShell 2, those get loaded "inside" your module, but don't seem to negatively affect resources when they're already loaded. However, as #JasonMArcher reminded me, in PowerShell 2, NestedModules get unloaded along with your module if your module gets unloaded (via Remove-Module), and this happens even if they were pre-loaded separately by the user, which can end up producing really weird bug reports since your users won't expect that.
The other option, which works in all versions of PowerShell, is to call Import-Module at the top of your module (in a psm1 script, after checking to make sure the module's not already loaded) with the -ErrorAction Stop set so that the import of your module will fail if the import of the dependent module fails.
if (!(Get-Module Dependency)) { ## Or check for the cmdlets you need
## Load it nested, and we'll automatically remove it during clean up
Import-Module Dependency -ErrorAction Stop
}
Actually, if you wanted to check for versions ...
if (!(Get-Module Dependency | Where { $_.Version -ge "2.5" )) {
## Load version 2.5 (or newer), or die
Import-Module Dependency -Version 2.5 -ErrorAction Stop
}
Just remember that this doesn't serve as documentation, so if you distribute the module, your users will not know about the dependencies.
PowerShell V3 works somewhat differently. Required modules are now loaded when you load a manifest with that key specified. Also, removing the module does not unload the required module, no matter how that module was loaded. Interestingly, -Force does not seem to show the autoloading of the required module(s).
Edit: If your module is not in one of the default locations, you will need to add the path to that module (or the parent folder under which the module resides) to the $env:PSModulePath variable.
Side note: since PSv3 there is support for the #Requires command in scripts. You can use this to (1) attempt to load a module dependency in your script and (2) terminate if the module is not/cannot be loaded.
#Requires -Modules ModuleName1,ModuleName2
This also needs the modules to be somewhere under $env:PSModulePath, which must be set before running the script.
about_Requires