Free tech learning for everyone, from basics to pro

ScriptingProgramming and Application SecurityIntermediate

Writing safe scripts: testing, dry runs, logging and secrets

In short: A script can make the same mistake on a hundred devices in a minute. Make scripts safe by linting them, testing in a lab, offering a dry-run mode, limiting scope, logging every action and keeping secrets out of code.

Why safety matters more in automation

A typing error at the command line affects one device. The same error inside a loop affects every device in the list. Automation multiplies both good and bad changes, so scripts that change things need more care than one-off commands. The good news is that a few habits remove most of the risk.

Safe script lifecycleWritesmall, clearLintshellcheck, PSSATestlab firstDry runpreview changesRunwith loggingReviewlogs, results
Figure 1. Safe scripts go through linting, lab testing and a dry run before touching production, then get reviewed.

Defensive coding

  • Fail fast: in Bash use set -euo pipefail; in PowerShell set $ErrorActionPreference = 'Stop' and use try/catch; in Python let exceptions surface rather than hiding them.
  • Validate input: check that an IP address is valid, a file exists and a list is not empty before you act on it.
  • Limit scope: accept a target list as an argument rather than "all devices"; process a small batch first.
  • Be idempotent: running the script twice should leave things the same as running it once, for example by checking whether a VLAN exists before creating it.
  • Lint: run shellcheck for Bash, PSScriptAnalyzer for PowerShell and ruff or pylint for Python.

Dry runs and confirmation

A dry run shows what the script would do without doing it. Build one in from the start.

#!/usr/bin/env bash
set -euo pipefail
DRY_RUN=1
[[ "${1:-}" == "--apply" ]] && DRY_RUN=0

run() {
  if (( DRY_RUN )); then echo "DRY-RUN: $*"; else "$@"; fi
}

for f in /var/backups/net/*.cfg; do
  [[ -e "$f" ]] || continue
  if [[ $(find "$f" -mtime +90) ]]; then
    run rm -- "$f"
  fi
done
(( DRY_RUN )) && echo "Nothing deleted. Re-run with --apply to make changes."

Making the safe mode the default means a mistyped command does no harm. PowerShell gives you this for free: add [CmdletBinding(SupportsShouldProcess)] to a script and wrap changes in if ($PSCmdlet.ShouldProcess($target)) { ... } to get -WhatIf and -Confirm support.

Logging

Every run that changes something should leave a record: when it ran, who ran it, which targets it touched and what happened. Write timestamped lines to a log file or syslog, and use Start-Transcript in PowerShell or the logging module in Python. Logs help you troubleshoot, prove what changed during an audit and roll back accurately.

import logging
logging.basicConfig(filename="vlan_push.log", level=logging.INFO,
                    format="%(asctime)s %(levelname)s %(message)s")
logging.info("Adding VLAN 30 to %s", "sw-access-01")

Keeping secrets out of scripts

Handling secretsAvoidPasswords in the scriptSecrets in Git historyKeys on the command linePrinting secrets to logsPreferPrompt at run timeSecret vault or managerEnvironment variablesSSH keys with passphrase
Figure 2. Keep secrets out of code, history and logs; fetch them at run time from a protected source.
import os, getpass
user = os.environ.get("NET_USER", "netops")
password = os.environ.get("NET_PASS") or getpass.getpass("Device password: ")

For shared or scheduled automation, use a proper secret store such as HashiCorp Vault, Azure Key Vault, AWS Secrets Manager or Ansible Vault, and give the script an identity with read access to only the secrets it needs. Never print secrets in logs, and add credential files to .gitignore.

  1. Test in a lab or on a single non-critical device.
  2. Run the dry run and read the planned changes.
  3. Apply to a small batch during an approved change window.
  4. Check the logs and results, then continue with the rest.

Tip: treat automation scripts like any other production change: peer review, change approval and a written rollback plan.

Practice

Why make dry-run the default mode?
So that running the script by accident, or with a typo, shows planned changes instead of making them.
What does idempotent mean for a script?
Running it more than once has the same effect as running it once, because it checks the current state before changing anything.
Name two safe ways to give a script a password.
Prompt for it at run time, or read it from an environment variable or a secret manager such as Vault or Key Vault.

Key takeaways

  • Automation multiplies mistakes, so build safety in from the start.
  • Fail fast, validate input, limit scope and lint your code.
  • Default to a dry run and require an explicit flag to apply.
  • Log every change with time, user, target and result.
  • Keep secrets in prompts, environment variables or a vault, never in code.
Tracks your learning on this device only. See your progress on the learning paths.

About the author

Mohamed Fayas

Founder of FM TecHub. CCNA certified, with a BTEC HND in Network Engineering, and BEng (Hons) in Computer Networking and Cloud Security. He writes practical, plain-English guides on networking, security and cloud.

More about the author

Original content written by Mohamed Fayas for FM TecHub. © 2026 FM TecHub. You're welcome to share a link to this page; please don't copy the text elsewhere without permission.

Stay up to date by email

New notes, tools and security news from FM TecHub, explained simply. Free.

We only use your email to send these updates. See our privacy policy.