Difference between using module, Import-Module, and #requires -Modules - powershell

Is there any detailed reference for how these 3 different methods for importing PowerShell modules work? I'm currently seeing different behavior with using module vs Import-Module in a script.
It seems importing dependencies works differently. Using Import-Module in order of dependencies can resolve the issue, but with using module it doesn't appear to be able to resolve dependencies.
Is this script defendant on how the import statements are created or is there a documented difference in how these different commands work?

I didn't find any guidelines either, but I made the following comparison to make some sense of what is what.
Import-Module
Well, it's a cmdlet. That means it
Accepts pipelines: 'PSReadLine','PSColor' | Import-Module
Accepts splatting: $params = #{Name = 'PSReadLine'; OutBuffer = 1} ; Import-Module #params
Supports flags and parameters: Import-Module -PassThru PSReadLine
Can be called almost everywhere: function Load {Import-Module PSReadLine}
Therefore, it is well suited for ad-hoc module loading and dynamic reloading.
using module
using is a keyword, so it's not something you can pass around like Import-Module. It doesn't take parameters the same way as cmdlets do, and it does not work with pipelines. In short, it's a primitive. Yet another limitation is that it has to be placed on top of the script, before all other statements.
A case when you need to use using module is when you want to load classes and enums. Neither Import-Module nor #Requires will add classes defined in a module into your scope. Generally speaking, it's designed for casual module loading.
#Requires -Modules
This is used to assert that certain modules are loaded (amongst others). In contrast to the rest, this command fails if the module cannot be loaded with Import-Module. Another difference is that it works only in scripts -- it does nothing in shell.

Related

In Powershell, is there a way to ensure the #Requires -Modules always loads the latest changes?

I have a Powershell (version 5.1) module that I just added a new function to, but it will not appear in the calling Powershell script.
I created a module, let's say myModule.psm1, and manifest myModule.psd1.
The manifest has the following settings:
FunctionsToExport = '*'
CmdletsToExport = '*'
VariablesToExport = '*'
AliasesToExport = '*'
myModule.psm1 is in the myModule directory of a path I have in my PSModulePath.
I have also explicitly added the fully qualified path of myModule to the PSModulePath.
In my calling Powershell script, I have #Requires -Modules myModule at the top of the file.
If I add a function to myModule, the calling script does not seem to load the changed module.
The only way to load the change, it seems, is to use Import-Module myModule -Force. It was my understanding that #Requires should take care of this.
#Requires -Modules when supplied with only a name, only checks to see that a module is loaded. It will load it if it's not already loaded, but an old version being loaded still satisfies it.
Alternatively, you can supply a hashtable that provides the name and either a minimum or exact version.
By doing this, you could load an updated version of your module as long as you did in fact update the version with your change (which you should be).
#Requires -Modules #{ ModuleName = 'myModule' ; RequiredVersion = '1.2.3' }
Of course that's not very useful when you're testing something out since you'll have to keep changing the version, in 2 places no less.
When I'm developing modules I usually have a debug script that force loads the module and I run it any time I make a change. If your test script is really just for testing, I recommend you use Import-Module -Force instead of #Requires.

Import-Module not working when called after a param() statement

I've a script which uses a parameter to pass details to it, and which needs to import the WebAdministration module.
The start of the script is :
param(
[parameter(position=0)]
[string]$iisAppName
)
Import-Module -name WebAdministration
however when I run the script I get errors from those cmdlets which use the module saying they're not found, since the module obviously hasn't been loaded.
If I put the Import-Module statement before the param() then the parameter isn't loaded. If I don't have the param() statement at all it works fine.
This script is for removing a website, but the companion creation script (which doesn't use param) works fine. In fact if I run that one it works, and if I then run this one (where the module is still loaded from the first) it works fine (annoyingly... since I didn't spot the issue in testing!), so I know I'm calling those cmdlets correctly.
Is there an alternate way I need to call one or both of these to allow both of them to work in my script?
I think this is to do with session states but would need more information to be sure.
https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/import-module#-global
By default, the commands in a module, including commands from nested modules, are imported into the caller's session state.
When you import a module from the global session state, it's available to the console and all modules. When the module is imported from another module, it will only be available to the module(s) that imported it. I think when you include Params it treats it differently, perhaps running it in a script state session instead of the global state session.
Try using Import-Module -Name WebAdministration -Global which, regardless of where it is called, should import it into the global state session and make it available to everything.

Binary Powershell Cmdlet not exported from module

I created a Powershell module containing one Cmdlet implemented by C# and some advanced functions provided by a psm1 file. When loading the module, only the functions implemented in advanced functions are exported.
I registered the assembly to load as part of my module and exported the functions:
RequiredAssemblies = #("lib\provider.dll","lib\myCmdlet.dll")
FunctionsToExport = #('New-assemblyFunction','New-advancedFunction')
Also I tried to mix the above functionstoexport with the cmdlettoexport for the assembly provided Cmdlet. All kind of combinations did not show any success:
CmdletsToExport = #('New-assemblyFunction')
If I start the import-module in verbose mode, I can see that the assemblies are being loaded but only functions implemented in advanced functions are being exported. The New-assemblyFunction does not appear anywhere in the verbose report.
I can load the DLL manually (import-module) and the cmdlet is available.
Any clue what's wrong here or how to further analyse? I deblock-file'd all of them.
That's it:
Value of RequiredAssemblieskey not considered as PowerShell modules. You need to use RootModule (ModuleToProcess) or NestedModules key. – PetSerAl

How do I force powershell to reload a custom module?

I have created a module 'ActiveDirectory.psm1' which contains a class in powershellv5. I am importing that module in another file called 'test.ps1' and then calling a method from the class.
test.ps1 contains the following:
using module '\\ser01\Shared\Scripts\Windows Powershell\modules\ActiveDirectory\ActiveDirectory.psm1'
Set-StrictMode -version Latest;
$AD = [ActiveDirectory]::New('CS');
$AD.SyncGroupMembership($True);
It all works as expected BUT when I make a change to ActiveDirectory.psm1 & save the changes they aren't reflected immediately. i.e. if ActiveDirectory.psm1 contains:
write-verbose 'do something';
If I change that to
write-verbose 'now the script does something else';
the output remains 'do something'
I'm guessing it has stored the module in memory and doesn't reload it therefore missing the changes I have made. What command do I need to run to load the most recent saved version of the module?
As suggested by wOxxOm, you can try pass the -Force flag:
Import-Module ... -Force
Or if that does not work try to explicitly remove it and then reimport with:
Remove-Module
From what I've gathered. Import-Module does not import classes. You have to use the "using module " and it has to be in the first line of your script. On top of that problem, the classes appear to be "cached" in some esoteric way that precludes any uninstall-module or remove-module options. I've found I basically need to restart my powershell terminal to clear it.
If classes are not involved use import-module OR install-module. In both cases you can do a get-modules -all or get-installedmodule and then remove-module or uninstall-module. You want to make sure you look for all versions and pipe that to remove/uninstall to ensure you wipe everything out.
For anyone else coming across this issue, see https://github.com/PowerShell/PowerShell/issues/2505
It seems that there is a known long-standing bug regarding importing of modules that are anything above rudimentary level in complexity (for example, I have a module with a single class and class method that fails to update).

Is there ever a reason to explicitly Import-Module?

I was just reading the PowerShell Modules guide page and I noticed a line on the Import-Module section:
The following actions trigger automatic importing of a module, also
known as "module auto-loading."
Using a cmdlet in a command. For
example, typing Get-ExecutionPolicy imports the
Microsoft.PowerShell.Security module that contains the
Get-ExecutionPolicy cmdlet.
So given that, why should we ever care about using Import-Module? Isn't it always taken care for us automatically? In what case would I need to explicitly write out Import-Module?
You have to use Import-Module in the following cases :
The module file is not in a path included in $PSModule Path
You have different modules with the same name but in different paths
The module is already loaded and you want to reload it after making modifications to it. (with -Force)
To import only specific cmdlets, functions or variables from that module (with the -Cmdlet, -Function, and -Variable parameters respectively)
To prevent loading cmdlets or functions from the module that would overwrite the commands with the same name and are already loaded in the current session ( with -NoClobber )
To add a prefix to the nouns of the cmdlets in this module ( with -Prefix)
To import a module from a remote computer (with the -PSSession parameter )
The list is not totally exhaustive but these are the main use cases for the Import-Module cmdlet.
I know there is already an accepted answer, but I wanted to add my two cents.
To explicitly document the dependency of a script upon a module
If $PSModuleAutoloadingPreference is set to "none", modules need to be explicitly loaded. You don't know if users have turned this off or not.