Powershell modules are packages that contain one or more blocks of code such as scripts, cmdlets, manifests, and functions that have a related purpose - as well as aliases and variables.
Because of the way Powershell is constructed it is easier to use a function that is part of a module than it is to run a script - so I looked at the possibility of turning my Powershell firewall script into a single function module.
NB - this is all based on Powershell version 5.1 as that is the version installed on Windows 10 1809.
There are a number of Powershell commands that allow you to look at the modules that are installed as part of the Powershell installation.
The most basic one is
Get-Module
which shows the installed and active Powershell modules.
An extension of this command is
Get-Module -ListAvailable
which shows the list of all the installed modules that are available for use but not currently active.
It is possible to look inside each module to see what functions are available in that module -
Get-Command -Module <module-name>
Some modules may have only one block of code inside them, but others may have dozens.
The code for all these modules are stored in files on the computer - in Powershell 5.1 there are two default locations in Windows 10 1809 for the installed modules, and another third default location for added modules that are user specific.
The first default location is
C:\Windows\System32\WindowsPowerShell\1.0\Modules
and is the location used by Microsoft for modules that are included by Microsoft in the Windows installation - this location should not be used for user added modules.
The second default location is
C:\Program Files\WindowsPowerShell\Modules
and can be used to install add-on modules that need to be accessible to all users on the computer.
The third default location is
C:\Users\<Username>\Documents\WindowsPowerShell\Modules
and can be used to install add-on modules that are available for just that one user.
Module files don`t have to be stored in these locations, but it makes it more complicated if they are elsewhere because the path to them will have to be added.
Powershell 5.1 knows about these three paths and has a PATH environment built in - this can be viewed using the command
$env:PSModulePath
Easier to read is the output from
$env:PSModulePath -Split ';'
Note that the location
C:\Program Files\WindowsPowerShell\Modules
is added to the value of the PSModulePath environment variable by default in Windows PowerShell 4.0 and later. For earlier versions of Windows PowerShell, you will have to manually create the location and then add it to the PATH.
To add a new module location, use the following command format.
$Env:PSModulePath = $Env:PSModulePath + ";<path>"
The semi-colon (;) in the command separates the new path from the path that precedes it in the list.
However this is not permanent - when the Powershell session is closed it forgets the addition to the PATH.
The location of the PATH in the registry is
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\PSModulePath
This shows the two system paths, but it is not modified by the above command to add the new path.
Microsoft publish a rather complicated way to change the PATH permamently using a three line Powershell script -
$p = [Environment]::GetEnvironmentVariable("PSModulePath")
$p += ";C:\Program Files\<new-folder>\Modules\"
[Environment]::SetEnvironmentVariable("PSModulePath",$p)
It does work but it isn`t permament unless you do an environment broadcast message - when you close the Powershell console the addition to the path is forgotten.
Another thing to watch is that the commands
$env:PsModulePath
and
[Environment]::GetEnvironmentVariable("PSModulePath")
may show that there is already a semi-colon at the end of the path - if you use this script as it stands you will end up with two semicolons, which Powershell will not like - you may need to take out the semicolon in the second line of the script.
It is quite straightforward to change the PATH in the registry - it could be done by editing the registry entry directly, but that is not really advisable.
Much easier to go to Control Panel / System / Advanced system settings / Environment Variables - on the lower pane scroll down to PSModulePath and edit that by adding the new path.
This will edit the registry entry, and the command
$env:PSModulePath -Split ';'
should now show the added path.
A restart may be advisable, as is quite common after changing the registry.
Out there in the outside world there are several thousand modules that are available for download - the command
Install-Module
can be used to download one or more modules from a repository, and install them on the local computer.
The command has a Scope parameter that allows you to specify whether the module is installed for the current user or for all users.
By adding the parameter
Install-Module ..... -Scope AllUsers ......
the module will be installed in
C:\Program Files\WindowsPowerShell\Modules
By adding the parameter
Install-Module ..... -Scope CurrentUser ......
the module will be installed in
C:\Users\<Username>\Documents\WindowsPowerShell\Modules
If the "Scope" parameter is not used the default location depends on which version of Powershell is being used.
The command
Import-Module
adds one or more modules to the current session - and only the current session - close the Powershell session and they are gone on the next session.
Beginning in PowerShell 3.0, modules are imported automatically when any cmdlet or function in the module is used in a command as long as the module is included in the PATH in the PSModulePath environment variable.
If the module is not on a valid path you can still import them by providing the path.
So to use the default installed modules in a Powershell 5.1 environment this command is not required.
A module can contain any number of components - scripts, cmdlets, manifests, and functions.
Probably the simplest component it can contain is a single script that contains a function - instead of naming the script with the " .ps1 " extension, the script is named with a " .psm1 " extension.
The name of the script has to have the same name as the name of the folder containing the module.
To turn the script into a function just involves adding a bit of text to the start of the script and ending the script with a curly bracket -
function Set-FirewallScript {
<whole script>
}
So the script is now a function with the name " Set-FirewallScript ".
I did it three times to see how the different locations behaved - I used the two default locations and created a new one as well.
So the paths to the default locations for my scripts are
C:\Program Files\WindowsPowerShell\Modules\FirewallScript\FirewallScript.psm1
and
C:\Users\<Username>\Documents\WindowsPowerShell\Modules\FirewallScript3\FirewallScript3.psm1
The path to the new location that I created is
C:\Program Files\Powershell-Dev\Modules\FirewallScript2\FirewallScript2.psm1
After updating the Powershell PATH as described above, the command
Get-Module -ListAvailable
shows the three functions in their various locations.