Skip to content

PowerShell Modules

How PowerShell modules package reusable code into distributable units, including script modules (.psm1), binary modules, and manifest modules, and how to import, manage, and inspect them with cmdlets like Import-Module and Get-Module.

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.

MyTools.psm1powershell
function Get-DiskSpaceReport {
    Get-Volume | Select-Object DriveLetter, FileSystemLabel, SizeRemaining, Size
}

where:

  • .psm1 is the file extension identifying a script module.
  • Get-DiskSpaceReport is 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 like Microsoft.PowerShell.Management.
  • Manifest modules: described by a .psd1 manifest file, which can reference one or more .psm1/.dll files 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.

NetTools.psm1powershell
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:

  • .psm1 is the dedicated file extension recognized by the PowerShell engine as a script module.
  • function Test-PortConnectivity is 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.TcpClient is a direct instantiation of a .NET socket class used to verify open ports.
Consolepowershell
Import-Module ./NetTools.psm1
Test-PortConnectivity -TargetHost "127.0.0.1" -Port 445
Outputplaintext
ComputerName Port IsOpen
------------ ---- ------
127.0.0.1     445   True

Viewing 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.

Consolepowershell
Get-Module
Outputplaintext
ModuleType 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.

Consolepowershell
Get-Module -ListAvailable
Outputplaintext
ModuleType 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.

Consolepowershell
Get-Module -Name "MyTools" -ListAvailable

where -Name is the module name to search for and supports wildcards (e.g., "My*").

Inspecting Modules in Detail

inspect-modules.ps1powershell
$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.
  • -ListAvailable directs PowerShell to scan all directory trees defined in $env:PSModulePath for valid modules.
  • -Name filters discovery by module name using exact strings or wildcard patterns.
  • ModuleType identifies whether the module is Script (.psm1), Manifest (.psd1), Binary (compiled .dll), or Cim.
Consolepowershell
./inspect-modules.ps1
Outputplaintext
LoadedCount SampleLoaded               InstalledMatch
----------- ------------               --------------
          4 Microsoft.PowerShell.Core               2

Module 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.

MyTools.psd1powershell
@{
    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:

  • RootModule is the primary .psm1 (or .dll) file the manifest describes.
  • ModuleVersion is a version number used for updates and dependency checks.
  • GUID is a unique identifier distinguishing this module from others with the same name.
  • FunctionsToExport is 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.

create-manifest.ps1powershell
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-ModuleManifest generates a standard, valid .psd1 manifest file.
  • -Path is the target disk path and file name for the manifest.
  • -RootModule specifies the primary script module (.psm1) or compiled library (.dll) to load when this manifest is processed.
  • -ModuleVersion is the semantic version string of the module release.
  • -FunctionsToExport is the whitelist of function names made publicly accessible. Using explicit arrays instead of wildcards (*) dramatically improves module loading and command discovery speed.
  • -CmdletsToExport specifies compiled binary cmdlets to expose (empty array @() prevents unauthorized export scans).
Consolepowershell
./create-manifest.ps1
Get-Content ./NetTools.psd1 -TotalCount 15
Outputplaintext
#
# 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.

Consolepowershell
Test-ModuleManifest -Path "C:\Modules\MyTools\MyTools.psd1"
Outputplaintext
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
check-module-paths.ps1powershell
$env:PSModulePath -split [System.IO.Path]::PathSeparator

where:

  • $env:PSModulePath is the system environment variable containing search paths for modules.
  • [System.IO.Path]::PathSeparator is the operating system delimiter (; on Windows, : on Linux/macOS).
Consolepowershell
./check-module-paths.ps1
Outputplaintext
C:\Users\Admin\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\Windows\system32\WindowsPowerShell\v1.0\Modules

Practical 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.

check-module-status.ps1powershell
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:

  • $loaded is the result of checking the current session for the module.
  • $installed is the result of checking all modules available on disk.
Consolepowershell
./check-module-status.ps1 -ModuleName "MyTools"
Outputplaintext
MyTools is installed but NOT loaded (v1.0.0)
Navigation