Examples & Recipes¶
Real-world, copy-paste ready PowerShell scripts for common automation tasks.
System Administration¶
Clean up temp files older than 30 days¶
$dirs = @($env:TEMP, "C:\Windows\Temp")
$cutoff = (Get-Date).AddDays(-30)
$deleted = 0
$errors = 0
foreach ($dir in $dirs) {
Get-ChildItem $dir -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $cutoff } |
ForEach-Object {
try {
Remove-Item $_.FullName -Force
$deleted++
} catch {
$errors++
}
}
}
Write-Host "Deleted $deleted files. Errors: $errors"
System inventory report (CSV)¶
$computers = Get-Content .\servers.txt # or @('server01','server02')
$report = Invoke-Command -ComputerName $computers -ScriptBlock {
$os = Get-CimInstance Win32_OperatingSystem
$cpu = Get-CimInstance Win32_Processor | Select-Object -First 1
$disk = Get-Volume C
[PSCustomObject]@{
Computer = $env:COMPUTERNAME
OS = $os.Caption
Build = $os.BuildNumber
CPU = $cpu.Name
Cores = $cpu.NumberOfCores
RAM_GB = [math]::Round($os.TotalVisibleMemorySize/1MB, 1)
FreeRAM_GB = [math]::Round($os.FreePhysicalMemory/1MB, 1)
Disk_GB = [math]::Round($disk.Size/1GB, 1)
FreeDisk_GB= [math]::Round($disk.SizeRemaining/1GB, 1)
}
} -ErrorAction SilentlyContinue
$report | Sort-Object Computer | Export-Csv .\inventory-$(Get-Date -f yyyyMMdd).csv -NoTypeInformation
Write-Host "Report saved."
Service monitor: alert when a service stops¶
$ServiceName = "Spooler"
$CheckEvery = 30 # seconds
$EmailTo = "admin@example.com"
while ($true) {
$svc = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -ne 'Running') {
Write-Warning "$(Get-Date) — $ServiceName stopped! Attempting restart..."
try {
Start-Service -Name $ServiceName
Write-Host "$(Get-Date) — $ServiceName restarted successfully." -ForegroundColor Green
} catch {
Write-Error "Failed to restart ${ServiceName}: $_"
# Send-MailMessage -To $EmailTo -Subject "Service down: $ServiceName" ...
}
}
Start-Sleep -Seconds $CheckEvery
}
File & Data Processing¶
Find duplicate files by hash¶
$folder = "C:\Users\Alice\Documents"
Get-ChildItem $folder -Recurse -File |
Group-Object -Property {
Get-FileHash $_.FullName -Algorithm MD5 | Select-Object -ExpandProperty Hash
} |
Where-Object Count -gt 1 |
ForEach-Object {
Write-Host "Duplicates:" -ForegroundColor Yellow
$_.Group | Select-Object FullName, Length, LastWriteTime | Format-Table
}
Bulk rename files¶
# Add a date prefix to all .jpg files in a folder
$folder = "C:\Photos"
$date = Get-Date -Format "yyyy-MM-dd"
Get-ChildItem $folder -Filter *.jpg |
Where-Object { $_.Name -notmatch "^\d{4}-\d{2}-\d{2}" } |
Rename-Item -NewName { "${date}_$($_.Name)" } -WhatIf
# Remove -WhatIf to execute
Convert JSON to CSV¶
$json = Get-Content .\data.json -Raw | ConvertFrom-Json
# If the JSON is an array of flat objects:
$json | Export-Csv .\data.csv -NoTypeInformation
Write-Host "Converted to CSV."
# If it has nested objects, flatten first:
$json | ForEach-Object {
[PSCustomObject]@{
Id = $_.id
Name = $_.name
Email = $_.contact.email # nested property
}
} | Export-Csv .\data-flat.csv -NoTypeInformation
Parse a log file and aggregate errors¶
$logPath = "C:\Logs\app.log"
$pattern = "^(?<ts>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(?<level>\w+)\] (?<msg>.+)$"
$entries = Get-Content $logPath | ForEach-Object {
if ($_ -match $pattern) {
[PSCustomObject]@{
Timestamp = [datetime]$Matches.ts
Level = $Matches.level
Message = $Matches.msg
}
}
}
# Error summary
$entries |
Where-Object Level -eq "ERROR" |
Group-Object -Property { $_.Message -replace "\d+","#" } | # normalize numbers
Sort-Object Count -Descending |
Select-Object Count, Name |
Format-Table -AutoSize
Networking¶
Check if a list of URLs are reachable¶
$urls = @(
"https://google.com",
"https://github.com",
"https://api.myapp.example.com/health",
"https://offline.example.com"
)
$urls | ForEach-Object {
$url = $_
try {
$resp = Invoke-WebRequest -Uri $url -TimeoutSec 5 -ErrorAction Stop
[PSCustomObject]@{ URL = $url; Status = $resp.StatusCode; OK = $true }
} catch {
[PSCustomObject]@{ URL = $url; Status = $_.Exception.Message; OK = $false }
}
} | Format-Table URL, Status, OK -AutoSize
Download all files from a remote directory listing¶
$baseUrl = "https://downloads.example.com/releases/"
$destDir = ".\downloads"
$pattern = 'href="(v\d+\.\d+\.\d+\.zip)"'
New-Item $destDir -ItemType Directory -Force | Out-Null
$page = (Invoke-WebRequest $baseUrl).Content
$files = [regex]::Matches($page, $pattern) | ForEach-Object { $_.Groups[1].Value }
foreach ($file in $files) {
$dest = Join-Path $destDir $file
if (-not (Test-Path $dest)) {
Write-Host "Downloading $file..."
Invoke-WebRequest -Uri "$baseUrl$file" -OutFile $dest
}
}
Write-Host "Done. $($files.Count) files."
Active Directory (requires RSAT / ActiveDirectory module)¶
Report users who haven't logged in for 90 days¶
Import-Module ActiveDirectory
$cutoff = (Get-Date).AddDays(-90)
Get-ADUser -Filter { LastLogonDate -lt $cutoff -and Enabled -eq $true } `
-Properties LastLogonDate, Department, Manager |
Select-Object SamAccountName, Name, LastLogonDate, Department,
@{Name='Manager'; Expression={ (Get-ADUser $_.Manager).Name }} |
Sort-Object LastLogonDate |
Export-Csv .\inactive-users.csv -NoTypeInformation
Write-Host "Report saved to inactive-users.csv"
Bulk create users from CSV¶
# CSV: Name,SamAccountName,Department,Password
Import-Csv .\new-users.csv | ForEach-Object {
$pwd = ConvertTo-SecureString $_.Password -AsPlainText -Force
$params = @{
Name = $_.Name
SamAccountName = $_.SamAccountName
AccountPassword = $pwd
Enabled = $true
Path = "OU=Users,DC=example,DC=com"
Department = $_.Department
ChangePasswordAtLogon = $true
}
try {
New-ADUser @params
Write-Host "Created: $($_.SamAccountName)" -ForegroundColor Green
} catch {
Write-Warning "Failed to create $($_.SamAccountName): $_"
}
}
Automation Utilities¶
Retry a command with exponential backoff¶
function Invoke-WithRetry {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[scriptblock] $ScriptBlock,
[int] $MaxAttempts = 5,
[int] $InitialDelaySeconds = 1
)
$attempt = 0
do {
$attempt++
try {
return (& $ScriptBlock)
} catch {
if ($attempt -ge $MaxAttempts) { throw }
$delay = $InitialDelaySeconds * [math]::Pow(2, $attempt - 1)
Write-Warning "Attempt $attempt failed: $_ Retrying in ${delay}s..."
Start-Sleep -Seconds $delay
}
} while ($true)
}
# Usage:
Invoke-WithRetry { Invoke-RestMethod https://unstable.api.example.com/data }
Run a script block with a timeout¶
function Invoke-WithTimeout {
param(
[scriptblock] $ScriptBlock,
[int] $TimeoutSeconds = 30
)
$job = Start-Job -ScriptBlock $ScriptBlock
if (Wait-Job $job -Timeout $TimeoutSeconds) {
Receive-Job $job
} else {
Stop-Job $job
throw "Operation timed out after ${TimeoutSeconds} seconds"
}
Remove-Job $job -Force
}
Invoke-WithTimeout -ScriptBlock { Start-Sleep 5; "Done" } -TimeoutSeconds 10
Send a Teams notification webhook¶
function Send-TeamsMessage {
param(
[string] $WebhookUrl,
[string] $Title,
[string] $Text,
[ValidateSet("default","good","warning","attention")]
[string] $Color = "default"
)
$body = @{
"@type" = "MessageCard"
"@context" = "http://schema.org/extensions"
themeColor = switch ($Color) {
"good" { "00FF00" }
"warning" { "FFA500" }
"attention" { "FF0000" }
default { "0078D7" }
}
summary = $Title
sections = @(@{ activityTitle = $Title; activityText = $Text })
} | ConvertTo-Json -Depth 5
Invoke-RestMethod -Uri $WebhookUrl -Method Post -Body $body -ContentType "application/json"
}
Send-TeamsMessage -WebhookUrl $env:TEAMS_WEBHOOK `
-Title "Deployment Complete" -Text "Version 2.3.1 deployed to production." -Color good
Archive and compress logs older than N days¶
param(
[string] $LogDir = "C:\Logs",
[string] $ArchDir = "C:\LogArchive",
[int] $DaysOld = 7
)
$cutoff = (Get-Date).AddDays(-$DaysOld)
$stamp = Get-Date -Format "yyyy-MM-dd"
$dest = Join-Path $ArchDir "logs-$stamp.zip"
New-Item $ArchDir -ItemType Directory -Force | Out-Null
$toArchive = Get-ChildItem $LogDir -Filter *.log |
Where-Object { $_.LastWriteTime -lt $cutoff }
if ($toArchive) {
Compress-Archive -Path $toArchive.FullName -DestinationPath $dest
$toArchive | Remove-Item -Force
Write-Host "Archived $($toArchive.Count) logs to $dest"
} else {
Write-Host "No logs older than $DaysOld days found."
}