Skip to content

Manual Test Runbook — X7: Sandbox Cleanup Workflow

Owner: Sagar  |  Time: ~5 min (Parts A + B offline) · +10 min (optional Part C live drill)  |  Sandbox: snowops-sandbox-01

Promotes X7 (sandbox/cleanup/ + .github/workflows/sandbox-cleanup.yml) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are 100% offline (shellcheck + the selection unit test + YAML lint). Part C is a live sandbox drill (~$0): seed three throwaway RGs, run the script, confirm the ephemeral one is deleted and the protected / non-ephemeral ones survive.


Prerequisites

  • Local tooling: bash, python3, shellcheck, ruby (YAML lint)
  • (Part C only) az login done; az account show confirms the sandbox subscription is selected
  • (Part C only) Identity has Contributor on the sandbox sub (RG create + delete)
  • Working directory: repo root

Steps

Part A — shellcheck + offline selection test (offline, ~3 min)

  1. Lint both scripts:
shellcheck sandbox/cleanup/cleanup-ephemeral.sh sandbox/cleanup/test/run-tests.sh

Expected: no output (clean).

  1. Run the offline selection test (feeds a fixture RG list, dry-run, asserts the plan):
bash sandbox/cleanup/test/run-tests.sh

Expected: ==> OK: all X7 selection assertions passed — 12 assertions, all ✓. It proves: the two ephemeral test RGs are selected; the sandbox baseline RG (protected glob), the state RG (ephemeral=false), a prod RG (no ephemeral tag), a too-young RG (age guard), and NetworkWatcherRG are all preserved.

Part B — workflow YAML lint (offline, ~2 min)

  1. bash ruby -ryaml -e "YAML.load_file('.github/workflows/sandbox-cleanup.yml'); puts 'parses OK'" # Optional, if installed: actionlint .github/workflows/sandbox-cleanup.yml

Expected: parses OK (and actionlint clean if run).

Part C — live sandbox drill (optional, ~10 min, ~$0)

Empty resource groups are free. This drill creates three, runs the cleanup, and confirms the selection behaves on real Azure exactly as the unit test predicts.

  1. Seed three throwaway resource groups in the sandbox sub:
# (a) an ephemeral test RG — should be DELETED
az group create -n snowops-x7drill-test -l eastus \
  --tags ephemeral=true Owner=terratest

# (b) a name that matches the protected glob — should be PRESERVED
az group create -n snowops-sandbox-x7drill-rg -l eastus \
  --tags ephemeral=true Owner=snowops

# (c) a non-ephemeral RG — should be PRESERVED
az group create -n snowops-x7drill-keep -l eastus \
  --tags ephemeral=false Owner=snowops
  1. Preview the plan (dry-run, min-age 0 so the brand-new RG isn't age-skipped):
sandbox/cleanup/cleanup-ephemeral.sh --dry-run --min-age-hours 0

Expected in the output: - WOULD DELETE: snowops-x7drill-test - SKIP (protected): snowops-sandbox-x7drill-rg - SKIP (not ephemeral): snowops-x7drill-keep

  1. Run for real:
sandbox/cleanup/cleanup-ephemeral.sh --min-age-hours 0
  1. Confirm the result (deletes are --no-wait; allow ~1 min):
az group exists -n snowops-x7drill-test          # → false (deleted)
az group exists -n snowops-sandbox-x7drill-rg     # → true  (preserved)
az group exists -n snowops-x7drill-keep           # → true  (preserved)
  1. (Optional) Trigger the workflow itself in dry-run from the Actions tab (sandbox-cleanup → Run workflow → dry_run = true) and confirm the step summary lists the same plan.

Pass criteria

  • Part A — shellcheck clean; offline test passes all 12 assertions
  • Part B — workflow YAML parses
  • (Part C) the ephemeral drill RG is deleted; the protected-name RG and the non-ephemeral RG both survive
  • All drill resources removed

Teardown

# Part C: clean up any survivors from the drill.
az group delete -n snowops-sandbox-x7drill-rg --yes --no-wait 2>/dev/null || true
az group delete -n snowops-x7drill-keep --yes --no-wait 2>/dev/null || true
az group delete -n snowops-x7drill-test --yes --no-wait 2>/dev/null || true

Sign-off

  • Tester: _  |  Date: _  |  Result: PASS / FAIL / N/A
  • Notes: