SnowOps — Project State¶
Current Phase¶
- M1: Sign-offs in flight
- M2a: Core Baseline FUNCTIONALLY CODE-COMPLETE (v0.40) — every non-postponed asset is 🟦+. W-series postponed (D35).
- M2b: 14-asset core CODE-COMPLETE — E0 (v0.42) + V2 + V3 (v0.43) + S1 (v0.44) + S2 (v0.45) + L1 (v0.46) + L2 (v0.47) + L4 (v0.48) + F12 (v0.49) + B6 (v0.50) + F11 (v0.51) shipped; K1 + K2 (PR #12) + D5 (PR #13) landed externally and merged. 14/14 in-repo — nothing external remaining. Focus now: runbook sign-offs.
- GTM: Track A COMPLETE (v0.34) — 39 files under
docs/gtm/. Awaiting human sign-offs.
Last Updated: 2026-06-10 (v0.71) — D71: M (data protection & privacy) series completed — M4 Purview + M5 DLP + M7 GDPR/CCPA evidence code-complete. Every M1–M7 asset is now 🟦+.
Prior: 2026-06-10 (v0.70) — D70 completed the H (identity) series (H4 SCIM + H6 access reviews).
Prior: 2026-06-10 (v0.69) — D69 batch (N1 + N2 + N7 + V1 + V4) finished the N (network) and V (documentation) series.
Prior: 2026-06-10 (v0.68) — D68 batch (I4 + I6 + I7 + U4 + U5) finished the I / U series. · 2026-06-09 (v0.67) — D66 batch (V5 + J8 + J7 + R3 + K5) finished the J / R / K series + the D65 client-posture pair.
In Flight¶
| Item | Status | Notes |
|---|---|---|
| M2b 14-asset core | 🟦 code-complete | all 14 in-repo (E0+V2+V3+S1+S2+L1+L2+L4+F12+B6+F11 + K1+K2+D5); nothing external remaining |
| N3 + N4 (WAF Policy + DDoS) | 🟦 code-complete (v0.60) | N3 runbook N3.md · N4 runbook N4.md — sign-offs pending |
| E2–E6 (E series) | 🟦 code-complete (v0.61) | E2 Vanta · E3 Drata stub · E4 Resource Graph · E5 Evidence Sync · E6 Access Review — sign-offs pending |
| D60 batch (J3/K3/J5/K4/R4) | 🟦 code-complete (v0.66) | J3 Sentinel · K3 SOAR · J5 Grafana · K4 PIR · R4 CAB — runbooks J3/K3/J5/K4/R4, sign-offs pending |
| D66 batch (V5/J8/J7/R3/K5) | 🟦 code-complete (v0.67) | V5 posture report · J8 client dashboard · J7 cost-controlled logs · R3 emergency change · K5 tabletop — runbooks V5/J8/J7/R3/K5; finishes the J/R/K series; sign-offs pending |
| D68 batch (I4/I6/I7/U4/U5) | 🟦 code-complete (v0.68) | I4 DAST · I6 patch-compliance · I7 CVE-triage+SLA · U4 FinOps dashboard · U5 cost-anomaly — runbooks I4/I6/I7/U4/U5; finishes the I/U series; sign-offs pending |
| D69 batch (N1/N2/N7/V1/V4) | 🟦 code-complete (v0.69) | N1 landing-zone connectivity · N2 Firewall Premium · N7 zero-trust ref · V1 policy templates · V4 compliance-manual — runbooks N1/N2/N7/V1/V4; finishes the N/V series; sign-offs pending |
| D70 H-series finish (H4/H6) | 🟦 code-complete (v0.70) | H4 SCIM provisioning · H6 Entra access reviews — runbooks H4/H6; completes the H identity series; sign-offs pending |
| D71 M-series finish (M4/M5/M7) | 🟦 code-complete (v0.71) | M4 Purview baseline · M5 DLP policy pack · M7 data inventory + DSAR — runbooks M4/M5/M7; completes the M data-protection series; sign-offs pending |
| M2a runbook sign-offs | 🟧 | X7+U+N+M+J offline parts are ~5 min each |
| GTM Track A | 🟦 complete | Human sign-offs pending (see §0) |
Open Issues / Tech Debt (v0.55 repo review — 2026-06-04)¶
Surfaced by a full-repo review. Full detail + IDs in docs/context/10-gap-register.md (G15–G20). Fixed in the same pass except G18 (seeded, ongoing).
| # | Issue | Severity | Status |
|---|---|---|---|
| G15 | B1 test red — apps/github-onboarder/src/load-template.test.ts asserted a stale workflow file list |
High | ✅ Fixed — test now walks disk dynamically; 38/38 pass |
| G16 | No PR CI job ran apps/* unit tests; B1 had no workflow |
High | ✅ Fixed — added .github/workflows/app-tests.yml (dynamic matrix, 13 apps) |
| G17 | CLAUDE.md §2 repo layout stale | Med | ✅ Fixed — §2 refreshed |
| G18 | Zero ADRs despite 50 decisions; DoD #5 + §3 require ADRs | Med | 🟧 Seeded — docs/adr/ README + template + ADRs 0001–0004; backfill rest ongoing |
| G19 | F7 live/validate.sh false-positive hclfmt warning (deprecated CLI flags) |
Low | ✅ Fixed — modern hcl fmt --check, now a hard fail |
| G20 | U3/K3 scaffold READMEs described absent behavior | Low | ✅ Fixed — "SCAFFOLD — postponed" banners + §2 tags |
Open Issues / Tech Debt (Compliance control-mapping review — 2026-06-07)¶
Surfaced while reading compliance/*/control-mapping.md end to end against the asset glossary + project state. Full detail in docs/context/10-gap-register.md (G21–G22).
| # | Issue | Severity | Status |
|---|---|---|---|
| G21 | Control-mapping docs (CIS Azure 6.4/6.5, COMPLIANCE-GUIDE.md Vanta sync, ~25× O/P/Q-series refs) cited shipped-but-marked-roadmap assets (N3/N4), the wrong asset (D2 vs E2), and a stale O↔P↔Q→V letter mapping |
Med | ✅ Fixed in same pass — see G21 detail |
| G22 | No CI lint ties compliance/*/control-mapping.md asset-code refs to 00-asset-glossary.md / shipped status — same staleness class as G15, will recur every time an asset ships or M4 items get reassigned letters |
Med | ⬜ Queued — needs a small Node lint script (extract codes → resolve vs. glossary → cross-check roadmap markers vs. code_complete status); longer-term: generate the 4 mapping docs from one compliance/control-mappings.yaml source of truth |
Verified passing in this review: terraform
fmt -check(modules/live/sandbox) ✅ · Go terratestvet+ compile ✅ · all 14 app suites now green (github-onboarder fixed) ·live/validate.sh✅. Tooling: node 26, go 1.26, terraform 1.15.
Runbook Sign-Off Backlog (Ordered)¶
Execute offline Parts A+B first (~5 min each). Cloud parts (C/D) are optional promotion-path extenders.
| Priority | Runbook | Cloud? | Time | Cost |
|---|---|---|---|---|
| 1 | V2 docs/runbooks/test/V2.md |
No | ~5 min | $0 |
| 2 | V3 docs/runbooks/test/V3.md |
No | ~5 min | $0 |
| 3 | E0 docs/runbooks/test/E0.md |
Partial (Part C) | ~6 min offline | $0 |
| 4 | X7 docs/runbooks/test/X7.md |
No | ~5 min | $0 |
| 5 | U1 docs/runbooks/test/U1.md |
Yes (Part C) | ~5 min offline | $0 |
| 6 | U2 docs/runbooks/test/U2.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 7 | N5 docs/runbooks/test/N5.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 8 | N6 docs/runbooks/test/N6.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 9 | M1 docs/runbooks/test/M1.md |
Yes | ~5 min offline | $0 |
| 10 | M2 docs/runbooks/test/M2.md |
Yes (Part C) | ~5 min offline | ~$1 |
| 11 | M3 docs/runbooks/test/M3.md |
Yes | ~5 min offline | $0 |
| 12 | M6 docs/runbooks/test/M6.md |
Yes | ~5 min offline | $0 |
| 13 | J1 docs/runbooks/test/J1.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 14 | J2 docs/runbooks/test/J2.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 15 | J6 docs/runbooks/test/J6.md |
Yes (Part C/D) | ~5 min offline | $0 |
| 16 | H5 docs/runbooks/test/H5.md |
Yes (Part C) | ~5 min offline | $0 |
| 17 | H7 docs/runbooks/test/H7.md |
Yes (Part C/D) | ~5 min offline | $0 (needs P1) |
| 18 | F8 docs/runbooks/test/F8.md |
Optional (kind) | ~5 min offline | $0 |
| 19 | B5 docs/runbooks/test/B5.md |
Yes (Part C) | ~5 min offline | $0 (needs P2) |
| 20 | B4 docs/runbooks/test/B4.md |
Yes (Part C) | ~8 min | $0 |
| 21 | B3 docs/runbooks/test/B3.md |
Yes (Part C) | ~12 min | $0 |
| 22 | B2 docs/runbooks/test/B2.md |
Yes (Part C) | ~25 min | $0 |
| 23 | C3 docs/runbooks/test/C3.md |
Yes (Parts C–F) | ~75 min | $0 |
| 24 | C2 docs/runbooks/test/C2.md |
Yes (Parts C–E) | ~40 min | <$1 |
| 25 | H1 docs/runbooks/test/H1.md |
Yes | ~25 min | $0 |
| 26 | H2 docs/runbooks/test/H2.md |
Yes (needs P1) | ~30 min | $0 |
| 27 | H3 docs/runbooks/test/H3.md |
Yes (needs P2) | ~30 min | $0 |
| 28 | F3 docs/runbooks/test/F3.md |
Yes (Part C) | ~30 min | ~$10 |
| 29 | F5 docs/runbooks/test/F5.md |
Yes (Part C) | ~25 min | ~$2 |
| 30 | F4 docs/runbooks/test/F4.md |
Yes (Part C) | ~30 min | ~$5 |
| 31 | D4 docs/runbooks/test/D4.md |
Optional (kind) | ~5 min offline | $0 |
| 32 | F2 docs/runbooks/test/F2.md |
Yes (Part C) | ~35 min | ~$5 |
| 33 | F0 docs/runbooks/test/F0.md |
No | ~15 min | $0 |
| 34 | B1 docs/runbooks/test/B1.md |
Yes | ~60 min | $0 |
| 35+ | D2, X1, X2, C1, G0–G6, A1, A5, F1, F6 | Mix | Various | Various |
M2b 14-Asset Core Progress (D36)¶
| # | Asset | Scope | Status | Est. Time | Cloud Cost |
|---|---|---|---|---|---|
| 1 | ✅ E0 | Compliance snapshot (Policy + Defender score, wired to C1) | 🟦 v0.42 | — | — |
| 2 | ✅ V2 | Architecture diagram generator (apps/diagram-generator/) |
🟦 v0.43 | — | — |
| 3 | ✅ V3 | Runbook generator (apps/runbook-generator/) |
🟦 v0.43 | — | — |
| 4 | ✅ S1 | Drift detection (scheduled terraform plan → ticket via TicketPlatform) |
🟦 v0.44 | — | — |
| 5 | ✅ S2 | Azure Policy compliance dashboard (apps/compliance-dashboard/) |
🟦 v0.45 | — | — |
| 6 | ✅ K1 | IR runbook library (docs/runbooks/incident/) |
🟦 (external, merged) | — | — |
| 7 | ✅ K2 | On-call integration (modules/azure/oncall-integration/) |
🟦 (external, merged) | — | — |
| 8 | ✅ L1 | Azure Backup policy module (modules/azure/backup-policy/) |
🟦 v0.46 | — | — |
| 9 | ✅ L2 | Cross-region replication (object replication + SQL failover group) | 🟦 v0.47 | — | — |
| 10 | ✅ L4 | Automated restore drill (apps/restore-drill/ → S2 DR panel) |
🟦 v0.48 | — | — |
| 11 | ✅ D5 | Policy waiver engine (waivers/, OPA exception records, CI expiry enforcement) |
🟦 (external, merged PR #13) | — | — |
| 12 | ✅ F12 | Brownfield import library (modules/azure/import-blocks/, 9 modules) |
🟦 v0.49 | — | — |
| 13 | ✅ B6 | Client self-service bootstrap (prerequisite checker + validator) (apps/client-bootstrap/) |
🟦 v0.50 | — | — |
| 14 | ✅ F11 | Module versioning + private registry (apps/module-registry/) |
🟦 v0.51 | — | — |
Rule (D36): ALL other M2b/M3 assets are POSTPONED until these 14 are code-complete + signed off. Depth before breadth.
Next 5 Selected (v0.53 — D48)¶
After the M2b 14-asset core went code-complete, the next 5 most-important postponed/unbuilt
items were selected. Reconciliation: C5 (ADO pipelines) was found already built in commit
51c7fc4 — docs were stale; it's now marked 🟦 code-complete and dropped from the candidate set.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | I3 — CodeQL SAST | No code-analysis layer existed; D2 covered only IaC/secrets | 🟦 code-complete (v0.53) |
| 2 | I2 — Dependency scanning | dependabot.yml existed but no PR gate / alert digest |
🟦 code-complete (v0.53) |
| 3 | I1 — Container image scanning | Closes G6 (non-K8s container security); reusable image scan | 🟦 code-complete (v0.53) |
| 4 | E7 — TicketPlatform adapters | Closes G8; unblocks E6/I5/K4/P3; generalizes the S1 seed | 🟦 code-complete (v0.54) |
| 5 | F7 — Terragrunt live-infra reference | The missing per-env/region module wiring repo; deploy enablement | 🟦 code-complete (v0.55) |
Done (v0.53): I1 + I2 + I3 — the M2a CI security-scanning suite (docs/runbooks/test/I1.md, I2.md, I3.md).
Done (v0.54): E7 — apps/ticket-platform/ (GitHub/Jira/Linear/ADO adapters + CLI, 26 tests); S1 repointed (interface-compatible). Runbook docs/runbooks/test/E7.md.
Done (v0.55): F7 — live/ Terragrunt reference (root + _envcommon + bootstrap + per-env/region units; baseline→net/kv/acr DAG; offline validate.sh). Runbook docs/runbooks/test/F7.md.
✅ Next-5 (D48) COMPLETE. Candidate next batch: runbook sign-offs (in parallel), then M3 tail (W4 client offboarding) / M2b additional (J4 alert pack, X5 pipeline integration tests, X8 synthetic monitoring) / M4 advanced.
Next 5 Selected (v0.56 — D51)¶
With the next-5 (D48) complete, the next 5 most-important postponed items were selected. Priority rule: depth before breadth (D36) + milestone order — finish the remaining M2b "additional" assets (M2b §84) before advancing to M3 tail / M4. All five are M2b. The heavier network items (N3 WAF, N4 DDoS — CO-owned, cloud-cost) are deferred to a later batch.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | J4 — Alert rule pack | No detection-rule layer existed; identity/network/privilege/data-exfil KQL alerts over the J1 LAW, wired to K2 action groups | 🟦 code-complete (v0.56) |
| 2 | I5 — Defender → ticket via E7 | Newly unblocked by E7 (D49); first consumer proving the TicketPlatform adapter; closes the Defender-alert→ticket loop |
🟦 code-complete (v0.58) |
| 3 | X5 — Pipeline integration tests | M2a CI gates (C1–C3) had no integration test consumers; reusable-workflow test repos | 🟦 code-complete (v0.57) |
| 4 | X8 — Synthetic monitoring | No availability/latency synthetic probes; Azure Monitor standard webtests + alert rules | 🟦 code-complete (v0.57) |
| 5 | R2 — Production change log | Merged-PR → changelog generator; reuses E7 for change-record tickets where required | 🟦 code-complete (v0.59) |
✅ D51 batch COMPLETE (all 5): J4 (v0.56) · X5 + X8 (v0.57) · I5 (v0.58) · R2 (v0.59).
Done (v0.56): J4 — modules/azure/alert-rule-pack/ (curated scheduled-query alert rules across four threat domains — identity/privilege/network/data-exfil; domain toggles + per-rule overrides + freeform custom rules; consumes the J1 workspace + K2 action groups by ARM ID). Offline TestAlertRulePackValidate green. Runbook docs/runbooks/test/J4.md.
Done (v0.57) — X series complete (X5 + X8):
- X5 — tests/pipeline-integration/ reusable-workflow contract gate (contract_check.py + test_contract_check.py, 11 unit tests; offline validate.sh; CI .github/workflows/pipeline-integration.yml) + live it-{container-build-sign,aks-deploy,terraform-plan-apply}.yml consumers driving the existing fixtures against the sandbox. Catches workflow_call interface drift across all callers offline. Runbook docs/runbooks/test/X5.md.
- X8 — modules/azure/synthetic-monitoring/ (App Insights standard availability tests + per-test availability metric alerts; optional workspace-based AI component; consumes the J1 workspace + K2 action groups by ARM ID). Offline TestSyntheticMonitoringValidate green. Runbook docs/runbooks/test/X8.md.
X series: X1✅ X2✅ X3🟩 X4✅ X5✅ X6(ongoing runbooks) X7✅ X8✅ — all X assets now code-complete/shipped except the ongoing X6 runbook track.
Done (v0.58): I5 — apps/defender-ticketer/ (Defender for Cloud alerts → idempotent tickets via the E7 snowops-ticket CLI; the first E7 consumer). Pure normalize/filter/dedupe core behind a Collector seam; consumes E7 at run time (no build-coupling, per D49). 21 jest tests; offline dry-run + E7 output-contract verified. Runbook docs/runbooks/test/I5.md.
Done (v0.59): R2 — apps/change-log/ (production change log: merged PRs / squash commits → categorized Keep-a-Changelog markdown; pure categorize/render core behind a collector seam — git log / gh pr list / fixture; optional E7 change-record ticket per release via the same run-time bridge as I5). 32 jest tests; offline + live git log + prepend verified. Runbook docs/runbooks/test/R2.md. D51 batch complete — next: open backlog (runbook sign-offs in parallel; M4 advanced; heavier net N3/N4).
Done (v0.60): N3 + N4 — WAF Policy Module (modules/azure/waf-policy/) + DDoS Protection (modules/azure/ddos-protection/); the deferred M2b-additional network items.
Done (v0.61): E2–E6 — M4 evidence chain (Vanta adapter, Drata stub, Resource Graph library, evidence sync, access review).
Done (v0.62): W4 + L3 (D56) — Client Offboarding Playbook (docs/runbooks/offboarding/) finishes the M3 tail; DR Runbook Templates (docs/runbooks/dr/) complete the DR series with L1/L2/L4. Documentation assets — verification is manual runbook sign-off. Next: runbook sign-offs in parallel; remaining M4 advanced (J3, S3/S4, L5, O/P/Q, T); W1–W3 last (D35).
Next 5 Selected (v0.63+ — D57)¶
With the D51 batch + the W4/L3/N3/N4/E2–E6 follow-ons complete, the next 5 most-important postponed items were selected by the same depth-before-breadth (D36) + "finish near-complete series / unblock scaffolds / extend E7" rationale D48/D51 used. Candidates were the items the v0.62 roadmap line already flagged as the remaining M4-advanced backlog: J3, S3/S4, L5, O/P/Q, T.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | L5 — RTO/RPO doc generator | Completes the DR series (L1/L2/L3/L4 all code-complete); small, standalone, evidence-only — merges already-shipped L1/L2/L4 outputs into one client doc, no new infra | 🟦 code-complete (v0.63) |
| 2 | S3 — Auto-remediation playbooks (Logic Apps) | Closes the loop the S series opens with S1 drift detection — detect → remediate; the natural next step now S1/S2 are shipped | 🟦 code-complete (v0.64) |
| 3 | S4 — Compliance scorecard generator | Pairs with S2 dashboard + E0 snapshots → branded PDF for client/sales reporting; finishes the S series alongside S3 | 🟦 code-complete (v0.65) |
| 4 | J3 — Microsoft Sentinel deployment | Unblocks K3 (Sentinel SOAR playbooks — was a postponed scaffold per G20) and J5 (Grafana dashboards); the missing SIEM layer the J/K series assume |
🟦 code-complete (v0.66) |
| 5 | K4 — Post-incident review + GH issue via E7 | Newly tractable now E7 ships adapters (same "first/early E7 consumer" rationale that selected I5 in D51) — closes part of the K series | 🟦 code-complete (v0.66) |
Rationale for the order: L5 first because it's the smallest, purely additive (reuses L1/L2/L4 outputs verbatim, ships zero new Azure resources), and finishes a series outright. S3+S4 next because they finish the S series and because S3 is the natural "what do we do about the drift S1 finds" follow-on. J3 then K4 because J3 unblocks two scaffolds/postponed items at once (K3, J5) and K4 extends the now-proven E7 ticketing bridge.
Done (v0.63): L5 — apps/rto-rpo-doc-generator/ (pure adapt → render + jest,
same mold as E0/S1/S2/L4; merges L1 retention_summary + L2
replication_summary + L4 RestoreDrillReports into one evidence-backed
RTO/RPO objectives markdown doc; documents only drilled workloads; states the
mechanism, never a fabricated minute figure, for continuously-replicating
SQL failover groups; 15 jest tests). Runbook
docs/runbooks/test/L5.md. Completes the DR series
(L1/L2/L3/L4/L5 all code-complete).
Done (v0.64): S3 — modules/azure/auto-remediation-playbooks/ (curated
Logic App playbook pack: enforce-mandatory-tags, lock-down-public-storage,
remove-risky-nsg-rule, enable-resource-diagnostics; structural
dry-run/enforce split — Terraform renders Compose (intent record) vs.
Http+ManagedServiceIdentity action bodies at plan time, so dry_run
playbooks are structurally incapable of writing to Azure regardless of RBAC,
extending the B3/D21 audit-only-by-default stance to active remediation;
per-playbook enabled/mode overrides + custom_playbooks escape hatch;
consumes the J1 workspace by ARM ID). Offline Terratest gate
TestAutoRemediationPlaybooksValidate; runbook
docs/runbooks/test/S3.md. Closes the detect→
remediate loop S1 opened. Next code: S4.
Done (v0.65): S4 — apps/compliance-scorecard-generator/ (pure
adapt → render + jest, the E0/S1/S2/L4/L5 mold; turns one E0
ComplianceSnapshot + optional S2 ComplianceDashboard + branding into a
versioned, branded one-page scorecard.md/scorecard.html (+ optional
Pandoc scorecard.pdf) + model.json; the only derived figure is a
documented A–F grading band — the simple mean of policy-compliance % and
Secure Score %, banded through a fixed five-way split, with its derivation
printed inline, the same "never fabricate, show your work" stance as L5/S3;
framework badges and trend direction are strict 1:1 re-presentations of S2's
pass/attention status and real delta signs; branding resolves against
documented neutral defaults so an unbranded run still looks professional;
both optional inputs honestly state when they weren't supplied rather than
silently omitting sections; 24 jest tests). Offline-only — no live phase;
runbook docs/runbooks/test/S4.md. Finishes the
S series (S1/S2/S3/S4 all code-complete). Next code: J3.
Next 5 Selected (v0.66 — D60)¶
With the D57 batch's L5/S3/S4 shipped, the two still-queued D57 items (J3, K4) were carried forward and joined by K3/J5/R4 — confirmed with the user — to form a tight SIEM + observability + IR/change-ticketing batch. Same depth-before-breadth (D36) + "finish near-complete series / unblock scaffolds / extend E7" lens. J3 is the keystone (onboarding Sentinel unblocks K3 + J5); K4 + R4 extend the proven E7 ticketing bridge (I5/R2 → K4/R4).
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | J3 — Microsoft Sentinel deployment | The missing SIEM layer; unblocks K3 + J5 | 🟦 code-complete (v0.66) |
| 2 | K3 — Sentinel SOAR playbooks | Retires the misleading apps/soar-playbooks scaffold (G20); active-IR containment over J3 incidents |
🟦 code-complete (v0.66) |
| 3 | J5 — Managed Grafana dashboards-as-code | The observability presentation layer over the J1 workspace | 🟦 code-complete (v0.66) |
| 4 | K4 — Post-incident review via E7 | Third E7 consumer; blameless PIR + tracking issue from closed incidents | 🟦 code-complete (v0.66) |
| 5 | R4 — CAB automation via E7 | Fourth E7 consumer; high-impact changes → CAB review tickets (SOC2 CC8.1) | 🟦 code-complete (v0.66) |
✅ D60 batch COMPLETE (all 5).
- J3 —
modules/azure/sentinel/: onboards the J1 workspace + toggleable connectors + curated Sentinel-native scheduled analytics rules → incidents (entity mappings + MITRE tactics) + opt-in Defender incident-creation rule. OfflineTestSentinelValidate. RunbookJ3.md. (D61) - K3 —
modules/azure/sentinel-soar/: isolate-account / disable-SP / quarantine-VM Logic App playbooks reusing S3's structural dry_run/enforce split + per-playbook Graph/ARM audience; retires theapps/soar-playbooks/scaffold. OfflineTestSentinelSoarValidate. RunbookK3.md. (D62) - J5 —
modules/azure/grafana-dashboards/: Managed Grafana + Monitoring Reader / Grafana RBAC + committed dashboards-as-code (offlinescripts/validate.shgate +az-based deploy script). OfflineTestGrafanaDashboardsValidate. RunbookJ5.md. (D63) - K4 —
apps/post-incident-review/: closed incidents → blameless PIR docs + idempotent tracking issues via the E7 CLI bridge; 23 jest tests. RunbookK4.md. (D64) - R4 —
apps/cab-automation/: high-impact changes → CAB review tickets via the E7 CLI bridge; 24 jest tests. RunbookR4.md. (D64)
Next: Client-Facing Posture Visibility (D65) — planned next (see below); then runbook sign-offs in parallel; remaining M4 advanced (J7 Basic-Logs strategy, H4/H6 IAM, I4/I6/I7 vuln, M4/M5/M7 data, N2/N7 net, O/P/Q/T series, U4/U5 cost, V1/V4 docs, R3 emergency-change, K5 tabletop); W1–W3 last (D35).
Client-Facing Posture Visibility (v0.67 — D65, planned)¶
Why: for an automation MSP, invisible value drives churn — the client should see the guardrails, backups, DR drills, incident handling, and cost governance SnowOps runs for them. This is the retention/QBR (Y11) and upsell ("Baseline → here's what Advanced lights up") artifact, and a sales proof asset. ~80% of the building blocks already exist (E0/S2/S4/L5/J5/V2) — the work is composition + packaging, not new infrastructure.
Approach (D65): two audience tiers, both composed from existing assets;
organized by SnowOps deliverable, not by raw Azure metric; the per-client-
subscription model (B-series) means each client's J5 instance is already
isolated, so a shared multi-tenant portal (W5, D35) is explicitly deferred.
Sequence V5 first (the near-free retention/sales artifact), then J8.
| # | Asset | Tier | Scope | Status |
|---|---|---|---|---|
| 1 | V5 — Client Cloud Posture Report | Tier 2 (periodic, exec/QBR) | apps/posture-report/ — offline composer bundling E0 + S2 + S4 + L5 + V2 (+ optional K4) into one branded HTML/PDF report, sections by deliverable, tagged Baseline/Advanced |
🟦 code-complete (v0.67) |
| 2 | J8 — Client Overview Dashboard | Tier 1 (live, engineer) | client-facing read-only Grafana dashboard added to the J5 module + client_grafana_viewers access hook + folder-aware deploy |
🟦 code-complete (v0.67) |
V5 — Client Cloud Posture Report (Tier 2) — implementation plan¶
- Scaffold
apps/posture-report/from the E0/S2/S4/L5 mold (copy the I5/S4 tsconfig/jest config;@snowops/posture-report, binsnowops-posture-report). compose.ts(pure): re-declare the input contracts (E0ComplianceSnapshot, S2ComplianceDashboard, S4 scorecardmodel.json, L5 RTO/RPO model, V2 diagram svg/d2 — no cross-package import, per D37/D40) → onePostureReportModel(schemaVersion 1.0). Every figure is a re-presentation of a source number; a missing input renders an honest "not supplied" section (L5/S4 never-fabricate stance).- Section taxonomy (shared with J8): exec summary + overall grade (S4) · your guardrails (policy/Defender, E0) · your data is protected (backup coverage L1 + restore-drill + RTO/RPO L4/L5) · your compliance posture (framework rollup
- trend, S2) · incidents we handled (reuse a K4 report when supplied) · cost
governance (roadmap until a U-series feed exists) · architecture (V2). Each
section tagged
[Baseline]/[Advanced]; ungated sections become upsell teasers. render.ts(pure): self-containedposture-report.html(inline CSS, no external refs — golden-file test, V2/S2/S4 pattern) + a markdown summary.pdf.ts: near-verbatim S4/G3 Pandoc-spawn (best-effort; failure logged, never fails the run).- Branding: reuse S4's
BrandingConfig(neutral defaults; onlyclientNamerequired). - CLI (
index.ts):--snapshot(E0, required) ·--dashboard(S2) ·--scorecard(S4 model) ·--rto-rpo(L5) ·--diagram(V2) ·--incidents(K4 report) ·--branding·--out-dir. Optional inputs degrade honestly. - Delivery:
.github/workflows/posture-report.yml(monthly + dispatch) that runs E0 → S2 → S4 → L5 then composes; uploads a PRIVATE artifact by default (posture data is sensitive — same stance as S2). QBR template underdocs/client-guides/. - Tests + runbook: jest (composition, honest-missing-input, tier tagging,
branding, never-fabricate, HTML self-containment); runbook
docs/runbooks/test/V5.md(offline Parts A+B, mirroring L5.md/S4.md — no live phase). app-tests CI auto-discovers it.
J8 — Client Overview Dashboard (Tier 1) — implementation plan¶
- Dashboard-as-code: add
modules/azure/grafana-dashboards/dashboards/snowops-client-overview.json— panels by deliverable (policy compliance %, Defender Secure Score, backup job statusL1, last restore drillL4, open Sentinel incidentsJ3, budget vs. actualU1), reading the Azure Monitor datasource + theworkspacetemplate variable already established in J5's dashboards. Passes J5's existing offline gate (scripts/validate_dashboards.py) automatically. - Client access: grant the client's AAD group Grafana Viewer via J5's
existing
grafana_viewersinput (read-only). Add a small J5 addition for a dedicated "client" Grafana folder so the client sees only this dashboard, not the internal ops ones (folder-scoped Viewer); document the pattern in J5's README. - No new module / no multi-tenant work — reuses the J5 instance, Monitoring
Reader grant, gate, and
az grafana dashboard createdeploy script. Per-client J5 instance = already isolated. - Tests + runbook: the J5 dashboards gate covers the new JSON; runbook
docs/runbooks/test/J8.md(Part A offline gate; Part C live — client group sees only the client folder, read-only).
Open item: the incidents and cost sections need a clean data feed — incidents can reuse the K4 report; cost has no SnowOps producer yet (U4/U5 are postponed). Both degrade honestly (the section states "not yet wired") until a source exists; wiring them is a follow-on, not a blocker for D65.
Next 5 Selected (v0.67 — D66)¶
The two planned D65 items (V5 + J8) were taken together with the three postponed items that finish a near-complete series — the same depth-before-breadth (D36) + "finish near-complete series" lens D48/D51/D57/D60 used. After this batch the J series (J1–J8), the R series (R1–R4), and the K series (K1–K5) are all code-complete.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | V5 — Client Cloud Posture Report | D65 flagship; the periodic exec/QBR posture artifact composing E0/S2/S4/L5/V2 (+K4) | 🟦 code-complete (v0.67) |
| 2 | J8 — Client Overview Dashboard | D65 Tier-1 live counterpart to V5; extends J5 (no new module) | 🟦 code-complete (v0.67) |
| 3 | J7 — Cost-Controlled Log Strategy | Finishes the J series; per-table Basic/Analytics plan + retention over a J1 workspace | 🟦 code-complete (v0.67) |
| 4 | R3 — Emergency Change Workflow | Finishes the R series; break-glass label → expedited CAB record via E7 + mandatory retro review | 🟦 code-complete (v0.67) |
| 5 | K5 — Tabletop Exercise Pack | Finishes the K series; five facilitated IR rehearsals mapped 1:1 to the K1 runbooks, feeding K4 | 🟦 code-complete (v0.67) |
✅ D66 batch COMPLETE (all 5).
- V5 —
apps/posture-report/: offlinecompose→render(+ jest, 27 tests) bundling the re-declared E0/S2/S4/L5/V2/K4 contracts into one branded, deliverable-organized HTML/markdown (+ optional Pandoc PDF) report; sections tagged Baseline/Advanced with upsell teasers for ungated/not-supplied Advanced sections; cost section is an honest U4/U5 roadmap note (never fabricated); self-contained HTML. Workflowposture-report.yml(monthly + dispatch, PRIVATE artifact); QBR guidedocs/client-guides/qbr-posture-report.md. RunbookV5.md. - J8 —
modules/azure/grafana-dashboards/dashboards/snowops-client-overview.json(passes the J5 offline gate) + a distinctclient_grafana_viewersaccess hook + folder-awaredeploy-dashboards.sh(client dashboards → "SnowOps Client" folder). RunbookJ8.md. - J7 —
modules/azure/log-analytics-cost/: per-table Basic/Analyticsplan+ retention viaazurerm_log_analytics_workspace_tableover an existing J1 workspace, curated overridable default strategy (security/audit → Analytics, high-volume telemetry → Basic), advisory daily-quota note. OfflineTestLogAnalyticsCostValidate. RunbookJ7.md. - R3 —
.github/workflows/emergency-change.yml(label-gated; drives R4'sapps/cab-automationin emergency mode via the E7 CLI, dry-run unless a tracker secret is set; posts a retrospective-review checklist) +tests/emergency-change/stdlib validator (14 tests) +docs/runbooks/change/emergency-change.md. RunbookR3.md. - K5 —
docs/runbooks/tabletop/: README + facilitation guide + scenario template + five Azure-flavored scenarios (account compromise / ransomware / data exfiltration / DDoS / vendor breach) cross-linked 1:1 to the K1 runbooks + after-action template feeding K4 + an offlinevalidate.pygate. RunbookK5.md.
Next: runbook sign-offs in parallel; remaining M4 advanced (H4/H6 IAM, I4/I6/I7 vuln, M4/M5/M7 data, N1/N2/N7 net, O/P/Q/T series, U3/U4/U5 cost, V1/V4 docs); F9/F10/W5/U3 multi-cloud (M5); W1–W3 last (D35). 43 postponed items remain.
Next 5 Selected (v0.68 — D68)¶
With the J/R/K series closed (D66), the next 5 finish the two next-most-complete series — I (vulnerability management) and U (cost governance) — under the same depth-before-breadth (D36) "finish a near-complete series" lens. The U items also close the cost-visibility gap V5/J8 flagged in D65.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | I4 — DAST (OWASP ZAP) | The missing dynamic-analysis layer (I1 images / I3 SAST are at-rest); reusable workflow_call gate |
🟦 code-complete (v0.68) |
| 2 | I6 — Azure Update Manager report | Patch-compliance reporting; the OS-update arm of vuln management | 🟦 code-complete (v0.68) |
| 3 | I7 — CVE triage + SLA dashboard | Ties the scanners together with a triage process + SLA tracking — finishes the I series | 🟦 code-complete (v0.68) |
| 4 | U4 — FinOps dashboard | Cost-governance visibility (log-cost + tag governance); extends J5 | 🟦 code-complete (v0.68) |
| 5 | U5 — Cost anomaly detection | Daily Cost Management anomaly alerts — finishes the U series | 🟦 code-complete (v0.68) |
✅ D68 batch COMPLETE (all 5).
- I4 —
.github/workflows/dast-scan.yml: reusable OWASP ZAP gate (baseline|full) against a deployed URL, fails on High web findings, uploads the report; tuned by a committed.zap/rules.tsv(+.zap/README.md). Tight perms (contents: read; ZAP issue-writing off). RunbookI4.md. - I6 —
apps/patch-compliance/: pure adapt→render (S4/L5 mold) turning an Azure Update Manager assessment into a patch-compliance report (coverage %, out-of-compliance machines, pending Critical/Security counts, SLA aging); self-contained HTML + optional Pandoc PDF; never fabricates a patch date. Jest suite. RunbookI6.md. - I7 —
docs/runbooks/vuln/cve-triage.md(triage→severity→SLA Critical 7d/High 30d/Medium 90d→owner/E7→remediate or D5 waiver→verify) +snowops-vuln-triage.jsonGrafana dashboard (open Defender findings by severity via ARG + SLA aging from the exportedSecurityRecommendationtable). RunbookI7.md. Finishes the I vuln series. - U4 —
snowops-finops.jsonGrafana dashboard (extends J5): daily billable log ingestion + top tables by ingestion (the J7 Basic-Logs lever) + tag governance / cost allocation via ARG; honest that $ spend needs a Cost Management export. RunbookU4.md. - U5 —
modules/azure/cost-anomaly-alert/: Azure Cost Management daily anomaly alerts (azurerm_cost_anomaly_alert,for_eachover analertsmap) → email recipients; the detection complement to U1 budgets. OfflineTestCostAnomalyAlertValidate. RunbookU5.md. Finishes the U cost series.
Next: runbook sign-offs in parallel; remaining M4 advanced (H4/H6 IAM, M4/M5/M7 data, N1/N2/N7 net, O/P/Q/T series, V1/V4 docs); F9/F10/W5/U3 multi-cloud (M5); W1–W3 last (D35). 38 postponed items remain.
Next 5 Selected (v0.69 — D69)¶
With the I/U series closed (D68), the next 5 finish the two next-most-complete series — N (network security) and V (documentation) — same depth-before-breadth (D36) lens. N1/N2 were scoped to be non-redundant with F2 (which already owns the hub-spoke + a Standard firewall): N1 adds the connectivity edge F2 lacks, N2 is the Premium deep-inspection policy layer.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | N1 — Landing-zone connectivity | The Bastion/gateway/DNS-resolver edge F2 lacks | 🟦 code-complete (v0.69) |
| 2 | N2 — Azure Firewall Premium | IDPS + TLS-inspection policy layer over F2's Standard firewall | 🟦 code-complete (v0.69) |
| 3 | N7 — Zero-trust reference architecture | Ties the N/F/H/M/J/K/E/S/C/I/D assets into one ZT story — finishes N | 🟦 code-complete (v0.69) |
| 4 | V1 — Policy repo template | The document-control half (the 📋 Manual controls) the automated assets pair with |
🟦 code-complete (v0.69) |
| 5 | V4 — Compliance manual generator | Composes controls + evidence + policies into a framework manual — finishes V | 🟦 code-complete (v0.69) |
✅ D69 batch COMPLETE (all 5).
- N1 —
modules/azure/landing-zone-connectivity/: Azure Bastion (always) + optional VPN/ER gateway + optional Private DNS Resolver, attached to the existing F2 hub vNet by name; owns no F2 resource. OfflineTestLandingZoneConnectivityValidate. RunbookN1.md. - N2 —
modules/azure/firewall-premium-policy/: Premiumazurerm_firewall_policywith IDPS (mode + signature overrides + traffic bypass) + optional TLS inspection (KV-cert + UserAssigned identity) + optional DNS proxy + overridable rule collection groups (deny-by-default). OfflineTestFirewallPremiumPolicyValidate. RunbookN2.md. - N7 —
docs/architecture/zero-trust-reference.md: the three ZT principles + five pillars + visibility spine mapped to SnowOps assets, with a maturity/gap table. RunbookN7.md. Finishes the N series. - V1 —
templates/policies/: six ratifiable policy templates (InfoSec/AUP/IR/BC-DR/Change/Vendor), each cross-referencing its enforcing assets, + an offlinevalidate.py(sections + placeholders + cross-link integrity). RunbookV1.md. - V4 —
apps/compliance-manual/: pure adapt→render composing a manual spec (+ optional E0 snapshot) into a branded framework compliance manual (md/HTML/PDF); never invents an undocumented control. RunbookV4.md. Finishes the V series.
Next: runbook sign-offs in parallel; remaining M4 advanced (H4/H6 IAM, M4/M5/M7 data, O/P/Q/T series); F9/F10/W5/U3 multi-cloud (M5); W1–W3 last (D35). 33 postponed items remain.
Series Completion (v0.70 — D70): H (Identity & Access Management)¶
The two remaining H assets were completed to close the H series outright
(H1–H7 all 🟦+). Both are azuread-provider modules in the H2/H3 mold.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | H4 — SCIM provisioning to SaaS | Automates joiner/mover/leaver provisioning from Entra to SaaS apps over SCIM | 🟦 code-complete (v0.70) |
| 2 | H6 — Access review automation | Recurring Entra access reviews (recertification) via entitlement management — completes H | 🟦 code-complete (v0.70) |
- H4 —
modules/azure/scim-provisioning/: instantiates the SaaS gallery app (azuread_application_from_template), sets the SCIM endpoint + bearer token (azuread_synchronization_secret, sensitive), and starts the provisioning job (azuread_synchronization_job). OfflineTestScimProvisioningValidate. RunbookH4.md. - H6 —
modules/azure/access-reviews/: a catalog + access packages, each with anazuread_access_package_assignment_policycarryingassignment_review_settings(frequency / duration / reviewers / timeout behavior) — recurring recertification. Native-config counterpart to E6 (the evidence/reporting app). OfflineTestAccessReviewsValidate. RunbookH6.md.
H series: H1✅ H2✅ H3✅ H4✅ H5✅ H6✅ H7✅ — complete.
Next: runbook sign-offs in parallel; remaining M4 advanced (M4/M5/M7 data, O/P/Q/T series); F9/F10/W5/U3 multi-cloud (M5); W1–W3 last (D35). 31 postponed items remain.
Series Completion (v0.71 — D71): M (Data Protection & Privacy)¶
The three remaining M assets were completed to close the M series outright (M1–M7 all 🟦+): M4 discovers + classifies, M5 prevents M365 egress, M7 proves you know your data and can answer for it — layered on the already-shipped M1/M2/M3/M6 encryption/TLS/residency enforcement.
| # | Asset | Rationale | Status |
|---|---|---|---|
| 1 | M4 — Microsoft Purview baseline | The data-classification foundation (account + access via ARM; scans are data-plane Part C) | 🟦 code-complete (v0.71) |
| 2 | M5 — DLP policies | M365 content-inspection layer as policy-as-code (Terraform can't manage M365 DLP) | 🟦 code-complete (v0.71) |
| 3 | M7 — GDPR/CCPA evidence | RoPA register + DSAR workflow on the statutory clocks — completes M | 🟦 code-complete (v0.71) |
- M4 —
modules/azure/purview-baseline/:azurerm_purview_account+ J1 diagnostics + Reader/Storage Blob Data Reader overscan_scopes. OfflineTestPurviewBaselineValidate. RunbookM4.md. - M5 —
policy/dlp/: three audit-first YAML DLP policies (PII-financial / PII-personal / PHI) + offline gate + unit tests + idempotentdeploy-dlp.ps1(Security & Compliance cmdlets). Cloud-storage half = N5/M1/M3/M6 (documented). RunbookM5.md. - M7 —
compliance/data-inventory/RoPA (template + fail-closed PyYAML gate: Art. 6 lawful-basis enum, retention-with-basis, DPA-per-processor, mandatorydsar_lookup) +docs/runbooks/privacy/DSAR workflow (30/45-day clocks, verify-first, documented exemptions, honest backups statement) +compliance/dsar/records. RunbookM7.md.
M series: M1✅ M2✅ M3✅ M4✅ M5✅ M6✅ M7✅ — complete.
Next: runbook sign-offs in parallel; remaining M4 advanced (O/P/Q/T series); F9/F10/W5/U3 multi-cloud (M5); W1–W3 last (D35). 28 postponed items remain.
Sequenced Full Roadmap (Track B — Sagar)¶
| Priority | Action | Status | Milestone |
|---|---|---|---|
| Next code | K1 + K2 (IR + on-call) | 🟦 external (merged) | M2b |
| After K-series | L1 + L2 + L4 (backup + DR + restore drill) | ⬜ | M2b |
| After L-series | D5 (policy waivers) | 🟦 external (merged PR #13) | M2b |
| After D5 | F12 (brownfield imports) | ⬜ | M3 |
| After F12 | B6 (self-service bootstrap) | 🟦 v0.50 | M3 |
| After B6 | F11 (module versioning) | 🟦 v0.51 | M3 |
| Then | C5, E7, W4 (ADO + ticket adapters + client offboarding) | ⬜ | M3 |
| Then | Advanced package (E1–E6 full, J3, O, P, Q, T, V1, V4) | 🟦 SIEM/IR/obs done (J3/K3/J5/K4/R4 v0.66); rest ⬜ | M4 |
| Next (D65) | Client-facing posture visibility — V5 (Cloud Posture Report) + J8 (Client Overview Dashboard) | ⬜ planned | M4 |
| Then | Multi-cloud (F9, F10, W5, U3) | ⬜ | M5 |
| Last | W1–W3 (multi-tenant) | ⏸️ postponed | after M2b/M3 |
GTM Track A — Status¶
| Batch | Assets | Status | Notes |
|---|---|---|---|
| A1 | Y0, Y1, Y2, §3.8 | 🟦 drafted (v0.33) | Awaiting Nidhi (Y1 claims) + Sagar (Y2 real numbers) |
| A2 | Y3, Y4 | 🟦 drafted (v0.33) | Awaiting Sagar's 50-account seed list |
| A3 | Y5, Y6, Y7 | 🟦 drafted (v0.34) | — |
| A4 | Y8, Y9 | 🟦 drafted (v0.34) | Y8 needs brand assets; Y9 is synthetic |
| A5 | Z0, Z1 | 🟦 drafted (v0.34) | — |
| A6 | Y10, Y11, Y12, Y13 | 🟦 drafted (v0.34) | Y12 needs counsel; Y13 needs HubSpot config |
| A7 | Z2, Z3 | 🟦 drafted (v0.34) | Unshipped delta assets flagged with milestone |
Human prerequisites before going live with outbound: - Nidhi: compliance-claim review on Y1/Y5/Y7/Y9/Z2/Z3 + Y9 sanitization - Sagar: Y2 real numbers, Y3 50-account seed list, Y13 HubSpot pipeline config - Counsel: Y12 contract pack - Brand: Y8 deck design assets
DevSecOps Readiness Backlog (D72)¶
Status: Backlog — build on demand or when bandwidth allows. No items scheduled. Full plan:
docs/context/12-devsecops-readiness.md
~70% of a DevSecOps engagement reuses existing assets (D1/D2/I1–I3 shift-left, D3/D4 policy-as-code, C2 container security, J3/K3 SIEM/SOAR, I5–I7 vuln mgmt, H1–H7 identity, E0–E6 evidence). The backlog covers the gaps:
| Phase | Items | Trigger | Effort |
|---|---|---|---|
| 1 — GTM-ready | DS1 (service package definition) + DS3 (threat modeling templates) | Before first DevSecOps outreach | ~2 days |
| 2 — Pre-sales | DS2 (DevSecOps maturity assessment tool) | Before first signed engagement | ~2–3 days |
| 3 — Delivery | DS4 (SLSA provenance) + DS6 (API scanning) + DS8 (security metrics) | First signed engagement | ~3 days |
| 4 — Differentiators | DS5 (Falco runtime) + DS7 (Dependency-Track) | Mature client request | ~4 days |
| 5 — Advanced | DS9 (pen test templates) + DS10 (security chaos engineering) | On demand | ~2 days |
| Tool-agnostic | Scanner contract adapters (per-tool, per-category) | Client mandates alternative tool | ~0.5 day each |
Tool-agnostic architecture (D72): Extend the F0/E1/E7 contract/adapter pattern to security tooling. CI tools normalize to SARIF (already the case); infrastructure tools (Kyverno↔Gatekeeper, Sentinel↔Splunk) swap at the module level. Don't pre-build adapters — build each when a client needs it.