Is there ever a reason to explicitly Import-Module? - powershell

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.

Related

PowerShell: Controlling verbose output in a module

So I have this module I will share with a group of people . I want all the output for all the cmdlets IN the module to be have like their where passed -Verbose. Without requiring the user to actual pass -verbose. I do not want -verbose output of any other modules cmdlets I call into.
So I tried $Global:VerbosePreference = "Continue" , and then explicitly -verbose:$false for cmdlets I all into. But it seems the global version overrides the specific version , and I get way to much verbose output.
Is this possible ?
My module is a multi file module , it has over 10 ps1 in it.
Add $VerbosePreference = 'Continue' to the top-level scope of the *.psm1 script-module file that is referenced in the RootModule entry of your module's manifest file (*.psd1).
This scopes the preference to the commands in your module, without affecting code outside of it.

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

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.

Write to Profile File After Installing PowerShell Module with PowerShellGet

I have a custom PowerShell module with two cmdlets. I have it successfully, but manually, deployed on my machine. However, I deployed it by placing the binary file and module manifest in a location, and then registering the module. I also had to manually write an Import-Module command into my 'all users' profile.
Now I am sure I can deploy this module with Publish-Module, but how do I get the Install-Module to write the Import-Module statement to the profile file?
As of PowerShell 3.0, a module is automatically imported when a command from the module is invoked. This was a brilliant on Microsoft's part; however, it did require that modules are located in a location where PowerShell looks for modules by default. Makes sense. You can see those locations by running the following command:
$env:PSModulePath -split ';'
Is there a reason you'd rather not use one of the paths stored in the above environmental variable? That said, I'd keep your code out of the "C:\Windows\System32..." path. The other options are better: "C:\Program Files\PowerShell\Modules" (AllUsers) and "C:\Users\tommymaynard\Documents\PowerShell\Modules" (CurrentUser). Depending on your PowerShell version/OS, those path could be different. You won't need to write an Import-Module command into a $PROFILE script if you get the module into a preferred location. Maybe you already know this, but maybe not.
You're not going to get Install-Module to write to any of the $PROFILE scripts.
$PROFILE | Select-Object -Property *
Well, not by default anyway. You could write your own Install-Module function, that runs PowerShellGet's Install-Module function, and includes writing to various $PROFILE scripts. The problem is that you'll need to include logic so you don't blow away the contents of someone's $PROFILE script if it's not empty, and only append to it.
Seriously though, this is turning into a lot of work, when you could drop the module into a location where PowerShell can find it on its own.
Edit: It just occurred to me, you can add a value/path to the $env:PSModulePath environmental variable. It's a single string with semi-colon delimiters:
$env:PSModulePath.GetType().Name
Therefore, it'd look like this:
$env:PSModulePath += ';C:\Another\Path'
That's great and all, but again how might you stage this, right? It takes you back to the write-to-all-the-$PROFILE-scripts problem,... although you may be able to update the variable via Group Policy Preferences. Again, probably better to just relocate your module.

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 a way to add Alias to Powershell Cmdlet programmatically?

I am writing custom Powershell cmdlets for my application and I need to provide Aliases to some cmdlets. So lets say I have cmdlet Get-DirectoryListing and I want to add Alias (say 'gdl') to this cmdlet. How can I do this?
The AliasAttribute doesn't work here, since it works only with Properties, Indexers or Field declarations. Also I know we can use Set-Alias command, but don't know where to put it.
Is it possible to programmatically add multiple aliases to a cmdlet?
You need to create a psm1 file (powershell module) where you specific your dll with yours cmdlets to load and add aliases in this way:
In your module folder ( Get-ModuleFolder give a list of all if you have more that the default one, in my example I use the first one) create a folder with same name of your .dll
and a SameNameOfYourDll.psm1 with this content:
Import-module "$((Get-ModulePath)[0])mycustomcmdlet\mycustomcmdlet.dll"
set-alias gdl Get-DirectoryListing -scope Global
For more raffinate module building look also at module manifest
Module manifest is the preffered way for .dll with custom cdmlets.