Manual Test Runbook — B6: Client Self-Service Bootstrap
Owner: Sagar | Time: ~5 min (Part A + B offline) / +3 min (optional Part C live) | Sandbox: none required (read-only)
Overview
B6 is the prerequisite checker a prospective client runs in their own Azure tenant before a SnowOps engagement. It probes tooling (az, terraform) and the signed-in principal's Azure permissions, then prints a READY / NOT READY verdict with clear remediation for anything that blocks onboarding. It is read-only — it never creates or changes anything.
The asset is a TS app (apps/client-bootstrap/): a pure evaluator over an
EnvironmentSnapshot, behind a collector seam (FixtureCollector for
tests/offline, AzureCliCollector for the live az path). The single
client-facing entrypoint is bootstrap.sh.
The acceptance contract (asset catalogue): passes against a correctly-permissioned subscription, and fails with clear remediation output on a restricted principal.
Parts A + B verify both required scenarios offline via fixtures. Part C runs the live check against a real signed-in session.
Part A — Offline (no cloud, ~4 min)
A1. Build + typecheck + unit tests
Expect: 3 suites, 25 tests pass — version/tooling checks, the permission checks (Reader/Contributor cannot assign roles; missing app-management role; unregistered providers named in remediation; permission checks skip when not signed in), the readiness rule, and the markdown/status renderers.
A2. Scenario 1 — correctly-permissioned subscription → READY
node dist/index.js --snapshot examples/snapshot.ok.json \
--out-dir /tmp/b6ok --now 2026-06-01T00:00:00.000Z --fail-on-not-ready true
echo "exit=$?"
cat /tmp/b6ok/status.json
Expect: stderr ... READY — 8/8 passed, 0 failed, 0 warning(s); exit 0;
status.json has "ready": true, "blockers": [], "warnings": [].
A3. Scenario 2 — restricted service principal → NOT READY + remediation
node dist/index.js --snapshot examples/snapshot.restricted.json \
--out-dir /tmp/b6bad --now 2026-06-01T00:00:00.000Z --fail-on-not-ready true
echo "exit=$?"
cat /tmp/b6bad/status.json
Expect: exit 2; stderr lists three ✗ blockers each with a remediation line;
status.json has "ready": false and
"blockers": ["perm-role-assignment", "perm-entra-app-management", "perm-providers"],
"warnings": ["license-entra-p2"].
A4. Remediation is actionable
Expect: each blocker carries a concrete fix — grant Owner/User Access
Administrator, assign an app-management Entra role, and az provider register
for each missing namespace. Recommended (P2 license) is flagged
_[recommended]_, not a blocker.
Part B — Wrapper script (offline, ~1 min)
B1. bootstrap.sh runs the fixture path and gates on exit code
./bootstrap.sh --snapshot examples/snapshot.ok.json --out-dir /tmp/b6sh
echo "exit=$?" # expect 0 (READY)
./bootstrap.sh --snapshot examples/snapshot.restricted.json --out-dir /tmp/b6sh2
echo "exit=$?" # expect 2 (NOT READY)
Expect: the wrapper prints the verdict, the report locations, and propagates the CLI exit code (0 / 2).
Part C — Live check (optional, requires az login, ~3 min)
Run in any subscription you can sign into. Read-only — safe in prod.
C1. Live run against your session
Expect: the verdict reflects your real permissions. As an Owner with
Application Administrator you should see READY (or a P2 warning only); a
Reader-scoped session should report the same permission blockers as A3 with
remediation. No resources are created (az group list is unchanged).
Sign-off
| Field | Value |
|---|---|
| Result | ☐ PASS ☐ FAIL |
| Run by | |
| Date | |
| Commit | |
| Notes |