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#

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-Noun naming 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#

  1. Download the MSI package from the Official PowerShell Docs.
  2. Alternatively, install via winget or Chocolatey:
# Using winget
winget install Microsoft.PowerShell

# Using Chocolatey
choco install powershell-core
  1. 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
pwsh

macOS#

brew install powershell/tap/powershell

3. 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 -Force

Profile locations:

ScopePath
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 ListView

Starship 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:USERNAME

Special Variables#

VariableDescription
$_ or $PSItemCurrent pipeline object
$nullNull value
$true / $falseBoolean values
$ErrorArray of recent errors
$LASTEXITCODEExit code from the last native command
$PSVersionTablePowerShell version information
$HOMEUser's home directory
$PWDCurrent working directory
$argsArguments 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#

OperatorDescriptionExample
-eqEqual5 -eq 5
-neNot equal5 -ne 3
-gtGreater than5 -gt 3
-ltLess than3 -lt 5
-geGreater or equal5 -ge 5
-leLess or equal3 -le 5
-likeWildcard match"hello" -like "h*"
-matchRegex match"hello" -match "^h"
-containsCollection contains@(1,2,3) -contains 2
-inValue in collection2 -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 -Descending

Pipeline 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.Management

Process Management#

Get-Process
Get-Process -Name chrome
Stop-Process -Name notepad -Force
Start-Process notepad.exe -ArgumentList "C:\file.txt"
Wait-Process -Name setup

Service 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 Automatic

Output 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-GridView

7. 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).LastWriteTime

8. 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"
ValueBehavior
ContinueDisplay error and continue (default)
StopTerminate on error (enables catch)
SilentlyContinueSuppress error and continue
InquirePrompt 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
PolicyDescription
RestrictedNo scripts allowed (default on Windows client)
AllSignedOnly scripts signed by a trusted publisher
RemoteSignedLocal scripts run freely; downloaded scripts need signature
UnrestrictedAll scripts run; warns for downloaded scripts
BypassNo 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 Az

Useful Modules#

ModulePurpose
PSWindowsUpdateManage Windows Updates
ActiveDirectoryAD user/group/computer management
AzAzure resource management
DnsServerWindows DNS Server management
PSReadLineEnhanced command-line editing
PesterTesting framework for PowerShell
ImportExcelExcel 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-PSSession

Invoke 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 $job

SSH-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_OperatingSystem

13. PowerShell 7 vs Windows PowerShell#

FeatureWindows PowerShell (5.1)PowerShell 7+
.NET version.NET Framework 4.x.NET 8+
Cross-platformWindows onlyWindows, Linux, macOS
Executablepowershell.exepwsh.exe
Default installBuilt into WindowsSeparate install
Pipeline chainingNot available&& and || operators
Ternary operatorNot available$x ? "yes" : "no"
Null coalescingNot available$x ?? "default"
ForEach-Object -ParallelNot availableAvailable
SSH remotingNot availableAvailable
Module compatibilityFull Windows module supportMost 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#

IssueCauseSolution
Script cannot be loaded, execution policyExecution policy too restrictiveRun Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Module not found after installModule installed for different scope/versionCheck $env:PSModulePath; install with -Scope CurrentUser or -Scope AllUsers
Remoting fails with "access denied"WinRM not enabled or user lacks permissionRun Enable-PSRemoting on target; ensure user is in local Administrators or Remote Management Users group
Cmdlet not recognizedModule not imported or not installedRun Get-Module -ListAvailable to check; install or import the module
Profile not loadingProfile file does not existRun New-Item -Path $PROFILE -Force to create it
PowerShell 7 missing Windows modulesModule not compatible with .NET CoreUse Import-Module <name> -UseWindowsPowerShell for compatibility shim
Slow startupHeavy profile scriptProfile audit: Measure-Command { pwsh -NoProfile } vs Measure-Command { pwsh }
Object properties appear emptyUsing Format-* cmdlets mid-pipelineMove Format-Table/Format-List to the end of the pipeline only

See Also#

Sources#