PowerShell is a cross-platform task automation and configuration management framework from Microsoft, consisting of a command-line shell, scripting language, and configuration management system.
Table of Contents#
- 1. Overview
- 2. Installation
- 3. Profile and Customization
- 4. Variables and Data Types
- 5. Operators and Pipeline
- 6. Common Cmdlets Reference
- 7. File and Directory Operations
- 8. Error Handling
- 9. Script Execution Policies
- 10. Modules
- 11. Remoting
- 12. WMI and CIM Queries
- 13. PowerShell 7 vs Windows PowerShell
- Troubleshooting
- See Also
- Sources
1. Overview#
PowerShell is a task automation and configuration management program from Microsoft, consisting of a command-line shell and the associated scripting language. Unlike traditional shells that pass text between commands, PowerShell passes .NET objects, making data manipulation and filtering more powerful and type-safe.
Key concepts:
- Cmdlets - native PowerShell commands following a
Verb-Nounnaming convention - Pipeline - passes objects (not text) between commands using the
|operator - Providers - expose data stores (registry, filesystem, certificates) as navigable drives
- Modules - packages of cmdlets, functions, and resources that extend functionality
2. Installation#
PowerShell was made open-source and cross-platform with PowerShell Core (now PowerShell 7+).
Windows#
- Download the MSI package from the Official PowerShell Docs.
- Alternatively, install via winget or Chocolatey:
# Using winget
winget install Microsoft.PowerShell
# Using Chocolatey
choco install powershell-core- Set up a PowerShell profile in Windows Terminal:
{
"commandline": "pwsh.exe -nologo",
"name": "PowerShell",
"source": "Windows.Terminal.PowershellCore"
}Linux (Ubuntu 24.04)#
# Update the list of packages
sudo apt-get update
# Install pre-requisite packages
sudo apt-get install -y wget apt-transport-https software-properties-common
# Download the Microsoft repository GPG keys
wget -q "https://packages.microsoft.com/config/ubuntu/24.04/packages-microsoft-prod.deb"
# Register the Microsoft repository GPG keys
sudo dpkg -i packages-microsoft-prod.deb
# Update the list of packages after adding packages.microsoft.com
sudo apt-get update
# Install PowerShell
sudo apt-get install -y powershell
# Start PowerShell
pwshmacOS#
brew install powershell/tap/powershell3. Profile and Customization#
The PowerShell profile is a script that runs when a session starts.
# Open your profile script in your default editor
code $PROFILE
# View all profile paths
$PROFILE | Format-List -ForceProfile locations:
| Scope | Path |
|---|---|
| Current User, Current Host | $PROFILE.CurrentUserCurrentHost |
| Current User, All Hosts | $PROFILE.CurrentUserAllHosts |
| All Users, Current Host | $PROFILE.AllUsersCurrentHost |
| All Users, All Hosts | $PROFILE.AllUsersAllHosts |
Example profile:
# Set aliases
Set-Alias -Name ll -Value Get-ChildItem
Set-Alias -Name np -Value notepad.exe
# Custom prompt
function prompt {
"$($executionContext.SessionState.Path.CurrentLocation)> "
}
# Import frequently used modules
Import-Module PSReadLine
Set-PSReadLineOption -PredictiveViewStyle ListViewStarship Prompt (Optional)#
Customize the look and feel of PowerShell with the Starship prompt:
# Install Starship
winget install Starship.Starship
# Add to your profile
Add-Content -Path $PROFILE -Value 'Invoke-Expression (&starship init powershell)'4. Variables and Data Types#
Variable Basics#
# Declare variables ($ prefix required)
$name = "Server01"
$count = 42
$isRunning = $true
$servers = @("Server01", "Server02", "Server03")
$config = @{ Name = "App"; Port = 8080; Enabled = $true }
# Variable types
[string]$hostname = "dc01"
[int]$port = 443
[datetime]$today = Get-Date
[array]$list = 1, 2, 3
# Environment variables
$env:PATH
$env:COMPUTERNAME
$env:USERNAMESpecial Variables#
| Variable | Description |
|---|---|
$_ or $PSItem | Current pipeline object |
$null | Null value |
$true / $false | Boolean values |
$Error | Array of recent errors |
$LASTEXITCODE | Exit code from the last native command |
$PSVersionTable | PowerShell version information |
$HOME | User's home directory |
$PWD | Current working directory |
$args | Arguments passed to a script or function |
String Operations#
# String interpolation (double quotes only)
"Hello, $name. You have $($servers.Count) servers."
# Literal string (no interpolation)
'Hello, $name'
# Here-strings (multiline)
$query = @"
SELECT *
FROM Servers
WHERE Name = '$name'
"@
# Common string methods
$name.ToUpper()
$name.ToLower()
$name.Contains("Server")
$name.Replace("01", "02")
$name.Split("r")5. Operators and Pipeline#
Comparison Operators#
| Operator | Description | Example |
|---|---|---|
-eq | Equal | 5 -eq 5 |
-ne | Not equal | 5 -ne 3 |
-gt | Greater than | 5 -gt 3 |
-lt | Less than | 3 -lt 5 |
-ge | Greater or equal | 5 -ge 5 |
-le | Less or equal | 3 -le 5 |
-like | Wildcard match | "hello" -like "h*" |
-match | Regex match | "hello" -match "^h" |
-contains | Collection contains | @(1,2,3) -contains 2 |
-in | Value in collection | 2 -in @(1,2,3) |
Logical Operators#
# AND, OR, NOT
($a -gt 5) -and ($b -lt 10)
($a -gt 5) -or ($b -lt 10)
-not ($a -gt 5)Pipeline#
The pipeline passes objects between cmdlets:
# Filter, sort, and select
Get-Process | Where-Object { $_.CPU -gt 100 } | Sort-Object CPU -Descending | Select-Object -First 5 Name, CPU
# ForEach-Object
Get-Service | ForEach-Object { "$($_.Name): $($_.Status)" }
# Measure results
Get-ChildItem -Recurse | Measure-Object -Property Length -Sum
# Group and aggregate
Get-EventLog -LogName System -Newest 100 | Group-Object -Property Source | Sort-Object Count -DescendingPipeline Chaining (PowerShell 7+)#
# Run second command only if first succeeds
Get-Process notepad && Stop-Process -Name notepad
# Run second command only if first fails
Get-Process notepad || Write-Host "Notepad is not running"6. Common Cmdlets Reference#
Getting Help#
# Get help for a cmdlet
Get-Help Get-Process -Full
# Update help files
Update-Help -Force
# Find cmdlets by name pattern
Get-Command -Name *service* -CommandType Cmdlet
# Find cmdlets by verb
Get-Command -Verb Get -Module Microsoft.PowerShell.ManagementProcess Management#
Get-Process
Get-Process -Name chrome
Stop-Process -Name notepad -Force
Start-Process notepad.exe -ArgumentList "C:\file.txt"
Wait-Process -Name setupService Management#
Get-Service
Get-Service -Name wuauserv
Start-Service -Name Spooler
Stop-Service -Name Spooler -Force
Restart-Service -Name Spooler
Set-Service -Name Spooler -StartupType AutomaticOutput and Formatting#
# Table format (default for most cmdlets)
Get-Process | Format-Table Name, CPU, WorkingSet -AutoSize
# List format
Get-Service | Format-List *
# Export to CSV
Get-Process | Export-Csv -Path C:\procs.csv -NoTypeInformation
# Export to JSON
Get-Service | ConvertTo-Json | Out-File C:\services.json
# Output to grid view (interactive)
Get-Process | Out-GridView7. File and Directory Operations#
# List files
Get-ChildItem -Path C:\Users
Get-ChildItem -Path C:\ -Recurse -Filter "*.log" -File
# Create directory
New-Item -Path C:\Temp\Logs -ItemType Directory
# Create file
New-Item -Path C:\Temp\test.txt -ItemType File -Value "Hello"
# Copy
Copy-Item -Path C:\source\* -Destination C:\dest -Recurse
# Move
Move-Item -Path C:\old\file.txt -Destination C:\new\file.txt
# Remove
Remove-Item -Path C:\Temp\old -Recurse -Force
# Read file content
Get-Content -Path C:\log.txt
Get-Content -Path C:\log.txt -Tail 20
# Write to file
Set-Content -Path C:\output.txt -Value "Content"
Add-Content -Path C:\log.txt -Value "New line"
# Test if path exists
Test-Path -Path C:\Temp\file.txt
# Get file properties
Get-ItemProperty -Path C:\file.txt
(Get-Item C:\file.txt).Length
(Get-Item C:\file.txt).LastWriteTime8. Error Handling#
Try/Catch/Finally#
try {
$result = Get-Item -Path "C:\nonexistent" -ErrorAction Stop
Write-Host "Found: $($result.FullName)"
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning "File not found: $_"
}
catch {
Write-Error "Unexpected error: $_"
}
finally {
Write-Host "Cleanup complete"
}Error Action Preference#
# Per-cmdlet error action
Get-Item -Path "C:\missing" -ErrorAction SilentlyContinue
# Session-wide preference
$ErrorActionPreference = "Stop"| Value | Behavior |
|---|---|
Continue | Display error and continue (default) |
Stop | Terminate on error (enables catch) |
SilentlyContinue | Suppress error and continue |
Inquire | Prompt user for action |
Inspecting Errors#
# View most recent error
$Error[0]
$Error[0].Exception.Message
$Error[0].ScriptStackTrace
# Clear error log
$Error.Clear()9. Script Execution Policies#
Execution policies control which scripts PowerShell is allowed to run.
# View current policy
Get-ExecutionPolicy
Get-ExecutionPolicy -List
# Set policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser| Policy | Description |
|---|---|
Restricted | No scripts allowed (default on Windows client) |
AllSigned | Only scripts signed by a trusted publisher |
RemoteSigned | Local scripts run freely; downloaded scripts need signature |
Unrestricted | All scripts run; warns for downloaded scripts |
Bypass | No restrictions, no warnings |
Scopes (in precedence order): MachinePolicy, UserPolicy, Process, CurrentUser, LocalMachine.
10. Modules#
Module Management#
# Find modules in the PowerShell Gallery
Find-Module -Name "*Azure*" | Select-Object Name, Version, Description
# Install a module
Install-Module -Name Az -Scope CurrentUser
# Update a module
Update-Module -Name Az
# Import a module into the current session
Import-Module -Name ActiveDirectory
# List imported modules
Get-Module
# List all installed modules
Get-Module -ListAvailable
# Remove a module from the current session
Remove-Module -Name Az
# Uninstall a module
Uninstall-Module -Name AzUseful Modules#
| Module | Purpose |
|---|---|
PSWindowsUpdate | Manage Windows Updates |
ActiveDirectory | AD user/group/computer management |
Az | Azure resource management |
DnsServer | Windows DNS Server management |
PSReadLine | Enhanced command-line editing |
Pester | Testing framework for PowerShell |
ImportExcel | Excel import/export without Office |
11. Remoting#
PowerShell Remoting uses WS-Management (WinRM) to execute commands on remote computers.
Enable Remoting#
# On the target machine (requires admin)
Enable-PSRemoting -Force
# Test connectivity
Test-WSMan -ComputerName <server-name>Interactive Session#
# Enter an interactive session
Enter-PSSession -ComputerName <server-name> -Credential (Get-Credential)
# Exit the session
Exit-PSSessionInvoke Commands Remotely#
# Run a command on one or more remote machines
Invoke-Command -ComputerName Server01, Server02 -ScriptBlock {
Get-Service -Name wuauserv
}
# Run a local script on remote machines
Invoke-Command -ComputerName Server01 -FilePath C:\Scripts\check.ps1
# Run as a background job
$job = Invoke-Command -ComputerName Server01, Server02, Server03 -ScriptBlock {
Get-EventLog -LogName System -Newest 10
} -AsJob
# Get job results
Receive-Job -Job $jobSSH-Based Remoting (PowerShell 7+)#
# Connect to a Linux host via SSH
Enter-PSSession -HostName <linux-host> -UserName <username>
# Invoke command via SSH
Invoke-Command -HostName <linux-host> -UserName <username> -ScriptBlock {
uname -a
}12. WMI and CIM Queries#
CIM (Common Information Model) cmdlets are the modern replacement for WMI cmdlets.
# System information
Get-CimInstance -ClassName Win32_OperatingSystem |
Select-Object Caption, Version, OSArchitecture, LastBootUpTime
# Disk space
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" |
Select-Object DeviceID,
@{N="SizeGB"; E={[math]::Round($_.Size/1GB,2)}},
@{N="FreeGB"; E={[math]::Round($_.FreeSpace/1GB,2)}}
# Installed software
Get-CimInstance -ClassName Win32_Product | Select-Object Name, Version
# Network adapters
Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "IPEnabled=True" |
Select-Object Description, IPAddress, DefaultIPGateway, DNSServerSearchOrder
# BIOS information
Get-CimInstance -ClassName Win32_BIOS
# Remote CIM query
Get-CimInstance -ClassName Win32_Service -ComputerName Server01 |
Where-Object { $_.State -eq "Stopped" -and $_.StartMode -eq "Auto" }Legacy WMI (Avoid in New Scripts)#
# Old style - use CIM equivalents instead
Get-WmiObject -Class Win32_OperatingSystem13. PowerShell 7 vs Windows PowerShell#
| Feature | Windows PowerShell (5.1) | PowerShell 7+ |
|---|---|---|
| .NET version | .NET Framework 4.x | .NET 8+ |
| Cross-platform | Windows only | Windows, Linux, macOS |
| Executable | powershell.exe | pwsh.exe |
| Default install | Built into Windows | Separate install |
| Pipeline chaining | Not available | && and || operators |
| Ternary operator | Not available | $x ? "yes" : "no" |
| Null coalescing | Not available | $x ?? "default" |
ForEach-Object -Parallel | Not available | Available |
| SSH remoting | Not available | Available |
| Module compatibility | Full Windows module support | Most modules via compatibility layer |
Note: Windows PowerShell 5.1 is still included in Windows and is not being replaced. PowerShell 7 runs side-by-side. Some Windows-specific modules (e.g., ActiveDirectory, GroupPolicy) may only work fully under Windows PowerShell 5.1 or require the Windows Compatibility module.
Troubleshooting#
| Issue | Cause | Solution |
|---|---|---|
| Script cannot be loaded, execution policy | Execution policy too restrictive | Run Set-ExecutionPolicy RemoteSigned -Scope CurrentUser |
| Module not found after install | Module installed for different scope/version | Check $env:PSModulePath; install with -Scope CurrentUser or -Scope AllUsers |
| Remoting fails with "access denied" | WinRM not enabled or user lacks permission | Run Enable-PSRemoting on target; ensure user is in local Administrators or Remote Management Users group |
| Cmdlet not recognized | Module not imported or not installed | Run Get-Module -ListAvailable to check; install or import the module |
| Profile not loading | Profile file does not exist | Run New-Item -Path $PROFILE -Force to create it |
| PowerShell 7 missing Windows modules | Module not compatible with .NET Core | Use Import-Module <name> -UseWindowsPowerShell for compatibility shim |
| Slow startup | Heavy profile script | Profile audit: Measure-Command { pwsh -NoProfile } vs Measure-Command { pwsh } |
| Object properties appear empty | Using Format-* cmdlets mid-pipeline | Move Format-Table/Format-List to the end of the pipeline only |