I am really at beginner level for working with commandlets and powershell stuff. I am invoking commandlets from C# using PowerShell API. I saw strange behaviours. While on different threads on stackoverfow, people explicitly importing modules using Import-Command or PSImportModule method, I can see if there is an assembly at $env:PSModulePath available already, it is automatically loaded. Does this behaviour is by default or due to criteria configurations which i m overlooking. I am running unit test in ms test environment.
I am using following code.
System.Management.Automation.PowerShell _powerShellInstance
_powerShellInstance.AddCommand("Connect-AzureRmAccount", false);
var output = _powerShellInstance.Invoke();
// Invoking my commandlets
_powerShellInstance.AddCommand("Get-LinkParameter", false);
The above command automatically loads the assembly from C:\Windows\system32\WindowsPowerShellModules\v1.0\Modules\
. I have not created any runspace and no configuration sets. Just above automatically loading things. I need to confirm how exactly is the powershell and run space behaves. Because I need to clear how i need to install my commandlets on production machine then. How unit tests on production machine will access my commandlets to perfectly get loaded.
While it is good practice to explicitly load the modules you want using Import-Module
, since Powershell 3.0 if a module is available at one of the locations returned by $env:PSModulePath
, it will automatically get loaded by default if one of its cmdlets are invoked. Below is a breakdown of the different paths:
$modulePath = "${env:UserProfile}\Documents\WindowsPowerShell\Modules"
Modules installed here are only available to the current user's Powershell session, and by default modules installed using Install-Module
are saved here.
$modulePath = "${env:ProgramFiles}\WindowsPowerShell\Modules"
Modules installed here are available to any users' Powershell session.
$modulePath = "${env:SystemRoot}\system32\WindowsPowerShell\v1.0\Modules"
Modules installed here are available system wide to any Powershell session, but should be kept clean for Windows to manage. Typically, you do not want to install your own modules here.
You can add additional paths to $env:PSModulePath
similarly to how you would modify the $env:PATH
variable for resolving executable paths. It is simply a semicolon ;
delimited string of directories where modules reside, and if a module is available at any path in $env:PSModulePath
, Powershell will know where to find it. And in fact, you may see that other installed tools may have added their own paths to $env:PSModulePath
. A few of examples of programs/toolsets which do this are Microsoft SQL Studio
, Microsoft System Center - Operations Manager
, and the Chef Development Kit
.
You can temporarily edit $env:PSModulePath
to contain a directory with a module you want to load. For example, if you wanted to import a module named TestModule
from some arbitrary path:
$env:PSModulePath += ';C:\Path\To\Temporary\ModuleDirectory'
Import-Module TestModule
where TestModule
exists as a sub-folder of C:\Path\To\Temporary\ModuleDirectory
You do not need to back out the module path change when you are ready to end your Powershell session as the change above is temporary. Consequently, you would need to modify $env:PSModulePath
in every session, so if TestModule
was something you wanted to have available at all times for use, you can either copy it to one of the other directories in $env:PSModulePath
or permanently add C:\Path\To\Temporary\ModuleDirectory
to the PSModulePath
environment variable.
Alternatively, you can also directly import a .psm1
file or the directory housing a module with a .psd1
file within it by providing the path to Import-Module
. This is useful for importing one-off modules that are not present somewhere in $env:PSModulePath
.
# Must have a valid `.psd1` manifest in the ModuleName folder
Import-Module C:\SomeFolder\ModuleName
# You can directly import `.psm1` files, but don't do this if a module manifest is present
Import-Module C:\SomeFolder\OtherModules\SomeModule.psm1
You can also add UNC (network) paths to $env:PSModulePath
. However, I believe any remote module scripts will still be subject to the Powershell ExecutionPolicy
set on the system.
By default, Install-Module
installs a module to the User Modules directory, but you can control this a bit with the -Scope
parameter. For example, the following commands will change the location a module is installed into:
# Install to the current user location (default behavior if scope unspecified)
Install-Module -Scope CurrentUser $moduleName
# Install to the all users location (requires elevated permissions)
Install-Module -Scope AllUsers $moduleName
Unfortunately, those are the only two locations PowerShell will assist you installing your modules to. The System modules are critical to the operation of PowerShell and should not be modified by end users, and additional paths added to $env:PSModulePath
are likely managed by software outside of PowerShell, typically by MSIs or other installers.
Additionally, if you write software that ships with one or more PowerShell modules it's good practice to have your installer add a new directory to the system's %PSModulePath%
and drop the module there, rather than installing to the standard AllUsers or CurrentUser path, as these are really meant for the end user to manage at their whim. Have your software update process update the module in this case. This has the benefit of preventing accidental modification or removal of the module to an incompatible version.