Intro
A PowerShell local lab gives you a safe environment to test commands, explore modules, and develop automation without affecting production systems. This guide walks through setting up your own lab, checking the environment, making configuration changes, verifying results, diagnosing failures, and recovering from common issues. The goal is operational safety: observe before changing, limit the blast radius, use placeholders instead of secrets, verify every result, and document recovery steps.
This article is for developers, DevOps consultants, and technical startup teams who want to improve their PowerShell skills. You will learn how to inventory your environment, apply safe configuration practices, run diagnostics, handle failures, and follow a repeatable operations checklist. All examples are concrete and include expected output and recovery paths.
Version and Environment Inventory
Before you make any changes, understand exactly what you are working with. The Version and Environment Inventory section helps you identify the relevant component, its supported version range, prerequisites, and deployment topology. Capture the current state with a read-only command from the official documentation, then define the expected result and failure signal before making a change.
Check Your PowerShell Version
PowerShell comes in two editions: Windows PowerShell 5.1 (built into Windows) and PowerShell 7+ (cross-platform, formerly PowerShell Core). To see what you have installed, run:
$PSVersionTable.PSVersion
Expected output on a modern Windows 11 system with PowerShell 7.4:
Major Minor Patch PreReleaseLabel BuildLabel
----- ----- ----- --------------- ----------
7 4 1
If the output shows Major 5 and Minor 1, you are running Windows PowerShell 5.1. For a local lab, install PowerShell 7+ because it receives ongoing updates and works across platforms. Download it from the official GitHub releases page or use winget:
winget install --id Microsoft.PowerShell --source winget
Verify after installation:
pwsh -NoProfile -Command '$PSVersionTable.PSVersion'
Expected output: the new version, e.g., 7.4.1.
Check Execution Policy
The execution policy controls whether scripts can run. View the current policy for your user and machine:
Get-ExecutionPolicy -List
Expected output (typical for a workstation):
Scope ExecutionPolicy
----- ---------------
MachinePolicy Undefined
UserPolicy Undefined
Process Undefined
CurrentUser Undefined
LocalMachine RemoteSigned
RemoteSigned is a reasonable starting point for a local lab: local scripts run, and remote scripts must be signed. You can change it later in the Safe Configuration section.
Check Installed Modules
List all modules available to your session:
Get-Module -ListAvailable | Select-Object Name, Version, ModuleType | Sort-Object Name | Format-Table -AutoSize
Expected output (truncated example):
Name Version ModuleType
---- ------- ----------
Microsoft.PowerShell.Archive 7.0.0.0 Binary
Microsoft.PowerShell.Management 7.4.0.0 Binary
PSReadLine 2.3.4 Binary
This inventory helps you spot missing prerequisites. For example, if you plan to use the Az module for Azure, you need to install it first.
Check Network Connectivity (optional)
If your lab requires downloading modules or packages, verify connectivity:
Test-NetConnection -ComputerName www.powershellgallery.com -Port 443 | Select-Object ComputerName, RemotePort, TcpTestSucceeded
Expected output when connectivity is good:
ComputerName RemotePort TcpTestSucceeded
------------ ---------- ----------------
www.powershellgallery.com 443 True
If TcpTestSucceeded is False, check your firewall, proxy settings, or internet connection before proceeding.
Record State and Timestamps
Always capture the current state and a timestamp before any changes. Use a simple transcript or a log file:
$timestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
$logFile = "$env:USERPROFILE\lab-inventory-$timestamp.txt"
Get-ExecutionPolicy -List | Out-File $logFile
Get-Module -ListAvailable | Out-File $logFile -Append
Write-Output "Inventory saved to $logFile"
This creates a baseline you can compare against later.
Safe Configuration Path
When you need to change settings in your lab, follow a safe configuration path: start with a read-only observation, make the smallest justified change, verify the outcome, and have a recovery plan. Never use real credentials, tokens, or private keys in your examples or scripts.
Change Execution Policy Safely
If you need to run unsigned scripts, you can set the execution policy for the current user only to avoid affecting the whole system:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned -Force
Expected behavior: no output. Verify:
Get-ExecutionPolicy -Scope CurrentUser
Expected output: RemoteSigned.
Recovery: to revert to default, run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy Undefined -Force.
Configure a Lab-Specific Profile
Create a profile that loads only for your lab sessions, not for all PowerShell sessions. You can use a separate script and dot-source it when needed. Create a file LabProfile.ps1 in a safe location:
$labProfilePath = "$env:USERPROFILE\Documents\PowerShellLab\LabProfile.ps1"
New-Item -Path (Split-Path $labProfilePath) -ItemType Directory -Force
@'
# Lab profile
Set-Location C:\Lab
$env:LAB_ENV = 'development'
Write-Host 'Lab profile loaded.'
'@ | Set-Content -Path $labProfilePath
To load it in a session:
. $labProfilePath
Expected output: Lab profile loaded. and the working directory changes to C:\Lab (create that directory first if it does not exist).
Recovery: simply start a new PowerShell session without dot-sourcing the profile.
Install a Module from PowerShell Gallery
Use Install-Module with scope CurrentUser to avoid needing admin rights and to limit the impact. For example, install the Pester testing framework:
Install-Module -Name Pester -Scope CurrentUser -Force
Verify:
Get-Module -Name Pester -ListAvailable | Select-Object Name, Version, ModuleType
Expected output (version may vary):
Name Version ModuleType
---- ------- ----------
Pester 5.5.0 Script
Recovery: uninstall with Uninstall-Module -Name Pester if needed.
Set Up a Local Git Repository for Scripts
Version control is essential for recovery. If Git is installed, initialize a repository for your lab scripts:
git init C:\Lab\Scripts
Then add and commit:
Set-Location C:\Lab\Scripts
git add .
git commit -m 'Initial lab scripts'
Expected output: a commit hash and summary. If Git is not installed, install it via winget install --id Git.Git -e --source winget or from https://git-scm.com/.
Verification and Diagnostics
After any change, verify that the system behaves as expected. This section shows concrete diagnostic commands and what to look for.
Verify Module Functionality
After installing a module, test that it can be imported and used. For Pester:
Import-Module Pester
Get-Command -Module Pester | Select-Object -First 5 Name, CommandType
Expected output:
Name CommandType
---- -----------
AfterAll Function
AfterEach Function
Assert-MockCalled Function
BeforeAll Function
BeforeEach Function
If the module fails to import, check the module path and installation scope.
Test a Simple Function
Create a file Greet.ps1 with:
function Get-Greeting {
param([string]$Name = 'World')
"Hello, $Name!"
}
Dot-source it and run:
. .\Greet.ps1
Get-Greeting -Name 'Lab'
Expected output: Hello, Lab!
If you get an error about execution policy, refer to the Safe Configuration Path section.
Run a Pester Test
Create a test file Greet.Tests.ps1:
BeforeAll {
. $PSScriptRoot\Greet.ps1
}
Describe 'Get-Greeting' {
It 'Returns hello with name' {
Get-Greeting -Name 'Test' | Should -Be 'Hello, Test!'
}
It 'Returns hello with default' {
Get-Greeting | Should -Be 'Hello, World!'
}
}
Run the tests:
Invoke-Pester -Path .\Greet.Tests.ps1
Expected output (passing):
Tests completed in 0.5s
Tests Passed: 2, Failed: 0, Skipped: 0
If a test fails, Pester shows the expected and actual values.
Diagnostic: Check for Errors in Event Logs (Windows)
If you suspect deeper issues, check the Windows PowerShell event log:
Get-WinEvent -LogName 'Windows PowerShell' -MaxEvents 10 | Format-List TimeCreated, Id, LevelDisplayName, Message
Look for errors that match the time of your failed command.
Failure Modes and Recovery
Even in a lab, things go wrong. This section describes common failure modes and how to recover.
Failure: Execution Policy Blocks Script
Symptom: running a script returns File cannot be loaded because running scripts is disabled on this system.
Diagnosis: check current policy:
Get-ExecutionPolicy -List
Recovery: set the policy for current user to RemoteSigned as shown in Safe Configuration Path, or run the script with bypass for one time only:
powershell -ExecutionPolicy Bypass -File .\script.ps1
Failure: Module Installation Fails Due to Trust
Symptom: Install-Module prompts for untrusted repository or fails with Unable to resolve package source.
Diagnosis: check registered repositories:
Get-PSRepository
Expected output:
Name InstallationPolicy SourceLocation
---- ------------------ --------------
PSGallery Untrusted https://www.powershellgallery.com/api/v2
Recovery: set the installation policy to trusted for the gallery or use -Force to accept prompts:
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module -Name Pester -Scope CurrentUser -Force
Remember to set it back to Untrusted if security is a concern:
Set-PSRepository -Name PSGallery -InstallationPolicy Untrusted
Failure: Command Not Recognized After Module Install
Symptom: after installing a module, Get-Command does not find its cmdlets.
Diagnosis: check if the module is available:
Get-Module -ListAvailable -Name ModuleName
If not listed, the module may be installed in a path not in $env:PSModulePath. View the variable:
$env:PSModulePath -split ';'
Recovery: ensure the installation path is included, or reinstall with -Scope CurrentUser and restart PowerShell. You can also manually import from the specific path:
Import-Module "C:\Users\YourUser\Documents\PowerShell\Modules\ModuleName"
Replace YourUser and ModuleName accordingly.
Failure: Script Produces Unexpected Output
Symptom: your script runs but output differs from expected.
Diagnosis: use Set-PSDebug -Trace 1 to trace execution, or add Write-Debug lines. For example:
Set-PSDebug -Trace 1
.\Greet.ps1
Get-Greeting -Name 'Test'
Set-PSDebug -Trace 0
This shows each line as it executes. Check variable values with Write-Host or Write-Verbose -Verbose.
Recovery: correct the logic, then re-run the Pester tests to verify.
Failure: Port or Connection Issues in Remote Labs
If your lab involves remoting, test WinRM:
Test-WSMan -ComputerName localhost
Expected output: a XML-like message with ProductVersion and ProtocolVersion. If it fails, ensure the Windows Remote Management service is running:
Get-Service WinRM
If status is not Running, start it:
Start-Service WinRM
Recovery: for persistent issues, run Enable-PSRemoting -Force (requires admin) to configure remoting.
Operations Checklist
Use this checklist before and after any change in your PowerShell local lab to ensure consistency and safety.
| Step | Action | Example Command | Expected Outcome |
|---|---|---|---|
| 1 | Record current state | Get-Date > lab-check.txt; Get-ExecutionPolicy -List >> lab-check.txt | Timestamp and policy list in file |
| 2 | Check PowerShell version | $PSVersionTable.PSVersion | Version 7.4.1 or similar |
| 3 | Verify execution policy | Get-ExecutionPolicy -List | RemoteSigned for CurrentUser |
| 4 | List installed modules | Get-Module -ListAvailable | At least: PSReadLine, Pester (if installed) |
| 5 | Test network connectivity | Test-NetConnection powershellgallery.com -Port 443 | TcpTestSucceeded : True |
| 6 | Make a scoped change | e.g., Set-ExecutionPolicy -Scope CurrentUser RemoteSigned | No output, success |
| 7 | Verify the change | Get-ExecutionPolicy -Scope CurrentUser | RemoteSigned |
| 8 | Run tests or sample | Invoke-Pester | All tests pass |
| 9 | Document recovery | Save rollback commands in a file | e.g., Set-ExecutionPolicy -Scope CurrentUser Undefined |
| 10 | Compare state before/after | Compare-Object (Get-Content old.txt) (Get-Content new.txt) | Only intended differences |
Keep this checklist as a file or in your lab notes. Before each session, run steps 1-5. After changes, run steps 6-10.
Conclusion
A PowerShell local lab is only as useful as the rigor you apply to it. By following the practices in this guide—inventorying your environment, making safe configuration changes, verifying with concrete commands, diagnosing failures, and using a repeatable checklist—you can develop and test with confidence. Remember that every command should be version-scoped, observable, and reversible where possible.
As a next step, choose one low-risk verification from this article, such as checking your execution policy or running a simple Pester test. Record the current state, run the check, compare the result with the expected output, and review any dependencies like Windows automation, Docker, or GitLab CI/CD that may affect your lab.
A reliable technical workflow makes failure visible, protects sensitive values, limits changes to the intended resource, and defines recovery verification before an incident forces the decision. Build your lab with these principles, and you will have a solid foundation for PowerShell automation.