09 · Modules Basics¶
A module is a packaged, reusable unit of PowerShell code — functions,
variables, and more — that you can load with Import-Module. Every cmdlet
you've used so far (Get-Process, Get-Content, ...) actually lives in a
built-in module.
Seeing what's loaded and available¶
Get-Module # modules currently loaded in this session
Get-Module -ListAvailable # all modules installed on this machine
Installing modules from the PowerShell Gallery¶
The PowerShell Gallery is the community package repository (like PyPI for Python or npm for JavaScript).
# search for a module
Find-Module -Name "Pester"
# install for the current user (no admin rights needed)
Install-Module -Name Pester -Scope CurrentUser -Force
# import it into the current session
Import-Module Pester
Writing your own simple module¶
A module can be as simple as a .psm1 file containing one or more
functions.
# MathHelpers.psm1
function Get-Square {
param([int]$Number)
return $Number * $Number
}
function Get-Cube {
param([int]$Number)
return $Number * $Number * $Number
}
Export-ModuleMember -Function Get-Square, Get-Cube
# using it from another script in the same folder
Import-Module ./MathHelpers.psm1
Get-Square -Number 5 # 25
Get-Cube -Number 3 # 27
Export-ModuleMember controls what's visible from outside the module —
without it, every function defined in the file is exported by default, but
being explicit is good practice once a module grows.
Module search paths¶
Modules placed in one of these standard locations (a per-user modules folder, in a subfolder matching the module's own name) can be imported by name alone, without a path:
Removing a module from the session¶
This unloads the module's functions from the current session (useful while iterating on a module's code) without uninstalling anything from disk.
Dot-sourcing vs importing¶
For a single script file full of helper functions (not a full module), you can also "dot-source" it — this runs the file in the current scope rather than a separate module scope.
# main.ps1
. ./helpers.ps1 # note the leading ". " with a space
Get-Greeting -Name "Ada" # Hello, Ada!
Dot-sourcing is simple and fine for small scripts; real reusable code that
you'll Import-Module from many places should be a proper module (covered
in depth in Level 3).
Cheat sheet¶
| Command | Purpose |
|---|---|
Get-Module / Get-Module -ListAvailable |
list loaded / installed modules |
Find-Module |
search the PowerShell Gallery |
Install-Module -Scope CurrentUser |
install a module for your user |
Import-Module <name-or-path> |
load a module into the session |
Export-ModuleMember -Function ... |
control what a module exposes |
Remove-Module |
unload a module from the session |
. ./script.ps1 |
dot-source a script into the current scope |
How It Actually Works¶
Importing a module is not "running a script" in the way dot-sourcing is —
Import-Module creates a child session state (a nested scope with its
own function/variable/alias tables) and executes the module's script
inside that child state, then selectively exports a subset of its
members back into your caller's session as FunctionInfo/AliasInfo
objects registered against the module. This is the actual mechanism
behind module encapsulation: a helper function you don't Export-
ModuleMember genuinely doesn't exist outside the module's own scope —
it's not hidden by convention, it's unreachable because it lives in a
session state your current scope has no reference to.
Dot-sourcing (. .\script.ps1) is the opposite: it runs the script's
code directly in the caller's own current scope, no child session state
created at all, so every variable and function the script defines lands
straight into your session as if you'd typed it yourself — that's exactly
why dot-sourced helper scripts can accidentally clobber your existing
variables, while an imported module's internals can't.
Module discovery walks $env:PSModulePath, a semicolon/colon-separated
list of directories, checking each for a subfolder matching the module
name containing either a .psd1 manifest or a same-named .psm1/.dll.
The manifest (.psd1) isn't code — it's a restricted-mode hashtable
literal (RootModule, ModuleVersion, RequiredModules,
FunctionsToExport, ...) that the module loader parses in a
constrained-language context before deciding how to load the module,
which is how PowerShell can validate compatibility (PowerShellVersion,
CompatiblePSEditions) and pre-declare exports without ever executing the
module's actual code.
🔀 See this in another language¶
- Python — Modules, Packages & pip
- C# — File I/O & Working with Text
- Kotlin — Extension Functions Intro
Exercise¶
Create a StringHelpers.psm1 module with two functions —
ConvertTo-TitleCaseWords (splits a sentence into words and title-cases
each) and Get-WordCount (returns the number of words in a string) — export
both with Export-ModuleMember, then write a separate test.ps1 that
imports the module and calls both functions on a sample sentence.