Manual Test Runbook — J5: Managed Grafana Dashboards-as-Code¶
Owner: Sagar | Time: ~8 min (Parts A + B offline) · +25 min (optional Part C integration apply) | Sandbox: snowops-sandbox-01
Promotes J5 (
modules/azure/grafana-dashboards/) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are offline ($0). Part C stands up a real Managed Grafana instance (~$9/mo prorated — a few cents for the test window), deploys the dashboards, confirms panels query the J1 workspace, and destroys.
Prerequisites¶
- Sandbox subscription access active (PIM activated if required)
-
az logindone; sandbox subscription selected;az extension add -n amg - Identity has Contributor + Owner/User Access Administrator on the sandbox sub (RBAC grants)
-
SNOWOPS_SANDBOX_SUBSCRIPTION_ID+SNOWOPS_SANDBOX_TENANT_IDexported - Local tooling:
terraform >= 1.6,go >= 1.22,az CLI >= 2.50,python3,jq - Working directory: repo root
Steps¶
Part A — terraform fmt + validate + dashboards gate (offline, ~4 min)¶
- Module + example:
terraform -chdir=modules/azure/grafana-dashboards fmt -recursive -check
terraform -chdir=modules/azure/grafana-dashboards init -backend=false -input=false
terraform -chdir=modules/azure/grafana-dashboards validate
terraform -chdir=modules/azure/grafana-dashboards/examples/basic init -backend=false -input=false
terraform -chdir=modules/azure/grafana-dashboards/examples/basic validate
Expected: Success! for both.
- Dashboards-as-code offline gate:
Expected: All 2 dashboard(s) valid: snowops-reliability-cost.json, snowops-security-posture.json.
- Offline Terratest case:
Expected: PASS — instance + Monitoring Reader (subscription + workspace scopes) + Grafana Admin/Editor/Viewer RBAC fan-out, offline.
Part B — full Terratest suite (offline, ~5 min)¶
bash cd tests/terratest && go test -count=1 -timeout 15m ./...
Expected: the full suite green (the new TestGrafanaDashboardsValidate included).
Part C — integration apply + dashboard deploy (sandbox, ~25 min, ~$0)¶
- Apply the example (or fixture) against a real workspace. Point
monitoring_reader_scopesat a REAL J1 workspace ARM ID + the sandbox sub:
cd modules/azure/grafana-dashboards/examples/basic
terraform init -input=false
terraform apply -auto-approve \
-var "subscription_scope=/subscriptions/$SNOWOPS_SANDBOX_SUBSCRIPTION_ID" \
-var "workspace_id=<REAL J1 workspace ARM ID>" \
-var 'grafana_admin_object_ids=["<your object id>"]'
NAME=$(terraform output -raw grafana_name)
- Deploy the dashboards-as-code and confirm:
cd ../..
./scripts/deploy-dashboards.sh "$NAME" snowops-grafana-example-rg "<REAL J1 workspace ARM ID>"
# Confirm both dashboards landed:
az grafana dashboard list --name "$NAME" --resource-group snowops-grafana-example-rg \
--query "[].{title:title, uid:uid}" -o table
Expected: SnowOps — Security Posture and SnowOps — Reliability & Cost
present. Open the endpoint (terraform output grafana_endpoint), pick the
Azure Monitor datasource, and confirm at least one panel returns data from the
J1 workspace (e.g. failed sign-ins / activity).
- Destroy:
cd examples/basic
terraform destroy -auto-approve \
-var "subscription_scope=/subscriptions/$SNOWOPS_SANDBOX_SUBSCRIPTION_ID" \
-var "workspace_id=<REAL J1 workspace ARM ID>" \
-var 'grafana_admin_object_ids=["<your object id>"]'
Expected: clean destroy — the instance + every role assignment removed. The J1 workspace (owned elsewhere) is untouched.
Pass criteria¶
- Part A — module + example validate; dashboards gate passes;
TestGrafanaDashboardsValidatepasses - Part B — full offline suite passes
- (Part C) instance applies; both dashboards deploy; a panel queries the J1 workspace; destroys clean
- All test resources removed
Failure mode¶
Panels return Unauthorized — the instance identity wasn't granted Monitoring
Reader on the queried scope. Add the scope to monitoring_reader_scopes. A
dashboard renders empty when the queried table isn't flowing into the workspace
yet (e.g. SecurityIncident needs J3 onboarded) — the offline gate validates
structure, not data presence. Both documented in the module README.
Cost impact¶
Managed Grafana (Standard) ≈ $9/instance/month base + per-active-user, prorated — a few cents for the Part C window. No per-dashboard cost. Log Analytics query cost is the usual per-GB on the J1 workspace.
Removal path¶
terraform destroy (Part C step 7) removes the instance and all role
assignments J5 created; dashboards are destroyed with the instance. The committed
JSON under dashboards/ is the source of truth and is untouched. Verified clean
in Part C.
Sign-Off¶
| Field | Value |
|---|---|
| Part A (validate + dashboards gate) | ☐ PASS |
| Part B (offline suite) | ☐ PASS |
| Part C (integration apply + deploy) | ☐ PASS / ☐ skipped |
| Tester | |
| Date | |
| Result | ☐ PASS |