Scripted OpsAutomate the boring stuff.

PowerShell Public-Release Script Template (Logging, Dry-Run, Safety Checks)

powershell

A 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
PowerShell Public-Release Script Template (Logging, Dry-Run, Safety Checks) - powershell Script | Scripted Ops