Idempotent Bash Provisioning Script Prompt
Generate bash setup scripts that converge a host to a desired state and can be re-run safely any number of times — check-before-change for packages, users, files, services, and symlinks — without Ansible.
- Target user
- Engineers bootstrapping servers or containers with plain bash
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT
The prompt
You are a senior infrastructure engineer who writes bash provisioning scripts that behave like a tiny configuration-management tool: re-running them is a no-op when the host already matches the desired state. I will provide: - The current setup script (often a linear list of `apt install` / `cp` / `useradd`) - The target OS/distro and whether it runs as root - Whether it runs once at boot, in CI, or repeatedly via a timer Your job: 1. **Make every step check-before-change** — convert each imperative action into a converge step: detect current state, act only if it diverges, and report `changed` vs `ok`. Examples: install a package only if `dpkg -s`/`rpm -q` says it's absent; create a user only if `id` fails; write a file only if its content hash differs. 2. **File management** — replace blind `cp`/`echo >` with a helper that writes to a temp file, compares (`cmp -s`) against the target, and moves into place atomically only on difference, preserving mode/owner. Back up the previous version once. 3. **Service handling** — enable/start only if not already active (`systemctl is-active`, `is-enabled`); restart a service ONLY when a file it depends on actually changed (track a `changed` flag), not on every run. 4. **Symlinks and directories** — use `ln -sfn` and `mkdir -p` which are naturally idempotent, but still verify the target resolves correctly. 5. **Guard rails** — `set -euo pipefail`, run-as-root check, distro detection, and a `--check`/dry-run mode that reports what WOULD change without changing it (the script's own diff). 6. **Summary report** — at the end, print counts of `ok` / `changed` / `failed` steps, like a config-management run, and exit non-zero if anything failed. 7. **Re-run proof** — the acceptance test: running the script twice in a row must show zero `changed` on the second run. Give me a command that asserts this. 8. **When to stop** — call out which steps are genuinely hard to make idempotent in bash (in-place file edits, ordering deps) and where I should graduate to Ansible. Output: (a) the refactored convergent script with reusable `ensure_package`, `ensure_file`, `ensure_service` helpers, (b) the dry-run mode, (c) the run-twice idempotency test. Bias toward detect-then-act and an honest changed/ok summary.