A PowerShell module is a self-contained package of reusable code, including functions, cmdlets, variables, aliases, providers, and custom types, organized into a single unit. Modules standardize script libraries into distributable components that can be versioned, shared, and automatically loaded into interactive shell sessions or automated pipelines. They let you organize scripts logically and share them across sessions, scripts, and even other users.
What is a Module?
At its core, a module is typically a .psm1 file (a PowerShell script module) containing one or more functions. PowerShell also ships with many binary modules (compiled .NET assemblies) and manifest modules that bundle multiple files together.
function Get-DiskSpaceReport {
Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size
}where:
.psm1is the file extension identifying a script module.Get-DiskSpaceReportis a function defined inside the module, which becomes available once the module is imported.
Types of Modules
- Script modules (
.psm1): plain PowerShell code, the most common type for custom modules. - Binary modules (
.dll): compiled .NET code, often used by built-in modules likeMicrosoft.PowerShell.Management. - Manifest modules: described by a
.psd1manifest file, which can reference one or more.psm1/.dllfiles plus metadata. - Dynamic modules: created in memory at runtime using
New-Module, without a backing file.
What is a Script Module (.psm1)
A script module is a file containing PowerShell code saved with the .psm1 extension rather than the standard .ps1. When imported, functions, aliases, and variables defined within the .psm1 file become accessible in the caller's session, while internal helper functions can be selectively hidden from the end user.
function Test-PortConnectivity {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$TargetHost,
[Parameter(Mandatory = $true)]
[int]$Port
)
$tcpClient = New-Object System.Net.Sockets.TcpClient
$connect = $tcpClient.BeginConnect($TargetHost, $Port, $null, $null)
$success = $connect.AsyncWaitHandle.WaitOne(1000, $false)
[PSCustomObject]@{
ComputerName = $TargetHost
Port = $Port
IsOpen = $success
}
$tcpClient.Close()
}where:
.psm1is the dedicated file extension recognized by the PowerShell engine as a script module.function Test-PortConnectivityis a public function exposed by the module to the caller.[CmdletBinding()]is an attribute that turns a standard function into an advanced PowerShell function.New-Object System.Net.Sockets.TcpClientis a direct instantiation of a .NET socket class used to verify open ports.
Import-Module ./NetTools.psm1
Test-PortConnectivity -TargetHost "127.0.0.1" -Port 445ComputerName Port IsOpen
------------ ---- ------
127.0.0.1 445 TrueViewing Modules with Get-Module
The Get-Module cmdlet inspects PowerShell modules across two operational states: loaded (currently active in the session memory) and available (installed on disk and discoverable by the shell).
Loaded Modules
Without any parameters, Get-Module shows only the modules currently imported into your session.
Get-ModuleModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 3.1.0 Microsoft.PowerShell.Management {Add-Content, Clear-Content...}
Script 1.0.0 MyTools {Get-DiskSpaceReport}Installed (Available) Modules
Use -ListAvailable to see every module installed on the system, whether loaded or not.
Get-Module -ListAvailableModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 2.2.5 PSReadLine {Get-PSReadLineOption...}
Script 1.0.0 MyTools {Get-DiskSpaceReport}
Manifest 1.0.0.0 ScheduledTasks {Get-ScheduledTask...}where -ListAvailable searches all directories in $env:PSModulePath for installed modules, regardless of whether they've been imported.
Filtering by Name
Both forms accept a -Name parameter to search for a specific module.
Get-Module -Name "MyTools" -ListAvailablewhere -Name is the module name to search for and supports wildcards (e.g., "My*").
Inspecting Modules in Detail
$loaded = Get-Module | Select-Object -Property Name, Version, ModuleType
$installed = Get-Module -ListAvailable -Name "Microsoft.PowerShell.*" |
Select-Object -Property Name, Version, Path -First 2
[PSCustomObject]@{
LoadedCount = $loaded.Count
SampleLoaded = $loaded[0].Name
InstalledMatch = $installed.Count
}where:
Get-Module(without parameters) queries only the modules currently active in the running session.-ListAvailabledirects PowerShell to scan all directory trees defined in$env:PSModulePathfor valid modules.-Namefilters discovery by module name using exact strings or wildcard patterns.ModuleTypeidentifies whether the module isScript(.psm1),Manifest(.psd1),Binary(compiled.dll), orCim.
./inspect-modules.ps1LoadedCount SampleLoaded InstalledMatch
----------- ------------ --------------
4 Microsoft.PowerShell.Core 2Module Manifests (.psd1)
A module manifest is a data file ending in .psd1 that contains a structured hash table of metadata describing a module. Manifests define the version number, author, required dependencies, and precisely control which functions or cmdlets the module exports. Manifests are optional for simple modules but essential for published or versioned ones.
@{
RootModule = 'MyTools.psm1'
ModuleVersion = '1.0.0'
GUID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
Author = 'Jane Doe'
Description = 'Utility functions for disk and system reporting.'
PowerShellVersion = '5.1'
FunctionsToExport = @('Get-DiskSpaceReport')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
}where:
RootModuleis the primary.psm1(or.dll) file the manifest describes.ModuleVersionis a version number used for updates and dependency checks.GUIDis a unique identifier distinguishing this module from others with the same name.FunctionsToExportis an explicit list of functions made available when the module is imported.
Creating a Manifest with New-ModuleManifest
A manifest is created programmatically using the New-ModuleManifest cmdlet.
New-ModuleManifest -Path "./NetTools.psd1" `
-RootModule "NetTools.psm1" `
-ModuleVersion "1.0.0" `
-Author "SysAdmin Team" `
-Description "Internal network connectivity troubleshooting tools." `
-FunctionsToExport @("Test-PortConnectivity") `
-CmdletsToExport @() `
-AliasesToExport @()where:
New-ModuleManifestgenerates a standard, valid.psd1manifest file.-Pathis the target disk path and file name for the manifest.-RootModulespecifies the primary script module (.psm1) or compiled library (.dll) to load when this manifest is processed.-ModuleVersionis the semantic version string of the module release.-FunctionsToExportis the whitelist of function names made publicly accessible. Using explicit arrays instead of wildcards (*) dramatically improves module loading and command discovery speed.-CmdletsToExportspecifies compiled binary cmdlets to expose (empty array@()prevents unauthorized export scans).
./create-manifest.ps1
Get-Content ./NetTools.psd1 -TotalCount 15#
# Module manifest for module 'NetTools'
#
# Generated by: SysAdmin Team
#
# Generated on: 9/28/2026
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'NetTools.psm1'
# Version number of this module.
ModuleVersion = '1.0.0'Inspecting a Manifest
Use Test-ModuleManifest to validate a manifest file and view its parsed metadata.
Test-ModuleManifest -Path "C:\Modules\MyTools\MyTools.psd1"ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0.0 MyTools {Get-DiskSpaceReport}Module Path Architecture ($env:PSModulePath)
For PowerShell to locate and auto-load a module without requiring an absolute file path, the module must reside inside one of the folders specified in the $env:PSModulePath environment variable.
The directory layout must match the module name:
C:\Program Files\PowerShell\Modules\NetTools\
├── 1.0.0\
│ ├── NetTools.psd1
│ └── NetTools.psm1$env:PSModulePath -split [System.IO.Path]::PathSeparatorwhere:
$env:PSModulePathis the system environment variable containing search paths for modules.[System.IO.Path]::PathSeparatoris the operating system delimiter (;on Windows,:on Linux/macOS).
./check-module-paths.ps1C:\Users\Admin\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\ModulesPractical Usage: Inspecting What's Available vs Loaded
A common task when troubleshooting missing cmdlets is checking whether a module is installed but simply not yet imported.
param([string]$ModuleName)
$loaded = Get-Module -Name $ModuleName
$installed = Get-Module -Name $ModuleName -ListAvailable
if ($loaded) {
Write-Output "$ModuleName is loaded (v$($loaded.Version))"
} elseif ($installed) {
Write-Output "$ModuleName is installed but NOT loaded (v$($installed.Version))"
} else {
Write-Output "$ModuleName is not installed"
}where:
$loadedis the result of checking the current session for the module.$installedis the result of checking all modules available on disk.
./check-module-status.ps1 -ModuleName "MyTools"MyTools is installed but NOT loaded (v1.0.0)