PowerShell Basics
PowerShell fundamentals and file operations.
Variables and Types
# Variable declaration
$name = "value"
$number = 42
$float = 3.14
$bool = $true
$null_var = $null
# Strong typing
[string]$str = "text"
[int]$num = 100
[datetime]$date = Get-Date
[array]$arr = @()
[hashtable]$hash = @{}
# Type conversion
[int]"42" # String to int
[string]42 # Int to string
[datetime]"2026-02-27" # String to datetime
# Check type
$var.GetType().Name
$var -is [string] # Returns $true or $false
# Arrays
$array = @("one", "two", "three")
$array = 1, 2, 3
$array += 4 # Append (creates new array!)
$array[0] # First element
$array[-1] # Last element
$array[1..3] # Slice (elements 1, 2, 3)
$array.Count # Length
# ArrayList (better for appending)
$list = [System.Collections.ArrayList]@()
$list.Add("item") | Out-Null # Out-Null suppresses return value
$list.Remove("item")
# Hashtables (dictionaries)
$hash = @{
Name = "vault-01"
IP = "10.50.1.60"
Port = 8200
}
$hash["Name"] # Access by key
$hash.Name # Dot notation
$hash.Keys # All keys
$hash.Values # All values
$hash.ContainsKey("Name") # Check if key exists
$hash += @{Role = "PKI"} # Add key-value
# Ordered hashtable (preserves insertion order)
$ordered = [ordered]@{
First = 1
Second = 2
Third = 3
}
# Environment variables
$env:PATH
$env:USERNAME
$env:COMPUTERNAME
$env:CUSTOM_VAR = "value" # Set for current session
TIP: Use ArrayList instead of @() arrays when building collections in loops - arrays create new copies on each append.
Operators
# Comparison (case-insensitive by default)
$a -eq $b # Equal
$a -ne $b # Not equal
$a -gt $b # Greater than
$a -ge $b # Greater than or equal
$a -lt $b # Less than
$a -le $b # Less than or equal
# Case-sensitive versions
$a -ceq $b # Case-sensitive equal
$a -cne $b # Case-sensitive not equal
# String matching
"hello" -like "*ell*" # Wildcard match
"hello" -notlike "world*" # Negation
"hello" -match "^h.*o$" # Regex match
$matches[0] # Capture group 0
"hello" -replace "l", "x" # Regex replace: hexxo
# Collection operators
$array -contains "value" # Check if array contains value
$array -notcontains "value"
"value" -in $array # Same but reversed operands
"value" -notin $array
# Logical operators
$true -and $false
$true -or $false
-not $false
!$false # Same as -not
# Bitwise operators
$a -band $b # AND
$a -bor $b # OR
$a -bxor $b # XOR
-bnot $a # NOT
# String operators
$s = "hello " + "world" # Concatenation
$s = "hello" * 3 # Repeat: hellohellohello
$s = "value: $variable" # Variable expansion
$s = "path: $($hash.Name)" # Expression expansion
# Range operator
1..10 # Array of 1 through 10
'a'..'z' # Array of a through z
# Split and join
"a,b,c" -split "," # @("a", "b", "c")
@("a", "b", "c") -join "," # "a,b,c"
# Ternary (PowerShell 7+)
$result = $condition ? "yes" : "no"
# Null-coalescing (PowerShell 7+)
$value = $null ?? "default"
$value ??= "default" # Assign if null
Control Flow
# If/ElseIf/Else
if ($value -gt 10) {
Write-Host "Greater"
} elseif ($value -eq 10) {
Write-Host "Equal"
} else {
Write-Host "Less"
}
# Switch
switch ($status) {
"Running" { Write-Host "Service is running" }
"Stopped" { Write-Host "Service is stopped" }
default { Write-Host "Unknown status: $status" }
}
# Switch with regex
switch -Regex ($input) {
"^Error" { Write-Host "Error found" }
"^Warning" { Write-Host "Warning found" }
}
# Switch with wildcard
switch -Wildcard ($filename) {
"*.txt" { Write-Host "Text file" }
"*.log" { Write-Host "Log file" }
}
# ForEach loop
foreach ($item in $collection) {
Write-Host $item
}
# For loop
for ($i = 0; $i -lt 10; $i++) {
Write-Host $i
}
# While loop
$i = 0
while ($i -lt 10) {
Write-Host $i
$i++
}
# Do-While (always runs at least once)
do {
$input = Read-Host "Enter value"
} while ($input -ne "quit")
# Do-Until
do {
$result = Get-Service -Name "MyService"
} until ($result.Status -eq "Running")
# Break and Continue
foreach ($item in $collection) {
if ($item -eq "skip") { continue }
if ($item -eq "stop") { break }
Write-Host $item
}
Functions
# Basic function
function Get-Greeting {
return "Hello, World!"
}
# Function with parameters
function Get-Greeting {
param (
[string]$Name = "World"
)
return "Hello, $Name!"
}
# Advanced function with validation
function Get-ServerInfo {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$ComputerName,
[Parameter()]
[ValidateSet("CPU", "Memory", "Disk")]
[string]$Resource = "CPU",
[Parameter()]
[switch]$Detailed
)
begin {
Write-Verbose "Starting Get-ServerInfo for $ComputerName"
}
process {
# Main logic here
$info = @{
ComputerName = $ComputerName
Resource = $Resource
Timestamp = Get-Date
}
if ($Detailed) {
$info.Add("Extra", "Details")
}
return [PSCustomObject]$info
}
end {
Write-Verbose "Completed Get-ServerInfo"
}
}
# Pipeline input
function Process-Items {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true)]
[string]$Item
)
process {
Write-Host "Processing: $Item"
}
}
# Usage: "a", "b", "c" | Process-Items
# Splatting (pass hashtable as parameters)
$params = @{
Path = "C:\logs"
Filter = "*.log"
Recurse = $true
}
Get-ChildItem @params
# Filter function (single-item pipeline)
filter Get-Large {
if ($_.Length -gt 1MB) { $_ }
}
Get-ChildItem | Get-Large
Modules
# List installed modules
Get-Module -ListAvailable
Get-Module # Currently loaded
# Import module
Import-Module ActiveDirectory
Import-Module -Name "C:\path\to\module.psm1"
# Install from PSGallery
Install-Module -Name Az -Scope CurrentUser
Install-Module -Name Pester -Force # Update if exists
# Find modules
Find-Module -Name "*Azure*"
Find-Module -Tag "Security"
# Update modules
Update-Module -Name Az
# Remove module
Remove-Module ActiveDirectory # Unload from session
Uninstall-Module -Name OldModule # Remove from system
# Create simple module
# File: MyModule.psm1
function Get-MyData {
# Function code
}
Export-ModuleMember -Function Get-MyData
# Module manifest
# File: MyModule.psd1
@{
ModuleVersion = '1.0.0'
RootModule = 'MyModule.psm1'
FunctionsToExport = @('Get-MyData')
Author = 'Your Name'
Description = 'Module description'
}
# Common modules
Import-Module ActiveDirectory # AD management
Import-Module DnsServer # DNS management
Import-Module DHCPServer # DHCP management
Import-Module GroupPolicy # GPO management
Import-Module ServerManager # Server roles/features
Import-Module NetSecurity # Firewall rules
Import-Module NetTCPIP # Network configuration
Import-Module Storage # Disk/volume management
Pipeline and Filtering
# Basic pipeline
Get-Process | Where-Object CPU -gt 10 | Sort-Object CPU -Descending
# Simplified Where-Object syntax
Get-Process | Where-Object {$_.CPU -gt 10 -and $_.Name -like "chrome*"}
# Simplified syntax (PowerShell 3+)
Get-Process | Where-Object CPU -gt 10
Get-Service | Where-Object Status -eq "Running"
# Select specific properties
Get-Process | Select-Object Name, CPU, WorkingSet
# Select first/last N
Get-Process | Select-Object -First 10
Get-Process | Select-Object -Last 5
# Unique values
Get-Process | Select-Object -Unique Name
# Calculated properties
Get-Process | Select-Object Name, @{
Name = "MemoryMB"
Expression = { [math]::Round($_.WorkingSet / 1MB, 2) }
}
# ForEach-Object
1..10 | ForEach-Object { $_ * 2 }
# Simplified ForEach syntax
Get-Process | ForEach-Object Name
# Method calling in ForEach
"hello", "world" | ForEach-Object ToUpper
# Sort
Get-Process | Sort-Object CPU -Descending
Get-Process | Sort-Object Name, CPU
# Group
Get-Service | Group-Object Status
# Measure
Get-Process | Measure-Object CPU -Sum -Average -Maximum
# Compare objects
Compare-Object $array1 $array2
# Tee (save and pass through)
Get-Process | Tee-Object -Variable procs | Select-Object -First 5
# Output to multiple places
Get-Process | Tee-Object -FilePath procs.txt | Where-Object CPU -gt 10
# Pipeline variable
Get-Process | Where-Object {$_.CPU -gt 10} -PipelineVariable proc |
ForEach-Object { "$($proc.Name): $($proc.CPU)" }
Output and Formatting
# Output methods
Write-Host "Message" # Console only (not pipeline)
Write-Output $object # Pipeline output (default)
Write-Information "Info" # Information stream
Write-Warning "Warning" # Warning stream
Write-Error "Error" # Error stream
Write-Verbose "Verbose" # Requires -Verbose
Write-Debug "Debug" # Requires -Debug
# Write-Host with colors
Write-Host "Success" -ForegroundColor Green
Write-Host "Error" -ForegroundColor Red -BackgroundColor Yellow
Write-Host "No newline" -NoNewline
# Format output
Get-Process | Format-Table # Table format
Get-Process | Format-Table -AutoSize # Adjust column widths
Get-Process | Format-List # List format (all properties)
Get-Process | Format-Wide Name # Wide format (single property)
# Custom table columns
Get-Process | Format-Table Name, @{
Label = "Memory (MB)"
Expression = { [math]::Round($_.WorkingSet / 1MB, 2) }
Alignment = "Right"
} -AutoSize
# Export data
Get-Process | Export-Csv -Path procs.csv -NoTypeInformation
Get-Process | Export-Clixml -Path procs.xml
Get-Process | ConvertTo-Json | Out-File procs.json
Get-Process | ConvertTo-Html | Out-File procs.html
# Out-* cmdlets
Get-Process | Out-File -FilePath procs.txt
Get-Process | Out-GridView # GUI table (Windows only)
Get-Process | Out-String # Convert to string
Get-Process | Out-Null # Discard output
# Redirect streams
Get-Process 2>&1 | Out-File all.txt # Errors to output
Get-Process *>&1 | Out-File all.txt # All streams to output
# Suppress output
[void](New-Item -Path "file.txt")
New-Item -Path "file.txt" | Out-Null
$null = New-Item -Path "file.txt"
Error Handling
# Try/Catch/Finally
try {
$result = Get-Content -Path "nonexistent.txt" -ErrorAction Stop
} catch {
Write-Error "Failed to read file: $_"
} finally {
# Cleanup code (always runs)
Write-Host "Cleanup complete"
}
# Catch specific exception types
try {
$null.Method()
} catch [System.Management.Automation.RuntimeException] {
Write-Error "Runtime error: $_"
} catch [System.IO.FileNotFoundException] {
Write-Error "File not found: $_"
} catch {
Write-Error "General error: $_"
}
# ErrorAction parameter
Get-Content -Path "file.txt" -ErrorAction SilentlyContinue
Get-Content -Path "file.txt" -ErrorAction Stop # Throws terminating error
Get-Content -Path "file.txt" -ErrorAction Continue # Default behavior
Get-Content -Path "file.txt" -ErrorAction Ignore # Suppress completely
# $ErrorActionPreference (global setting)
$ErrorActionPreference = "Stop" # All errors become terminating
# Check if error occurred
if (!$?) {
Write-Host "Previous command failed"
}
# $Error automatic variable
$Error[0] # Most recent error
$Error.Clear() # Clear error history
# Throw custom errors
throw "Something went wrong"
throw [System.ArgumentException]::new("Invalid argument", "paramName")
# Write-Error vs Throw
Write-Error "Non-terminating error" # Continues execution
throw "Terminating error" # Stops execution
# ErrorVariable parameter
Get-ChildItem -Path "C:\fake" -ErrorVariable myError -ErrorAction SilentlyContinue
if ($myError) {
Write-Host "Errors: $($myError.Count)"
}
TIP: Use -ErrorAction Stop when you want to catch errors with try/catch - most cmdlet errors are non-terminating by default.
JSON and XML
# JSON - Read
$json = Get-Content -Path "config.json" -Raw | ConvertFrom-Json
$json.server.host
$json.users[0].name
# JSON - Write
$data = @{
server = @{
host = "vault-01"
port = 8200
}
users = @("admin", "operator")
}
$data | ConvertTo-Json -Depth 10 | Out-File "config.json"
# JSON from API
$response = Invoke-RestMethod -Uri "https://api.example.com/data"
$response.items | ForEach-Object { $_.name }
# Modify JSON
$json = Get-Content "config.json" -Raw | ConvertFrom-Json
$json.server.port = 8201
$json | ConvertTo-Json -Depth 10 | Set-Content "config.json"
# XML - Read
[xml]$xml = Get-Content -Path "config.xml"
$xml.configuration.appSettings.add
$xml.SelectNodes("//add[@key='server']")
# XML - Modify
$node = $xml.SelectSingleNode("//add[@key='server']")
$node.value = "new-server"
$xml.Save("config.xml")
# Create XML
$xml = New-Object System.Xml.XmlDocument
$root = $xml.CreateElement("configuration")
$xml.AppendChild($root)
$setting = $xml.CreateElement("setting")
$setting.SetAttribute("name", "value")
$root.AppendChild($setting)
$xml.Save("new-config.xml")
# CSV
$csv = Import-Csv -Path "data.csv"
$csv | Where-Object Status -eq "Active"
$data | Export-Csv -Path "output.csv" -NoTypeInformation
# CLI XML (PowerShell object serialization)
Get-Process | Export-Clixml -Path "procs.xml"
$procs = Import-Clixml -Path "procs.xml"
Web Requests and APIs
# Simple GET
$response = Invoke-WebRequest -Uri "https://example.com"
$response.Content
$response.StatusCode
$response.Headers
# REST API (returns parsed JSON)
$data = Invoke-RestMethod -Uri "https://api.example.com/users"
# POST with body
$body = @{
name = "test"
value = 123
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://api.example.com/items" `
-Method Post `
-ContentType "application/json" `
-Body $body
# Authentication - Basic
$cred = Get-Credential
Invoke-RestMethod -Uri "https://api.example.com" -Credential $cred
# Authentication - Bearer token
$headers = @{
Authorization = "Bearer $token"
"Content-Type" = "application/json"
}
Invoke-RestMethod -Uri "https://api.example.com" -Headers $headers
# Authentication - API key
$headers = @{
"X-API-Key" = $apiKey
}
Invoke-RestMethod -Uri "https://api.example.com" -Headers $headers
# Ignore SSL errors (for self-signed certs)
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
# Or in PowerShell 7+:
Invoke-RestMethod -Uri "https://..." -SkipCertificateCheck
# Download file
Invoke-WebRequest -Uri "https://example.com/file.zip" -OutFile "file.zip"
# Upload file
Invoke-RestMethod -Uri "https://api.example.com/upload" `
-Method Post `
-InFile "file.txt" `
-ContentType "text/plain"
# Form data
$form = @{
file = Get-Item -Path "file.txt"
name = "test"
}
Invoke-RestMethod -Uri "https://api.example.com/upload" `
-Method Post `
-Form $form
Common Cmdlets
# Information
Get-Help Get-Process -Full # Help documentation
Get-Command -Noun "Process" # Find commands
Get-Member -InputObject $object # Object properties/methods
Get-Alias # List aliases
# String operations
"Hello".ToUpper()
"Hello".ToLower()
"Hello".Contains("ell")
"Hello".Replace("l", "x")
" Hello ".Trim()
"Hello".Substring(0, 3) # "Hel"
"Hello".Split("l") # @("He", "", "o")
# Date/Time
Get-Date
Get-Date -Format "yyyy-MM-dd"
Get-Date -Format "yyyy-MM-dd HH:mm:ss"
(Get-Date).AddDays(7)
(Get-Date).AddHours(-24)
[datetime]::Now
[datetime]::UtcNow
# Math
[math]::Round(3.14159, 2)
[math]::Ceiling(3.2)
[math]::Floor(3.8)
[math]::Pow(2, 10)
[math]::Sqrt(16)
[math]::Abs(-5)
# Random
Get-Random
Get-Random -Minimum 1 -Maximum 100
Get-Random -InputObject @("a", "b", "c")
1..10 | Get-Random -Count 3 # 3 random items
# Sleep/Wait
Start-Sleep -Seconds 5
Start-Sleep -Milliseconds 500
# Object creation
[PSCustomObject]@{
Name = "vault-01"
IP = "10.50.1.60"
Status = "Active"
}
# Clipboard (Windows)
Get-Clipboard
Set-Clipboard -Value "text"
Get-Process | Set-Clipboard
# History
Get-History
Invoke-History 42 # Run command #42
Clear-History
Common Gotchas
# WRONG: Using = instead of -eq in comparisons
if ($value = 10) { } # This ASSIGNS 10 to $value!
# CORRECT: Use comparison operator
if ($value -eq 10) { }
# WRONG: Forgetting $_ in Where-Object script block
Get-Process | Where-Object {CPU -gt 10} # Error!
# CORRECT: Use $_ or simplified syntax
Get-Process | Where-Object {$_.CPU -gt 10}
Get-Process | Where-Object CPU -gt 10
# WRONG: Expecting case-sensitive comparison
"Hello" -eq "HELLO" # Returns $true!
# CORRECT: Use case-sensitive operator
"Hello" -ceq "HELLO" # Returns $false
# WRONG: String expansion in single quotes
$name = "World"
'Hello, $name' # Outputs: Hello, $name
# CORRECT: Use double quotes for expansion
"Hello, $name" # Outputs: Hello, World
# WRONG: Array appending in loops (slow!)
$results = @()
foreach ($item in $collection) {
$results += $item # Creates new array each time!
}
# CORRECT: Use ArrayList or collect from pipeline
$results = [System.Collections.ArrayList]@()
foreach ($item in $collection) {
$results.Add($item) | Out-Null
}
# Or:
$results = foreach ($item in $collection) { $item }
# WRONG: Assuming cmdlets throw on error
Get-ChildItem -Path "C:\fake" # Continues execution!
# CORRECT: Use -ErrorAction Stop
try {
Get-ChildItem -Path "C:\fake" -ErrorAction Stop
} catch {
Write-Error "Path not found"
}
# WRONG: Forgetting -Raw for file content
$json = Get-Content "file.json" | ConvertFrom-Json # Fails on multi-line!
# CORRECT: Use -Raw for single string
$json = Get-Content "file.json" -Raw | ConvertFrom-Json
# WRONG: Return statement behavior
function Get-Data {
return 1
return 2 # Never executed - return exits immediately
}
# CORRECT: Output to pipeline for multiple values
function Get-Data {
1
2 # Both values output
}
# WRONG: Forgetting ConvertTo-Json depth limit
$deep | ConvertTo-Json # Default depth is 2!
# CORRECT: Specify depth
$deep | ConvertTo-Json -Depth 10
File System Navigation
# Get current location
Get-Location
$PWD
(Get-Location).Path
# Change directory
Set-Location C:\Windows
cd C:\Windows # Alias
Push-Location C:\temp # Save current, go to new
Pop-Location # Return to saved
# List directory contents
Get-ChildItem
Get-ChildItem -Path C:\Windows
ls # Alias
dir # Alias
# List with filters
Get-ChildItem -Filter *.txt
Get-ChildItem -Include *.txt, *.log
Get-ChildItem -Exclude *.tmp
# Recursive listing
Get-ChildItem -Recurse
Get-ChildItem -Recurse -Depth 2 # Limit depth
# Hidden and system files
Get-ChildItem -Force # Include hidden
Get-ChildItem -Hidden # Only hidden
Get-ChildItem -System # Only system
# Files only or directories only
Get-ChildItem -File
Get-ChildItem -Directory
# List with properties
Get-ChildItem | Select-Object Name, Length, LastWriteTime
Get-ChildItem | Format-Table Name, @{
Name = "SizeMB"
Expression = { [math]::Round($_.Length / 1MB, 2) }
} -AutoSize
# Sort by size (largest first)
Get-ChildItem -File | Sort-Object Length -Descending | Select-Object -First 10
# Find large files
Get-ChildItem -Recurse -File | Where-Object Length -gt 100MB
# Find recent files
Get-ChildItem -Recurse -File |
Where-Object LastWriteTime -gt (Get-Date).AddDays(-7)
Path Operations
# Path manipulation
$path = "C:\Users\Admin\Documents\file.txt"
Split-Path -Path $path -Parent # C:\Users\Admin\Documents
Split-Path -Path $path -Leaf # file.txt
Split-Path -Path $path -Extension # .txt (PowerShell 7+)
Split-Path -Path $path -Qualifier # C:
# Join paths (safe concatenation)
Join-Path -Path "C:\Users" -ChildPath "Admin"
Join-Path "C:\Users" "Admin" "Documents" # Multiple segments (PS 7+)
# Resolve relative paths
Resolve-Path -Path "..\file.txt"
Resolve-Path -Path "C:\Win*" # Wildcards
# Test path validity (doesn't check existence)
Test-Path -Path "C:\file.txt" -IsValid
# Convert to absolute path
[System.IO.Path]::GetFullPath(".\file.txt")
# Get file extension
[System.IO.Path]::GetExtension("file.txt") # .txt
# Change extension
[System.IO.Path]::ChangeExtension("file.txt", ".bak") # file.bak
# Get filename without extension
[System.IO.Path]::GetFileNameWithoutExtension("file.txt") # file
# Get temp path
[System.IO.Path]::GetTempPath()
# Create temp file
$tempFile = [System.IO.Path]::GetTempFileName()
# Or:
$tempFile = New-TemporaryFile
# Normalize path separators
$path -replace '/', '\'
# Environment paths
$env:TEMP
$env:APPDATA
$env:LOCALAPPDATA
$env:USERPROFILE
$env:ProgramFiles
$env:ProgramData
File Operations (Copy, Move, Delete)
# Copy file
Copy-Item -Path source.txt -Destination dest.txt
Copy-Item source.txt dest.txt # Positional
# Copy with overwrite
Copy-Item -Path source.txt -Destination dest.txt -Force
# Copy directory
Copy-Item -Path C:\Source -Destination C:\Dest -Recurse
# Copy multiple files
Copy-Item -Path *.txt -Destination C:\Backup
# Move file
Move-Item -Path source.txt -Destination dest.txt
Move-Item source.txt C:\Archive\
# Move with rename
Move-Item -Path file.txt -Destination newname.txt
# Rename file
Rename-Item -Path oldname.txt -NewName newname.txt
# Delete file
Remove-Item -Path file.txt
Remove-Item file.txt -Force # Ignore read-only
Remove-Item file.txt -WhatIf # Preview
# Delete directory
Remove-Item -Path C:\Folder -Recurse -Force
# Delete by pattern
Remove-Item -Path C:\Temp\*.tmp
# Create file
New-Item -Path file.txt -ItemType File
New-Item -Path file.txt -ItemType File -Value "content"
# Create directory
New-Item -Path C:\NewFolder -ItemType Directory
mkdir C:\NewFolder # Alias
# Create parent directories if needed
New-Item -Path C:\A\B\C\file.txt -ItemType File -Force
# Test if exists
Test-Path -Path C:\file.txt
Test-Path -Path C:\folder -PathType Container
Test-Path -Path C:\file.txt -PathType Leaf
# Create if not exists
if (-not (Test-Path C:\folder)) {
New-Item -Path C:\folder -ItemType Directory
}
File Content Operations
# Read entire file
Get-Content -Path file.txt
Get-Content -Path file.txt -Raw # As single string (important for JSON!)
# Read with encoding
Get-Content -Path file.txt -Encoding UTF8
# Read specific lines
Get-Content -Path file.txt -First 10 # First 10 lines
Get-Content -Path file.txt -Last 5 # Last 5 lines
Get-Content -Path file.txt -Tail 5 # Same as -Last
# Read by line number
(Get-Content file.txt)[0] # First line (0-indexed)
(Get-Content file.txt)[5..10] # Lines 6-11
# Write file (overwrite)
Set-Content -Path file.txt -Value "content"
"content" | Set-Content file.txt
# Write multiple lines
$lines = "line1", "line2", "line3"
Set-Content -Path file.txt -Value $lines
# Append to file
Add-Content -Path file.txt -Value "new line"
"new line" | Add-Content file.txt
# Write with encoding
Set-Content -Path file.txt -Value "content" -Encoding UTF8
# Clear file contents
Clear-Content -Path file.txt
# Out-File (alternative, preserves formatting)
Get-Process | Out-File -FilePath procs.txt
Get-Process | Out-File -FilePath procs.txt -Append
# Stream reader for large files (memory efficient)
$reader = [System.IO.StreamReader]::new("C:\large.log")
while ($null -ne ($line = $reader.ReadLine())) {
# Process each line
if ($line -match "error") {
Write-Host $line
}
}
$reader.Close()
# Read binary file
[System.IO.File]::ReadAllBytes("file.bin")
$bytes = Get-Content -Path "file.bin" -AsByteStream -Raw
# Write binary file
[System.IO.File]::WriteAllBytes("file.bin", $bytes)
$bytes | Set-Content -Path "file.bin" -AsByteStream
Searching File Contents
# Basic search (grep equivalent)
Select-String -Pattern "error" -Path *.log
# Recursive search
Get-ChildItem -Recurse -Filter *.log | Select-String -Pattern "error"
# Case-sensitive search
Select-String -Pattern "Error" -Path *.log -CaseSensitive
# Regex search
Select-String -Pattern "error|warning|critical" -Path *.log
Select-String -Pattern "^\d{4}-\d{2}-\d{2}" -Path *.log # Date at start
# Context (lines before/after match)
Select-String -Pattern "error" -Path *.log -Context 2, 2
# Count matches
(Select-String -Pattern "error" -Path *.log).Count
# Get just matching values
(Select-String -Pattern "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" -Path *.log).Matches.Value
# Search with Where-Object (more control)
Get-ChildItem -Recurse -File | ForEach-Object {
$content = Get-Content $_.FullName -Raw -ErrorAction SilentlyContinue
if ($content -match "password") {
Write-Host "Found in: $($_.FullName)"
}
}
# Find files containing text
Get-ChildItem -Recurse -Filter *.ps1 |
Select-String -Pattern "Get-ADUser" |
Select-Object -Unique Path
# Infrastructure pattern: Search logs for errors
function Search-ErrorLogs {
param (
[string]$Path = "C:\Logs",
[int]$Hours = 24
)
$cutoff = (Get-Date).AddHours(-$Hours)
Get-ChildItem -Path $Path -Filter *.log -Recurse |
Where-Object LastWriteTime -gt $cutoff |
Select-String -Pattern "error|exception|failed" -AllMatches |
Group-Object Path |
Select-Object @{N='File'; E={$_.Name}}, Count |
Sort-Object Count -Descending
}
File Attributes and Properties
# Get file info
$file = Get-Item -Path C:\file.txt
$file.Name
$file.FullName
$file.Extension
$file.Length # Size in bytes
$file.CreationTime
$file.LastWriteTime
$file.LastAccessTime
$file.Attributes
# Format file size
function Get-FileSize {
param ([long]$Bytes)
switch ($Bytes) {
{$_ -ge 1TB} { "{0:N2} TB" -f ($_ / 1TB); break }
{$_ -ge 1GB} { "{0:N2} GB" -f ($_ / 1GB); break }
{$_ -ge 1MB} { "{0:N2} MB" -f ($_ / 1MB); break }
{$_ -ge 1KB} { "{0:N2} KB" -f ($_ / 1KB); break }
default { "$_ Bytes" }
}
}
# Set file attributes
$file = Get-Item C:\file.txt
$file.Attributes = "ReadOnly"
$file.Attributes = "Hidden, System"
# Remove read-only
$file.Attributes = $file.Attributes -band (-bnot [System.IO.FileAttributes]::ReadOnly)
# Set timestamps
$file = Get-Item C:\file.txt
$file.CreationTime = Get-Date
$file.LastWriteTime = "2026-01-01 12:00:00"
# Get ACL (permissions)
Get-Acl -Path C:\file.txt
(Get-Acl C:\file.txt).Access # Detailed permissions
# Set ACL
$acl = Get-Acl C:\file.txt
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
"DOMAIN\User", "FullControl", "Allow"
)
$acl.SetAccessRule($rule)
Set-Acl -Path C:\file.txt -AclObject $acl
# Take ownership
takeown /f C:\file.txt /a # To Administrators group
# Directory size
function Get-DirectorySize {
param ([string]$Path)
$size = (Get-ChildItem -Path $Path -Recurse -File -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum).Sum
[PSCustomObject]@{
Path = $Path
SizeBytes = $size
SizeMB = [math]::Round($size / 1MB, 2)
SizeGB = [math]::Round($size / 1GB, 2)
}
}
Compression and Archives
# Create ZIP archive
Compress-Archive -Path C:\Folder\* -DestinationPath C:\archive.zip
# Create ZIP from multiple sources
Compress-Archive -Path file1.txt, file2.txt, C:\Folder -DestinationPath archive.zip
# Update existing archive
Compress-Archive -Path newfile.txt -DestinationPath archive.zip -Update
# Overwrite existing archive
Compress-Archive -Path C:\Folder\* -DestinationPath archive.zip -Force
# Set compression level
Compress-Archive -Path C:\Folder\* -DestinationPath archive.zip -CompressionLevel Optimal
# Options: Optimal, Fastest, NoCompression
# Extract ZIP
Expand-Archive -Path archive.zip -DestinationPath C:\Extracted
# Extract with overwrite
Expand-Archive -Path archive.zip -DestinationPath C:\Extracted -Force
# List ZIP contents
$zip = [System.IO.Compression.ZipFile]::OpenRead("archive.zip")
$zip.Entries | Select-Object FullName, Length, CompressedLength
$zip.Dispose()
# Extract single file from ZIP
$zip = [System.IO.Compression.ZipFile]::OpenRead("archive.zip")
$entry = $zip.Entries | Where-Object FullName -eq "specific-file.txt"
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, "output.txt", $true)
$zip.Dispose()
# Infrastructure pattern: Backup with timestamp
function New-Backup {
param (
[string]$SourcePath,
[string]$BackupFolder = "C:\Backups"
)
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$name = Split-Path -Path $SourcePath -Leaf
$archiveName = "${name}_${timestamp}.zip"
$archivePath = Join-Path $BackupFolder $archiveName
Compress-Archive -Path $SourcePath -DestinationPath $archivePath -CompressionLevel Optimal
return $archivePath
}
# Backup with rotation
function New-RotatingBackup {
param (
[string]$SourcePath,
[string]$BackupFolder,
[int]$KeepCount = 7
)
$archivePath = New-Backup -SourcePath $SourcePath -BackupFolder $BackupFolder
# Remove old backups
$name = Split-Path -Path $SourcePath -Leaf
Get-ChildItem -Path $BackupFolder -Filter "${name}_*.zip" |
Sort-Object CreationTime -Descending |
Select-Object -Skip $KeepCount |
Remove-Item -Force
return $archivePath
}
Configuration File Handling
# JSON config
$config = Get-Content -Path "config.json" -Raw | ConvertFrom-Json
$config.server.host = "new-server"
$config | ConvertTo-Json -Depth 10 | Set-Content "config.json"
# XML config
[xml]$config = Get-Content "app.config"
$appSettings = $config.configuration.appSettings
$setting = $appSettings.add | Where-Object key -eq "ServerName"
$setting.value = "new-server"
$config.Save("app.config")
# INI file parser
function Get-IniContent {
param ([string]$Path)
$ini = @{}
$section = ""
Get-Content $Path | ForEach-Object {
$line = $_.Trim()
if ($line -match '^\[(.+)\]$') {
$section = $Matches[1]
$ini[$section] = @{}
} elseif ($line -match '^([^=]+)=(.*)$') {
$key = $Matches[1].Trim()
$value = $Matches[2].Trim()
if ($section) {
$ini[$section][$key] = $value
}
}
}
return $ini
}
# Usage
$config = Get-IniContent "settings.ini"
$config["Database"]["Server"]
# Write INI file
function Set-IniContent {
param (
[hashtable]$Content,
[string]$Path
)
$lines = foreach ($section in $Content.Keys) {
"[$section]"
foreach ($key in $Content[$section].Keys) {
"$key=$($Content[$section][$key])"
}
""
}
$lines | Set-Content $Path
}
# Environment file (.env)
function Import-EnvFile {
param ([string]$Path)
Get-Content $Path | ForEach-Object {
if ($_ -match '^([^#][^=]+)=(.*)$') {
$name = $Matches[1].Trim()
$value = $Matches[2].Trim().Trim('"').Trim("'")
Set-Item -Path "Env:$name" -Value $value
}
}
}
# YAML (requires powershell-yaml module)
# Install-Module powershell-yaml
Import-Module powershell-yaml
$yaml = Get-Content "config.yaml" -Raw | ConvertFrom-Yaml
$yaml.settings.value = "new"
$yaml | ConvertTo-Yaml | Set-Content "config.yaml"
Registry Operations
# Navigate registry as drive
Set-Location HKLM:\SOFTWARE
Get-ChildItem HKCU:\SOFTWARE\Microsoft
# Read registry value
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion" -Name "ProgramFilesDir"
# Get all values in key
Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion"
# Read single value
(Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion").ProgramFilesDir
# Test if key exists
Test-Path "HKLM:\SOFTWARE\MyApp"
# Test if value exists
(Get-ItemProperty "HKLM:\SOFTWARE\MyApp" -ErrorAction SilentlyContinue).PSObject.Properties.Name -contains "Setting"
# Create registry key
New-Item -Path "HKLM:\SOFTWARE\MyApp"
# Create registry value
New-ItemProperty -Path "HKLM:\SOFTWARE\MyApp" -Name "Setting" -Value "data" -PropertyType String
# Property types: String, ExpandString, Binary, DWord, MultiString, QWord
# Set registry value
Set-ItemProperty -Path "HKLM:\SOFTWARE\MyApp" -Name "Setting" -Value "new value"
# Delete registry value
Remove-ItemProperty -Path "HKLM:\SOFTWARE\MyApp" -Name "Setting"
# Delete registry key
Remove-Item -Path "HKLM:\SOFTWARE\MyApp" -Recurse
# Export registry key
reg export "HKLM\SOFTWARE\MyApp" C:\backup.reg
# Import registry key
reg import C:\backup.reg
# Search registry
Get-ChildItem -Path HKLM:\SOFTWARE -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.GetValueNames() -contains "TargetSetting" }
# Common registry locations
# HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run # Startup programs
# HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run # User startup
# HKLM:\SYSTEM\CurrentControlSet\Services # Services
# HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion # OS info
Log File Parsing
# Parse structured log (space-delimited)
Get-Content app.log | ForEach-Object {
$parts = $_ -split '\s+'
[PSCustomObject]@{
Timestamp = $parts[0] + " " + $parts[1]
Level = $parts[2]
Message = $parts[3..($parts.Length-1)] -join ' '
}
}
# Parse CSV-like log
Import-Csv -Path app.log -Delimiter "|" -Header "Time", "Level", "Message"
# Parse JSON log (NDJSON)
Get-Content app.log | ForEach-Object {
$_ | ConvertFrom-Json
} | Where-Object level -eq "error"
# Parse IIS log
Get-Content "C:\inetpub\logs\LogFiles\W3SVC1\u_ex*.log" |
Where-Object { $_ -notmatch "^#" } |
ForEach-Object {
$fields = $_ -split ' '
[PSCustomObject]@{
Date = $fields[0]
Time = $fields[1]
ClientIP = $fields[2]
Method = $fields[3]
UriStem = $fields[4]
Status = $fields[5]
}
}
# Parse Windows Event Log format
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624
StartTime = (Get-Date).AddHours(-24)
} | Select-Object TimeCreated, @{
N='User'
E={$_.Properties[5].Value}
}, @{
N='LogonType'
E={$_.Properties[8].Value}
}
# Real-time log monitoring (tail -f equivalent)
Get-Content -Path C:\app.log -Wait -Tail 10
# With filtering
Get-Content -Path C:\app.log -Wait -Tail 0 | Where-Object { $_ -match "error" }
# Infrastructure pattern: Parse auth failures
function Get-AuthFailures {
param (
[string]$LogPath,
[int]$Hours = 24
)
$cutoff = (Get-Date).AddHours(-$Hours)
Get-Content $LogPath | ForEach-Object {
if ($_ -match "(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*Failed login.*user[=:]\s*(\S+).*from[=:]\s*([\d.]+)") {
$time = [datetime]$Matches[1]
if ($time -gt $cutoff) {
[PSCustomObject]@{
Time = $time
User = $Matches[2]
SourceIP = $Matches[3]
}
}
}
}
}
Common Gotchas
# WRONG: Get-Content returns array of lines
$json = Get-Content "file.json" | ConvertFrom-Json # Fails!
# CORRECT: Use -Raw for single string
$json = Get-Content "file.json" -Raw | ConvertFrom-Json
# WRONG: Forgetting about encoding
Set-Content -Path file.txt -Value "text" # Default encoding varies by PS version
# CORRECT: Specify encoding
Set-Content -Path file.txt -Value "text" -Encoding UTF8
# WRONG: Using > or >> (lose encoding control)
Get-Process > procs.txt # Uses system default encoding
# CORRECT: Use Out-File or Set-Content
Get-Process | Out-File procs.txt -Encoding UTF8
# WRONG: Path with spaces
Copy-Item C:\My Folder\file.txt D:\
# CORRECT: Quote paths with spaces
Copy-Item "C:\My Folder\file.txt" "D:\"
# WRONG: Assuming path exists
Get-ChildItem C:\NonExistent\* # Error
# CORRECT: Check first
if (Test-Path "C:\NonExistent") {
Get-ChildItem "C:\NonExistent\*"
}
# WRONG: Using Remove-Item -Recurse on locked files
Remove-Item C:\Folder -Recurse # Fails if files locked
# CORRECT: Handle errors
Remove-Item C:\Folder -Recurse -Force -ErrorAction SilentlyContinue
# WRONG: Assuming Select-String returns strings
$result = Select-String -Pattern "error" -Path *.log
$result # Returns MatchInfo objects!
# CORRECT: Access the matched text
$result.Line # The full line
$result.Matches.Value # Just the matched part
# WRONG: Registry path format
Get-ItemProperty -Path HKEY_LOCAL_MACHINE\SOFTWARE\... # Error!
# CORRECT: Use PSDrive format
Get-ItemProperty -Path HKLM:\SOFTWARE\...