Scripts and modules
Save code as .ps1 scripts with parameters, share functions by dot-sourcing and modules, and install modules from the gallery.
- Write and run .ps1 scripts with their own param() block, and understand execution policy
- Reuse functions by dot-sourcing and by building a .psm1 module
- Find and install modules from the PowerShell Gallery, and customize your $PROFILE
A script is a .ps1 file. Run it by path: .\Get-Report.ps1 (or ./Get-Report.ps1). The .\ matters - PowerShell never runs scripts from the current folder by bare name, so a malicious ls.ps1 can’t hijack ls.
A script can start with its own param() block, exactly like a function, and then takes parameters: .\Get-Report.ps1 -Enclosure volcano -Days 7. Inside, $PSScriptRoot is the folder the script lives in - use it to find files that sit next to the script.
1$folder = Join-Path ([IO.Path]::GetTempPath()) "scripts-$(Get-Random)"
2New-Item -ItemType Directory -Path $folder | Out-Null
3Set-Content -Path (Join-Path $folder 'Get-Welcome.ps1') -Value @'
4param(
5 [Parameter(Mandatory)][string]$Name,
6 [int]$Times = 1
7)
8foreach ($i in 1..$Times) { "Welcome, $Name!" }
9'@
10& (Join-Path $folder 'Get-Welcome.ps1') -Name 'Skyla' -Times 2
11$script = Join-Path $folder 'Get-Welcome.ps1'
12& $script 'Pebble'Welcome, Skyla! Welcome, Skyla! Welcome, Pebble!
The & is the call operator: it runs a command whose name or path is in a string or variable. You need it whenever the path is computed or contains spaces - & "C:\Dragon Tools\report.ps1".
Sharing functions: dot-sourcing and modules
Running a script normally gives it its own scope: when it ends, its variables and functions disappear. Two ways to keep them:
- Dot-sourcing:
. .\helpers.ps1(a dot, a space, then the path) runs the file in your scope, so its functions stay defined. Quick, but everything it defines leaks in. - Modules: a
.psm1file of functions.Import-Module .\Dragons.psm1loads it, andExport-ModuleMemberchooses which functions are public - helpers stay private. Put a module folder in one of the paths in$env:PSModulePathand it even loads automatically the first time you call one of its commands.
1$folder = Join-Path ([IO.Path]::GetTempPath()) "module-$(Get-Random)"
2New-Item -ItemType Directory -Path $folder | Out-Null
3$modulePath = Join-Path $folder 'Dragons.psm1'
4Set-Content -Path $modulePath -Value @'
5function Get-Roar { param([string]$Name) "$Name says $(Get-RoarSound)" }
6function Get-RoarSound { 'RAAAWR' }
7Export-ModuleMember -Function Get-Roar
8'@
9Import-Module $modulePath
10Get-Roar -Name 'Ember'
11[bool](Get-Command Get-Roar -ErrorAction SilentlyContinue)
12[bool](Get-Command Get-RoarSound -ErrorAction SilentlyContinue)
13(Get-Module Dragons).ExportedFunctions.Keys -join ','
14Remove-Module DragonsEmber says RAAAWR True False Get-Roar
The module’s public function can still call its private helper, but you can’t. That’s encapsulation.
Thousands of ready-made modules live on the PowerShell Gallery (powershellgallery.com): Find-Module *azure* searches it and Install-Module Pester -Scope CurrentUser installs one for just you. (PowerShell 7.4 adds the faster Install-PSResource.) Popular picks: Pester for testing, PSScriptAnalyzer for linting, Az for Azure and Microsoft.Graph for Microsoft 365.
Finally, your profile - the script at $PROFILE - runs every time PowerShell starts. It’s the place for your favorite aliases, functions and prompt tweaks. notepad $PROFILE (or code $PROFILE) opens it; create it first with New-Item -Force $PROFILE if it doesn’t exist.
Key takeaways
Run scripts by path (
.\script.ps1); give them aparam()block; use$PSScriptRootfor nearby files.The call operator
&runs a command or script whose path is in a variable.Dot-source (
. .\file.ps1) to load functions into your scope; build a.psm1module to share them properly.Install-Modulegets modules from the PowerShell Gallery;$PROFILEcustomizes every session.
Lesson quiz
7 questions · pass with 5 correct · up to 50 XP
Passing this quiz completes the lesson and keeps your streak going. Questions you miss come back in review sessions later.
Practice: write PowerShell scripts
Write a script in the editor and run it for real against sample input, which is piped into your script as $input. Scripts run on PowerShell 6.2 through Try It Online (tio.run), a free public service, so these exercises avoid PowerShell 7-only syntax; your script and test input are sent there.
A script with parameters
Finish the script file the starter writes, Get-Enclosure.ps1, so that it has a param() block with a mandatory -Name and a -Size that must be small, medium or large (default medium), and outputs Ember needs a medium enclosure.
Then, for each input line (Name or Name Size), call the script with the call operator &, passing -Size only when the line has one.
- Three dragons
Lessons teach PowerShell 7; your script runs on PowerShell 6.2 via Try It Online (tio.run), a free public service, so stick to syntax that works there. Test input is piped into your script as $input. Your script and test input are sent to that service.
Build a module
Write a module Keepers.psm1 (the starter saves it to a temporary folder) containing two functions:
Get-ShiftKeeper -Hour <int>returnsAdafor hours 6-13,Gracefor 14-21 andLinusotherwise.Test-DayShift- a private helper (not exported) thatGet-ShiftKeepermay use.
Export only Get-ShiftKeeper. The provided code imports the module, looks up each input hour, and checks the helper is hidden.
- Around the clock
Lessons teach PowerShell 7; your script runs on PowerShell 6.2 via Try It Online (tio.run), a free public service, so stick to syntax that works there. Test input is piped into your script as $input. Your script and test input are sent to that service.
Questions about this lesson
Stuck? Ask. Figured something out? Share it. Explaining is one of the best ways to learn.
Loading posts…