PowerShell Public-Release Script Template (Logging, Dry-Run, Safety Checks)
powershellA reusable PowerShell template designed for scripts you plan to publish publicly. Includes strict mode, parameter validation, WhatIf/DryRun behavior, structured logging, safe defaults, and a clean header you can reuse across ScriptedOps scripts.
powershelltemplateopen-sourceportfoliobest practicesloggingsafetyautomation
Script Code
PowerShellpowershell
<#
.SYNOPSIS
Template for PowerShell scripts intended for public release.
.DESCRIPTION
Use this template to publish safe, reusable automation scripts.
Includes:
- StrictMode + error handling
- Parameter validation
- SupportsShouldProcess (WhatIf/Confirm)
- Optional DryRun mode (no changes)
- Structured logging to file and console
- Safe defaults and environment-agnostic placeholders
.NOTES
Author: ScriptedOps
License: MIT (recommended for public scripts)
Repo: https://scriptedops.com
SECURITY / PRIVACY
- Do not hardcode internal domains, server names, or credentials
- Do not include real data samples in comments or output examples
- Use example.com / placeholder values in docs
.EXAMPLE
# Preview actions without making changes
.\Script.ps1 -Target "C:\Temp" -DryRun
.EXAMPLE
# Use PowerShell native -WhatIf behavior
.\Script.ps1 -Target "C:\Temp" -WhatIf
.EXAMPLE
# Run with logging
.\Script.ps1 -Target "C:\Temp" -LogPath "C:\Logs\script.log"
#>
[CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
# A primary target. Keep names generic and environment-agnostic.
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$Target,
# Optional: path to a log file. If omitted, logging is console-only.
[Parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]
[string]$LogPath,
# Optional: dry-run mode. No changes are made even if -WhatIf is not supplied.
[Parameter(Mandatory=$false)]
[switch]$DryRun,
# Optional: increase verbosity for troubleshooting.
[Parameter(Mandatory=$false)]
[switch]$VerboseMode
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
# ----------------------------
# Logging helpers
# ----------------------------
function New-LogLine {
param(
[Parameter(Mandatory=$true)][string]$Level,
[Parameter(Mandatory=$true)][string]$Message
)
$ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
return "$ts [$Level] $Message"
}
function Write-Log {
param(
[Parameter(Mandatory=$true)][ValidateSet('INFO','WARN','ERROR','DEBUG')][string]$Level,
[Parameter(Mandatory=$true)][string]$Message
)
$line = New-LogLine -Level $Level -Message $Message
# Console
switch ($Level) {
'ERROR' { Write-Error $line }
'WARN' { Write-Warning $line }
'DEBUG' { if ($VerboseMode) { Write-Verbose $line } else { Write-Host $line } }
default { Write-Host $line }
}
# File
if ($LogPath) {
$dir = Split-Path -Parent $LogPath
if ($dir -and -not (Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
}
$line | Out-File -FilePath $LogPath -Append -Encoding utf8
}
}
# ----------------------------
# Validation + environment checks
# ----------------------------
function Assert-TargetExists {
param([Parameter(Mandatory=$true)][string]$Path)
if (-not (Test-Path -LiteralPath $Path)) {
throw "Target does not exist: $Path"
}
}
# ----------------------------
# Main action function
# ----------------------------
function Invoke-Main {
Write-Log -Level INFO -Message "=== Start ==="
Write-Log -Level INFO -Message "Target=$Target DryRun=$($DryRun.IsPresent) WhatIf=$($WhatIfPreference) Confirm=$($ConfirmPreference) LogPath=$LogPath"
# Example validation (adjust for your script)
Assert-TargetExists -Path $Target
# Example "work items" (replace with your real logic)
$items = Get-ChildItem -LiteralPath $Target -ErrorAction Stop
Write-Log -Level INFO -Message "Found $($items.Count) item(s) under target."
foreach ($item in $items) {
# Describe the action in human terms
$actionDescription = "Process item: $($item.FullName)"
# SupportsShouldProcess integrates PowerShell -WhatIf/-Confirm
if ($PSCmdlet.ShouldProcess($item.FullName, $actionDescription)) {
if ($DryRun) {
Write-Log -Level INFO -Message "DRY RUN: Would process $($item.FullName)"
continue
}
try {
# === PLACEHOLDER ACTION ===
# Replace this with your real operation:
# e.g. Remove-Item, Set-Acl, Disable-ADAccount, Invoke-RestMethod, etc.
Write-Log -Level INFO -Message "Processing: $($item.FullName)"
# Do-Thing -InputObject $item
} catch {
Write-Log -Level ERROR -Message "Failed processing $($item.FullName): $($_.Exception.Message)"
}
} else {
Write-Log -Level INFO -Message "Skipped by ShouldProcess: $($item.FullName)"
}
}
Write-Log -Level INFO -Message "=== End ==="
}
# ----------------------------
# Entrypoint
# ----------------------------
try {
Invoke-Main
exit 0
} catch {
Write-Log -Level ERROR -Message "Fatal error: $($_.Exception.Message)"
exit 1
}Get new scripts weekly.
Subscribe to receive fresh PowerShell scripts, automation tips, and IT workflow improvements.
Subscribe to Newsletter