Skip to content

SnowOps β€” Full Asset Catalog

Status: ⬜ Not Started Β· 🟨 Scaffolding Β· 🟧 In Progress Β· 🟦 Code Complete Β· 🟩 Shipped Each asset leads with its human-readable name; the short code (e.g. E0) is kept as a secondary tag β€” it remains the stable cross-reference anchor used elsewhere in the repo. Code β†’ name lookup: docs/context/00-asset-glossary.md. For compact status table: docs/context/04-asset-status.md For full version history: docs/context/05-history.md


A. Sales & CRM Automation (HubSpot) [X][SO]

  • 🟦 HubSpot Lead Enrichment (A1) β€” HubSpot Private App + lead-enrichment Custom Code Action (Clearbit/Apollo β†’ contact props). [M1][CA] β†’ Test: Clearbit/Apollo lookups enriching contact properties. Manual runbook: docs/runbooks/test/A1.md.

  • ⬜ ICP Lead Scoring & Routing (A2) β€” ICP scoring Custom Code Action (TS) β†’ routes to Sagar/Nidhi. [M2b] ⏸️ postponed β†’ Test: fixture leads covering ICP / near-ICP / out-of-ICP; assert routing.

  • ⬜ Proposal PDF Generator (A3) β€” Proposal generator (Deal stage β†’ templated PDF; DocRaptor or Google Docs API). [M2b] ⏸️ postponed β†’ Test: golden-file diff on rendered PDF text content for 3 sample deals.

  • ⬜ Project Kickoff Webhook (A4) β€” Project kickoff webhook (Closed Won β†’ triggers the GitHub Onboarder App (B1) + Notion + Slack). [M2b] ⏸️ postponed β†’ Test: simulate webhook payload β†’ assert repo created + Slack channel.

  • 🟦 Discovery Trigger Automation (A5) β€” Discovery trigger Custom Code Action (Qualified β†’ dispatches the Discovery Run Workflow (G4) + sends offer email). [M1][CA] β†’ Test: Deal stage change β†’ G4 dispatched, offer email sent. Manual runbook: docs/runbooks/test/A5.md.


B. Client Onboarding [B]

  • 🟦 GitHub Onboarder App (B1) β€” GitHub App snowops-onboarder (Probot/TS): repo + branch protection + CODEOWNERS + checks + env vars + federated OIDC to client Azure AD. [M1][SOβ†’CO] β†’ Test: install on test org, trigger create β†’ assert all settings via GH API. Manual runbook: docs/runbooks/test/B1.md.

  • 🟦 Azure Client Bootstrap Module (B2) β€” modules/azure/client-bootstrap/ β€” Azure AD application + service principal + federated identity credentials (GitHub OIDC only, repo: prefix validated) + role assignments (sub/ACR/KV/freeform). No client secret ever created. [M2a][SOβ†’CO] β†’ Test: Terratest validates SP + fed creds + role-assignment IDs. Manual runbook: docs/runbooks/test/B2.md.

  • 🟦 Subscription Baseline Module (B3) β€” modules/azure/subscription-baseline/ β€” composes the Azure Baseline (F1) + group RBAC (6 named shortcuts: Owner/Contributor/Reader/Security Admin/Security Reader/UAA + freeform escape hatch) + MCSB regulatory-compliance initiative (system-assigned identity, audit-only until remediation role granted out-of-band). Defender ON by default. azurerm-only (RBAC binds by group object ID). [M2a][CO] β†’ Test: Terratest validates (offline composition + RBAC flatten + MCSB block) + integration applies to sandbox asserts LAW + SnowOps Standard set + MCSB assignment + system-assigned identity GUID + 3 RBAC role-assignment IDs. Manual runbook: docs/runbooks/test/B3.md.

  • 🟦 Client State Backend Module (B4) β€” modules/azure/client-state-backend/ β€” wraps the State Backend (F6) + Blob Data RBAC (Contributor/Reader/Owner, principal_type unset for mixed SP+group) + optional Private Endpoint + optional network-rule lockdown (standalone azurerm_storage_account_network_rules, default-deny; precondition requires reach-path) + optional diagnostics. backend_config forces use_azuread_auth = true. azurerm-only. [M2a][CO] β†’ Test: Terratest validates (offline composition + RBAC flatten) + integration asserts SA ARM ID + backend_config + Blob Data role-assignment IDs + contract versioning_enabled. Manual runbook: docs/runbooks/test/B4.md.

  • 🟦 Azure Resource PIM Module (B5) β€” modules/azure/pim-azure-resources/ β€” PIM for Azure resource roles. Tier-0 (Owner + UAA) β†’ MFA + justification + ticketing + approval + max 8h; tier-1 (Contributor) β†’ MFA + justification + max 4h. Uses azurerm's native azurerm_pim_eligible_role_assignment + azurerm_role_management_policy. Eligibility permanent; time-box is on activation. Requires Entra ID P2 at apply. Precondition: β‰₯1 break-glass Owner so PIM can't lock the sub out. [M2a][CO] β†’ Test: Terratest validates (offline). Live activation drill is manual (can't Terratest MFA/approval flow). Manual runbook: docs/runbooks/test/B5.md.

  • 🟦 Client Prerequisite Checker (B6) β€” apps/client-bootstrap/ β€” client self-service bootstrap: prerequisite checker + Azure permission validator a prospective client runs in their OWN tenant pre-engagement. Pure evaluator over an EnvironmentSnapshot behind a Collector seam (FixtureCollector tests/offline; AzureCliCollector live az). Read-only, no secrets. Checks tooling (az β‰₯ 2.50, terraform β‰₯ 1.6), auth, permissions (assign RBAC = Owner/UAA not Contributor; create Entra apps+SPs; required providers registered), Entra ID P2 (warn). READY only when every required check passes; summary.md ends in a remediation list; status.json carries ready/blockers/warnings. Single entrypoint bootstrap.sh (exit 0/2). [M3][CA] (14-asset M2b core) β†’ Test: examples/snapshot.ok.json β†’ READY/exit 0; examples/snapshot.restricted.json (restricted SP) β†’ NOT READY + clear remediation/exit 2. Manual runbook: docs/runbooks/test/B6.md.


C. CI/CD & Delivery Pipelines [B]

  • 🟦 Terraform Plan/Apply Pipeline (C1) β€” terraform-plan-apply.yml GitHub Actions reusable workflow (Azure OIDC, plan-on-PR comment, apply-on-merge, environment gates, conftest OPA check post-plan). [M2a][CA] ← KEYSTONE β†’ Test: create test repo consuming workflow; open PR β†’ plan comment; merge β†’ apply against sandbox. Manual runbook: docs/runbooks/test/C1.md.

  • 🟦 Container Build & Sign Pipeline (C2) β€” container-build-sign.yml (build β†’ ACR push by digest β†’ Notation v2 sign via AKV plugin β†’ Syft SBOM β†’ Grype scan with severity cutoff). Signing by digest not tag. 13 inputs; grype_severity_cutoff defaults critical; fail_on_scan_findings defaults true. [M2a][CA] β†’ Test: clean image passes; planted-CVE image fails at critical cutoff; notation verify succeeds. Manual runbook: docs/runbooks/test/C2.md.

  • 🟦 AKS Deploy Pipeline (C3) β€” aks-deploy.yml (ArgoCD image override β†’ sync β†’ wait Healthy β†’ smoke probe β†’ optional rollback drill; Kustomize XOR Helm modes, rejected if both/neither set). ArgoCD-token-only auth. [M2a][CA] β†’ Test: happy-path Kustomize deploy + smoke green; rollback drill exercises revert + roll-forward; ambiguous + empty image-set inputs both rejected. Manual runbook: docs/runbooks/test/C3.md.

  • 🟩 GitOps Branching Standard (C4) β€” GitOps branching standard doc + client-repo template + branch protection rules. [M1][QW][CA] β†’ Test: branching standard doc + template structure + protection validation. Manual runbook: docs/runbooks/test/C4.md. Signed off 26/05 (Sagar).

  • 🟦 Azure DevOps Pipeline Templates (C5) β€” pipelines/azure-devops/ β€” ADO Pipeline templates: terraform-plan-apply.yml (mirrors C1), container-build-sign.yml (C2), aks-deploy.yml (C3), quality-gates.yml (D2) + caller examples + README. Same underlying tools as the GH Actions workflows; only the CI wrapper changes. Callers reference via resources: repositories:. [M3][CA] 🟦 code-complete (commit 51c7fc4) β†’ Test: ADO test project consuming pipeline; PR triggers plan; stage approval gates apply on merge. Runbook: docs/runbooks/test/C5.md (pending).


D. Quality & Security Gates (PR-time) [B]

  • 🟩 Pre-Commit Quality Hooks (D1) β€” .pre-commit-config.yaml (tflint, fmt, checkov, tfsec, gitleaks, trivy fs, conftest verify on pre-push). [M1][QW][CA] β†’ Test: planted bad commits blocked locally. Manual runbook: docs/runbooks/test/D1.md. Signed off 26/05 (Sagar).

  • 🟦 PR Quality-Gate Workflow (D2) β€” PR-blocking GH Actions mirroring the Pre-Commit Quality Hooks (D1) (.github/workflows/quality-gates.yml). [M1][QW][CA] β†’ Test: planted bad PRs blocked; clean PRs pass. Manual runbook: docs/runbooks/test/D2.md.

  • 🟩 Terraform OPA Policy Bundle (D3) β€” Conftest/OPA policy bundle for terraform plan JSON (encryption, tags, no public network, allowed regions, cost caps, managed-identity / no-standing-secrets, Azure naming-constraint pre-flight). [M1][QW][CA] β†’ Test: conftest verify suite (Conftest Test Suite X3) β€” every rule has pass + fail fixture. Manual runbook: docs/runbooks/test/D3.md. Signed off 26/05.

  • 🟦 Kyverno AKS Policy Bundle (D4) β€” Kyverno policy bundle for AKS β€” 9 ClusterPolicies (disallow-latest-tag, require-signed-images, require-pod-labels, disallow-privileged-containers, require-network-policy with generate of default-deny NetPol, require-resource-limits, require-non-root, disallow-host-namespaces, require-probes). All Enforce. System namespaces excluded via exclude.any.resources.namespaces. [M2a][CO] β†’ Test: kyverno test per rule (Kyverno Test Framework X4) β€” 9 suites / 35 assertions. Live admission round-trip is manual. Manual runbook: docs/runbooks/test/D4.md.

  • 🟦 Policy Waiver Engine (D5) β€” waivers/ + policy/opa/rules/main.rego waiver engine β€” time-boxed OPA exception records (waivers/exceptions.yaml: rule_prefix + resource_address + expiry_date + owner + justification) + CI enforcement. The Terraform OPA Policy Bundle (D3) rule files (tags/locations/network/encryption/cost) now emit raw_violation; main.rego filters them through has_active_waiver (suppress matching, non-expired) and hard-denies expired waivers (snowops.waiver_expired) so they fail the pipeline. Wired into terraform-plan-apply.yml via conftest test plan.json --data waivers/exceptions.yaml. [M2b][CA] 🟦 code-complete (external, gemini-work PR #13). Runbook docs/runbooks/test/D5.md. β†’ Test: unexpired waiver suppresses a D3 finding; expired waiver causes CI failure.


E. Automated Evidence Collection [A]

  • 🟦 Compliance Snapshot Collector (E0) β€” apps/evidence-collector/ β€” Lightweight compliance snapshot [B]: read-only TS tool collecting Azure Policy compliance state (summarize) + Defender secure score (secureScores/ascScore) into a versioned JSON artifact (schemaVersion 1.0). diffSnapshots is the regression signal. Wired into the Terraform Plan/Apply Pipeline (C1) as continue-on-error post-apply step. Reader + Security Reader only. [M2b][SH] ⬜ in scope (14-asset core) β†’ Test: jest (3 suites / 18 tests) β€” required-field validation, seeded-policy-violation diff, markdown render. Manual runbook: docs/runbooks/test/E0.md.

  • 🟦 Evidence Platform Interface (E1) β€” EvidencePlatform TS interface + EvidenceUpsertApi seam + DryRunEvidencePlatform + KeyedEvidencePlatform + selectEvidencePlatform factory (apps/evidence-collector/src/platforms/). The evidence-chain equivalent of E7's TicketPlatform: one contract, one shared keyed upsert algorithm, thin per-platform adapters. [M4][SO] 🟦 code-complete (v0.61) β†’ Test: jest β€” controlKey, KeyedEvidencePlatform create/update/idempotence, DryRunEvidencePlatform, factory resolution.

  • 🟦 Vanta Adapter (E2) β€” VantaApi implements EvidenceUpsertApi over the Vanta REST API (apps/evidence-collector/src/platforms/vanta.ts). Pushes test results keyed by framework:controlId; vantaPlatform() factory. Resolves from VANTA_API_KEY + VANTA_ACCOUNT_ID. Injectable FetchLike for unit tests. [M4][SO] 🟦 code-complete (v0.61) β†’ Test: jest β€” listExisting mapping, create (PASS/FAIL/NOT_APPLICABLE status), update URL, source_ref, idempotency, non-2xx errors. Runbook: docs/runbooks/test/E2.md.

  • 🟦 Drata Adapter (E3) β€” DrataApi implements EvidenceUpsertApi stub over the Drata REST API (apps/evidence-collector/src/platforms/drata.ts). DRATA_CONTROL_MAP maps framework:controlId β†’ Drata check IDs. drataPlatform() factory. [M4][SO] 🟦 code-complete (v0.61) β€” stub (API paths subject to validation against live workspace) β†’ Test: jest β€” listExisting filtering, create with/without controlMap, update, idempotency. Runbook: docs/runbooks/test/E3.md.

  • 🟦 Resource Graph Query Library (E4) β€” 7 pre-built KQL queries (apps/evidence-collector/src/resource-graph/) mapped to SOC 2 CC + ISO 27001 A.x controls: storage CMK, public network access, TLS enforcement, owner role assignments, Defender plans, diagnostic settings, required tags. runAllQueries() client + mapQueryResultsToEvidence() mapper β†’ EvidenceItem[]. [M4][SH] 🟦 code-complete (v0.61) β†’ Test: jest (query evaluators Γ— 7 + mapper). Runbook: docs/runbooks/test/E4.md.

  • 🟦 Evidence Sync (E5) β€” syncEvidence() (apps/evidence-collector/src/sync.ts) maps E0 snapshot (policy compliance % β†’ SOC2 CC7.1 + ISO A.8.15; Defender score β†’ SOC2 CC7.1) + E4 resource-graph results to EvidenceItem[] and pushes via any E1 platform. Configurable pass thresholds. Scheduled via .github/workflows/evidence-sync.yml (weekly Monday 02:00 UTC). [M4][SO] 🟦 code-complete (v0.61) β†’ Test: jest β€” snapshot items, pass/fail thresholds, null score β†’ not_applicable, resource-graph items, error isolation, empty options. Runbook: docs/runbooks/test/E5.md.

  • 🟦 Access Review Automation (E6) β€” apps/access-review/ β€” quarterly Azure RBAC access review: AzureCliCollector (live az rest) or FixtureCollector (offline); buildReview() sorts high-privilege first; renderCsv() (RFC 4180, reviewer-fillable action column) + renderMarkdown() (high-privilege section, summary table); tickets via E7 snowops-ticket CLI. Artifact committed to compliance/access-reviews/<period>/. Scheduled quarterly via .github/workflows/access-review.yml. [M4][SO] 🟦 code-complete (v0.61) β†’ Test: jest β€” FixtureCollector, buildReview sorting + schemaVersion, renderCsv header/rows/escaping/empty-action, renderMarkdown high-priv section/"None found", quarterLabel Q1–Q4. Runbook: docs/runbooks/test/E6.md.

  • 🟦 Ticket Platform Library (E7) β€” apps/ticket-platform/ (@snowops/ticket-platform) β€” platform-neutral TicketPlatform interface + adapters (GitHub Issues, Jira REST v2, Linear GraphQL, Azure DevOps Boards) + DryRunTicketPlatform + selectPlatform factory + snowops-ticket CLI. One shared marker-based upsertByMarker (idempotent create-or-update via an HTML-comment dedupe marker); each adapter implements the same MarkerUpsertApi seam (listOpen/create/update) with an injectable fetch. Generalizes the Drift Detector (S1) seed (D15/D39); closes G8. [M3][CO/CA] 🟦 code-complete (v0.54) β†’ Test: npm test (6 suites / 26 β€” shared upsert + each adapter's HTTP mapping + factory) + dry-run CLI. Runbook: docs/runbooks/test/E7.md.

F. SnowOps Module Library (Azure-First, Cloud-Agnostic Contracts) [B]/[A]

Sequencing constraint: the Cloud-Agnostic Module Contracts (F0) must land before any new F-module. The Azure Baseline (F1) and State Backend (F6) were retrofitted in v0.17.

  • 🟦 Cloud-Agnostic Module Contracts (F0) β€” modules/_contracts/ β€” 7 contracts: network, identity, cluster, registry, kv, observability, object_store. Each: typed variable "candidate" + echoing output "candidate" + no providers. [M2a][B][SO] β†’ Test: every contract validates standalone; F1/F6 conformance tests; 4 negative-literal tests. Manual runbook: docs/runbooks/test/F0.md.

  • 🟦 Azure Baseline Module (F1) β€” modules/azure/baseline/ (Mgmt Group, Subs, Policy, Defender, Log Analytics, Activity Log). Emits identity_contract + observability_contract. [M2a][B][CO] β†’ Test: Terratest applies to sandbox; asserts policy assignment + Defender plans. Manual runbook: docs/runbooks/test/F1.md.

  • 🟦 Network Hub Module (F2) β€” modules/azure/network-hub/ (hub-spoke vNets, optional Azure Firewall, optional Private DNS zones, NSG flow logs to the Azure Baseline (F1), per-spoke route-table forcing 0.0.0.0/0 through firewall). Emits F0 spoke_network_contracts map. [M2a][B][CO] β†’ Test: Terratest validates topology + routing. Manual runbook: docs/runbooks/test/F2.md.

  • 🟦 Secure AKS Module (F3) β€” modules/azure/aks-secure/ (private AKS, Workload Identity, OIDC issuer, AAD-RBAC + local accounts disabled, Azure CNI Overlay + Calico NetworkPolicy, Defender for Containers, KEDA, Image Cleaner, AKV CSI driver, system + user node pools across 3 AZs on AzureLinux+Ephemeral OS). Emits F0 cluster_contract. [M2a][B][CO] β†’ Test: Terratest provisions cluster; AAD-only kubectl smoke; private API confirmed. Manual runbook: docs/runbooks/test/F3.md.

  • 🟦 Container Registry Module (F4) β€” modules/azure/acr/ (Premium SKU, Private Endpoint + auto-A-record in privatelink.azurecr.io, AAD-only auth, public access disabled, optional geo-replication + AcrPull bindings + Defender scanning passthrough). Emits F0 registry_contract. [M2a][B][CO] β†’ Test: Terratest provisions; push + sign sample image; pull from private endpoint only. Manual runbook: docs/runbooks/test/F4.md.

  • 🟦 Key Vault Module (F5) β€” modules/azure/key-vault/ (Premium SKU default, RBAC mode enforced, purge protection enforced, default-deny network ACLs + AzureServices bypass, Private Endpoint + auto-A-record in privatelink.vaultcore.azure.net, optional role bindings across 5 built-in KV roles, optional diag forward). Emits F0 kv_contract. [M2a][B][CO] β†’ Test: Terratest; secret CRUD via Workload Identity; public access denied. Manual runbook: docs/runbooks/test/F5.md.

  • 🟦 State Backend Module (F6) β€” modules/azure/state-backend/ (state SA + container per env, used by the Client State Backend (B4)). Emits object_store_contract. [M2a][B][CO] β†’ Test: Terratest applies; init a dummy Terraform stack against it; lease lock observed. Manual runbook: docs/runbooks/test/F6.md.

  • 🟦 Terragrunt Live-Infra Reference (F7) β€” live/ β€” Terragrunt live-infra reference: root.hcl (remote state in the State Backend (F6), generated OIDC azurerm provider, common Β§3 tags) + DRY _envcommon/ templates (Azure Baseline F1, Network Hub F2, Key Vault F5, Container Registry F4) + bootstrap/ (F6 state account, local state β€” breaks the chicken-and-egg) + per-env/per-region units (prod eastus full chain + westus2, staging, sandbox). Real dependency DAG: baseline β†’ network-hub/key-vault/acr via dependency.baseline.outputs.log_analytics_workspace_id (mock_outputs for pre-apply plan). Variance isolated in env.hcl/region.hcl; units are 3-line includes. In-repo source via get_repo_root(); Module Registry (F11) registry-pin form documented for external use. Offline live/validate.sh structural gate + terragrunt hcl validate. [M2b][B][CO] 🟦 code-complete (v0.55) β†’ Test: live/validate.sh (offline structural gate, A) + terragrunt hcl validate/hclfmt --check (B); live: run-all plan/apply against sandbox (C). Runbook: docs/runbooks/test/F7.md.

  • 🟦 ArgoCD GitOps Bundle (F8) β€” gitops/ β€” K8s reference manifests bundle as ArgoCD app-of-apps (cert-manager + Kyverno + ESO wave 0 β†’ ingress-nginx wave 1 β†’ Kyverno AKS Policy Bundle (D4) policies + ClusterSecretStore wave 2). The D4 bundle is reused not forked. [M2b][B][CO] β†’ Test: gitops/validate.sh (offline β€” 13 files / 7 Applications) + kyverno test. Live kind-cluster bootstrap is runbook. Manual runbook: docs/runbooks/test/F8.md.

  • ⬜ AWS Module Parity (F9) β€” modules/aws/* parity. [M5][B]/[A] ⏸️ deferred

  • ⬜ GCP Module Parity (F10) β€” modules/gcp/* parity. [M5][B]/[A] ⏸️ deferred
  • 🟦 Module Registry & Versioning (F11) β€” apps/module-registry/ + modules/registry.json + per-module CHANGELOG.md + .github/workflows/module-release.yml β€” module versioning + private Terraform registry. Private registry = the monorepo itself: modules publish as git tags <module>/v<version>, consumers pin via source = "git::…//<path>?ref=<module>/vX.Y.Z" (no hosted service). Manifest is the source of truth (10 modules: F0 0.1.0; F1–F6 + J1/J2/J6 1.0.0). TS tool (Client Prerequisite Checker B6 / Automated Restore Drill L4 mold; pure core over a RegistrySnapshot behind a Collector seam): validate (unique names/paths, strict semver, CHANGELOG top == manifest version, no version-regression), buildIndex, planReleases, auditPins (flags unpinned/ref-mismatch/unknown-version in a consumer tree). module-release workflow tags + GitHub-Releases pending modules on merge to main (CHANGELOG section as body, idempotent). 3 jest suites / 27 tests incl. a guard over the real manifest+CHANGELOGs. Convention: docs/conventions/module-versioning.md. [M3][SO] 🟦 code-complete (v0.51) β†’ Test: npm test (27); --manifest modules/registry.json --fail-on-issues β†’ OK/exit 0; --consumer-dir examples/consumer-unpinned --fail-on-issues β†’ exit 2. Manual runbook: docs/runbooks/test/F11.md.
  • 🟦 Brownfield Import Library (F12) β€” modules/azure/import-blocks/ β€” brownfield import library [B]: config-driven Terraform import {} blocks (one <module>.tf per module) that adopt pre-existing Azure resources into the F-modules β€” covers Azure Baseline F1, Network Hub F2, Secure AKS F3, Container Registry F4, Key Vault F5, State Backend F6 + Log Analytics J1, Policy Diagnostics J2, Audit Log Archive J6 (9 modules). Each file pairs the import blocks with a placeholder module call so the whole directory is self-validating β€” terraform validate confirms every to = address resolves (incl. count[0] + for_each["key"] instances); for_each key schemes are derived from source + documented per file. Offline TestImportBlocksValidate gate. Each covered module's README brownfield section now points at its real file; adoption procedure in docs/runbooks/import/F12.md. [M3][B][CO] 🟦 code-complete (v0.49) β†’ Test: docs/runbooks/import/F12.md β€” offline validate/fmt + TestImportBlocksValidate (A+B); live: adopt a real sandbox resource with a zero-change plan (C).

G. Pre-Sales Discovery & Audit Automation [X][SH]

Azure-only through M3. AWS Discovery Mode (G7) lands in M4.

  • 🟦 Discovery SP Bootstrap (G0) β€” Client-side scoped Reader + Security Reader SP bootstrap script + Bicep alt; time-boxed federated cred; no secrets leave client tenant. [M1]
  • 🟦 Discovery Auditor Collectors (G1) β€” apps/discovery-auditor/ (Node/TS) collectors: Resource Graph KQL, Defender REST, Azure Policy state, AAD audit logs, Cost Mgmt. [M1]
  • 🟦 Discovery Rule Pack (G2) β€” YAML rule pack mapped to SOC2 CC + ISO27001 A.x + CIS Azure Benchmark; severity/evidence/remediation/effort. Each finding includes remediation_asset_id. 11 rules / 22 fixtures. [M1]
  • 🟦 Discovery Report Renderer (G3) β€” Report renderer (Markdown β†’ PDF via Pandoc/Playwright; branded cover, exec summary, control table, prioritized roadmap). [M1]
  • 🟦 Discovery Run Workflow (G4) β€” .github/workflows/discovery-run.yml (manual dispatch with tenant_id + sub_id; artifact upload; Slack notify with reviewer checklist). [M1]
  • 🟦 Discovery HubSpot Integration (G5) β€” HubSpot integration (Discovery Trigger Automation (A5) β†’ Deal property discovery_report_url). [M1]
  • 🟦 Discovery Audit Log (G6) β€” Immutable run audit log (client/scope/timestamp/operator/findings hash β†’ WORM blob, SHA-256 hash chain). [M1]
  • ⬜ AWS Discovery Mode (G7) β€” AWS discovery mode. [M4][X][SH] ⏸️ postponed

H. Identity & Access Management [B]/[A]

  • 🟦 Entra ID Baseline Module (H1) β€” modules/azure/aad-baseline/ (IP + country named locations via azuread_named_location, custom Authentication Strength Policy for phishing-resistant MFA, password protection + tenant branding emitted as *_patch_body JSON for az rest PATCH). Precondition: verified custom domain required. [M2a][B][CO] β†’ Test: Terratest validate; the H1 runbook applies in sandbox + applies Graph PATCHes.

  • 🟦 Conditional Access Module (H2) β€” modules/azure/conditional-access/ (6 SnowOps CA policies: MFA Mandatory / Tier-0 Phishing-Resistant+Compliant Device / Block Legacy Auth / Geo-Block / High-Risk Block / Medium-Risk MFA; every policy excludes break-glass group; risk policies gated on P2). [M2a][B][CO] β†’ Test: Terratest validate; the H2 runbook applies in report-only β†’ CA What-If β†’ enforce + live sign-in.

  • 🟦 Entra PIM Templates Module (H3) β€” modules/azure/pim-templates/ (tier-0 + tier-1 AAD role eligibility via azuread_directory_role_eligibility_schedule_request; activation rule bodies emitted as JSON for Graph az rest PATCH since roleManagementPolicies has no TF resource). Precondition: β‰₯1 permanent break-glass tier-0 holder. [M2a][B][CO] β†’ Test: Terratest validate; the H3 runbook applies eligibility + Graph PATCHes + live activation drill.

  • 🟦 SCIM Provisioning (H4) β€” modules/azure/scim-provisioning/ (azuread) β€” Entra automatic user provisioning to a SaaS app over SCIM: instantiates the gallery app (azuread_application_from_template), sets the SCIM endpoint + bearer token (azuread_synchronization_secret, sensitive), and starts the provisioning job (azuread_synchronization_job). Joiner/mover/leaver lifecycle flows from Entra automatically. [M4][A][CO] 🟦 code-complete (v0.70) β†’ Test: offline TestScimProvisioningValidate. Runbook: docs/runbooks/test/H4.md.

  • 🟦 Service Principal Inventory (H5) β€” apps/sp-inventory/ (read-only-Graph TS, Application.Read.All) + .github/workflows/sp-inventory-rotation.yml (scheduled reusable workflow). Inventories app registration credentials; flags aged (β‰₯ threshold_days=90) / expiring-soon (within expiry_warning_days=30) / expired. Opens/idempotently updates rotation PR. Never rotates a secret itself. Federated-OIDC-only SPs never stale. 2 test suites / 19 tests. [M2a][B][CA] β†’ Test: jest unit suite covers stale SP path + federated-OIDC-only SP path. Live tenant read + PR drill is manual.

  • 🟦 Access Review Automation (H6) β€” modules/azure/access-reviews/ (azuread) β€” recurring Entra access reviews via Entitlement Management: a catalog + access packages, each with an azuread_access_package_assignment_policy carrying assignment_review_settings (frequency, duration, reviewers, timeout behavior). The native-config counterpart to E6 (apps/access-review, the evidence/reporting app). Completes the H series. [M4][A][CO] 🟦 code-complete (v0.70) β†’ Test: offline TestAccessReviewsValidate. Runbook: docs/runbooks/test/H6.md.

  • 🟦 Break-Glass Account Module (H7) β€” modules/azure/break-glass/ β€” dual-provider (azuread + azurerm). Role-assignable group + azuread_group_member per member + permanent (active, non-PIM) Global Administrator + severity-0 sign-in alert (azurerm_monitor_scheduled_query_rules_alert_v2, KQL on UserId, threshold=0). Producer of break-glass group the Conditional Access (H2) / Subscription Baseline (B3) / Azure Resource PIM (B5) modules consume. Takes existing account object IDs as input (no account or password creation β€” Identity > Secrets). [M2a][B][CO] β†’ Test: Terratest validate (offline β€” 4 preconditions). Live sign-in drill is manual (needs P1 + real LAW).


I. Vulnerability & Patch Management [B]/[A]

  • 🟦 Container Image Scan (I1) β€” .github/workflows/image-scan.yml β€” reusable (workflow_call) Trivy image scan; fails on High/Critical OS+library CVEs, SARIF β†’ Code Scanning, optional registry login, ignore_unfixed/fail_on_findings/severity_cutoff inputs. Closes G6 (container security for non-K8s clients). Distinct from the Container Build & Sign Pipeline (C2, build-time grype): I1 scans an arbitrary image ref. [M2a][B][CA] 🟦 code-complete (v0.53) β†’ Test: YAML lint (offline) + dispatch scan of an old image fails / current passes. Runbook: docs/runbooks/test/I1.md.
  • 🟦 Dependency Scanning (I2) β€” .github/dependabot.yml (4 ecosystems) + .github/workflows/dependency-review.yml (PR-blocking SCA gate, fail-on-severity: high + licence deny-list) + .github/workflows/dependency-digest.yml (weekly idempotent Dependabot-alert digest issue). [M2a][B][CA] 🟦 code-complete (v0.53) β†’ Test: config lint (offline) + PR introduces vuln dep β†’ review fails; digest run upserts one rolling issue. Runbook: docs/runbooks/test/I2.md.
  • 🟦 CodeQL SAST (I3) β€” .github/workflows/codeql.yml β€” CodeQL SAST over javascript-typescript (apps/) + go (terratest), security-extended,security-and-quality queries, PR + push + weekly schedule, SARIF β†’ Code Scanning. [M2a][B][CA] 🟦 code-complete (v0.53) β†’ Test: YAML lint (offline) + PR with a planted CWE finding surfaces in Code Scanning. Runbook: docs/runbooks/test/I3.md.
  • 🟦 Defender Ticketer (I5) β€” apps/defender-ticketer/ β€” Defender for Cloud alerts β†’ tickets via the Ticket Platform Library CLI (E7); see docs/context/04-asset-status.md. [M2b][A] 🟦 code-complete
  • 🟦 DAST (I4) β€” .github/workflows/dast-scan.yml β€” reusable workflow_call OWASP ZAP gate (baseline|full) against a deployed app URL, fails on High web findings, uploads the ZAP report; tuned by a committed .zap/rules.tsv. The dynamic-analysis layer (I1 images / I3 SAST are at-rest). [M4][A] 🟦 code-complete (v0.68) β†’ Test: YAML lint + rules.tsv shape (offline); live ZAP scan vs a vulnerable target. Runbook: docs/runbooks/test/I4.md.
  • 🟦 Azure Update Manager Report (I6) β€” apps/patch-compliance/ β€” pure adaptβ†’render (S4/L5 mold): an Azure Update Manager assessment β†’ patch-compliance report (coverage %, machines out of compliance, pending Critical/Security counts, SLA aging); self-contained HTML + optional Pandoc PDF; never fabricates a patch date. [M4][A] 🟦 code-complete (v0.68) β†’ Test: jest (coverage math, compliant-rule, SLA breach, honest-empty, HTML self-containment, determinism). Runbook: docs/runbooks/test/I6.md.
  • 🟦 CVE Triage Runbook (I7) β€” docs/runbooks/vuln/cve-triage.md (triage β†’ severity β†’ SLA Critical 7d/High 30d/Medium 90d β†’ owner/E7 ticket β†’ remediate or D5 waiver β†’ verify) + the vuln-triage Grafana dashboard (snowops-vuln-triage.json: open Defender findings by severity via ARG + SLA aging from the exported SecurityRecommendation table). Finishes the I vulnerability series. [M4][A] 🟦 code-complete (v0.68) β†’ Test: J5 dashboards-as-code gate covers the new JSON; runbook docs/runbooks/test/I7.md.

J. Logging, Monitoring & SIEM [B]/[A]

  • 🟦 Log Analytics Module (J1) β€” modules/azure/log-analytics/ β€” standalone hardened LAW: per-table retention (30-730d interactive + archive, total >= retention validated), CanNotDelete management lock, scoped RBAC (Log Analytics Reader/Contributor + Monitoring Reader + freeform), self-audit azurerm_monitor_diagnostic_setting (who ran KQL queries). AAD-only by default. Optional daily_quota_gb cost cap. Emits F0 observability_contract. azurerm-only. [M2a][B][CO] β†’ Test: Terratest validate + TestJ1ObservabilityContractConformance + build-tagged integration (~$0) asserts workspace + self-audit diag + contract shape.

  • 🟦 Policy Diagnostics Module (J2) β€” modules/azure/policy-diagnostics/ β€” custom azurerm_policy_set_definition (DINE initiative, not Deny) bundling built-in DeployIfNotExists diagnostic policies. GUID-agnostic (caller supplies GUIDs via diagnostic_policies input map sourced from az policy definition list). Sub- or MG-scope. System-assigned identity + remediation roles. Emits az policy remediation create command. Validate-only in CI. [M2a][B][CO] β†’ Test: Terratest validate (offline). Live apply + DINE remediation drill is manual.

  • 🟦 Microsoft Sentinel Deployment (J3) β€” modules/azure/sentinel/ β€” the SIEM layer over the Log Analytics workspace (J1): onboards Microsoft Sentinel onto the CONSUMED workspace (by ARM ID), toggleable first-party data connectors (Entra ID / Defender for Cloud / Threat Intelligence / Office 365 / MDCA, all default off), and curated Sentinel-native scheduled analytics rules β†’ incidents with entity mappings + MITRE tactics (identity + privilege domains; per-domain toggles + per-rule overrides + custom_rules escape hatch), plus an opt-in Microsoft incident-creation rule promoting Defender alerts to incidents. Scope boundary vs the Alert Rule Pack (J4): J4 = Monitor scheduled-query alerts β†’ action groups (paging); J3 = Sentinel analytics rules β†’ incidents the SOC + Sentinel SOAR Playbooks (K3) act on. Unblocks K3 + Managed Grafana Dashboards (J5). Offline TestSentinelValidate. [M4][A][CO] 🟦 code-complete (v0.66) β†’ Test: docs/runbooks/test/J3.md β€” fmt/validate + Terratest validate (A+B); live: onboard + ingest logs + watch an analytics rule raise an incident in a sandbox workspace (C).

  • 🟦 Alert Rule Pack (J4) β€” modules/azure/alert-rule-pack/ β€” curated scheduled-query alert rules across identity/network/privilege/data-exfil; consumes the Log Analytics workspace (J1) + On-Call Integration (K2) action groups by ARM ID. [M2b][B][CO] 🟦 code-complete (v0.56)
  • 🟦 Managed Grafana Dashboards (J5) β€” modules/azure/grafana-dashboards/ β€” Azure Managed Grafana + dashboards-as-code over the Log Analytics workspace (J1): azurerm_dashboard_grafana instance (SystemAssigned identity) + a Monitoring Reader grant to the identity (so panels can query Azure Monitor + Log Analytics) + a Grafana Admin/Editor/Viewer Azure-role fan-out to caller principals, plus version-controlled dashboard JSON under dashboards/ (Security Posture; Reliability & Cost β€” reading the workspace via a workspace template variable). Dashboards-as-code = committed JSON + an always-on offline gate (scripts/validate.sh) + an idempotent az grafana dashboard create deploy script β€” the same "infra + declarative artifacts + validate.sh + tooling deploy" shape as the ArgoCD bundle (F8). Offline TestGrafanaDashboardsValidate + the dashboards JSON gate. [M4][A][CO] 🟦 code-complete (v0.66) β†’ Test: docs/runbooks/test/J5.md β€” fmt/validate + dashboards gate + Terratest validate (A+B); live: stand up the instance, deploy dashboards, confirm a panel queries the workspace, destroy (C).

  • 🟦 Audit Log Archive Module (J6) β€” modules/azure/audit-log-archive/ β€” RA-GZRS StorageV2 with account-level time-based immutability (allow_protected_append_writes = true, state defaults Unlocked for teardown safety). Forwards subscription Activity Log; optional Log Analytics data export; optional Storage Blob Data Reader grants. shared_access_key_enabled defaults true (platform diagnostic writer requires it). Distinct from the State Backend (F6). [M2a][B][CO] β†’ Test: Terratest validate + build-tagged integration (~$0, immutability OFF). WORM mutation-refused drill is manual.

  • 🟦 Cost-Controlled Log Strategy (J7) β€” modules/azure/log-analytics-cost/ β€” applies a Basic-Logs-tier cost strategy to an existing J1 workspace by ID: per-table plan (Basic vs Analytics) + retention via azurerm_log_analytics_workspace_table, a curated overridable default_strategy (security/audit tables β†’ Analytics for KQL + alerting; high-volume telemetry β†’ Basic), and an advisory daily-quota note (J7 does not re-manage the workspace; the cap belongs on J1's daily_quota_gb). States the Basic-tier mechanism (fixed 8-day interactive retention; can't back log alerts) rather than fabricating dollar figures. [M4][A][CO] β†’ Test: offline TestLogAnalyticsCostValidate (Terratest validate). Manual runbook: docs/runbooks/test/J7.md.

  • ⬜ Client Overview Dashboard (J8) β€” Tier 1 of the client-facing posture-visibility initiative (D65). A curated, read-only client-facing Grafana dashboard added to the Managed Grafana Dashboards (J5) module (modules/azure/grafana-dashboards/dashboards/snowops-client-overview.json) in a dedicated "client" Grafana folder, with the client's AAD group granted Grafana Viewer via J5's existing grafana_viewers input. Panels are organized by SnowOps deliverable, not by raw metric β€” guardrails (policy compliance %, Defender Secure Score), data protection (backup job status L1, last restore drill L4), compliance posture (framework rollup), incidents handled (open Sentinel incidents J3), cost governance (budget vs. actual U1) β€” each panel degrades honestly when its source table isn't flowing (the same stance as J5's existing dashboards). Reuses J5 wholesale (instance, Monitoring Reader grant, the offline dashboards-as-code gate scripts/validate.sh, the az grafana dashboard create deploy script); the only new artifacts are one dashboard JSON + the client-folder/Viewer access pattern. No multi-tenant lift β€” the per-client-subscription model (B-series) means each client's J5 instance is already isolated; the shared cross-client portal is the postponed W-series (W5, D35). Shares the section taxonomy with the Client Cloud Posture Report (V5) β€” J8 is the live view, V5 the periodic snapshot. Shipped: one dashboard JSON (passes the J5 offline gate), a distinct client_grafana_viewers access hook (auditable apart from internal-ops viewers), and a folder-aware deploy script that routes snowops-client-* dashboards into a dedicated client folder; cost (U1) is omitted rather than fabricated (no FinOps feed yet β€” U4/U5 postponed). [M4][A][SH] 🟦 code-complete (v0.67) β†’ Test: J5 dashboards-as-code gate covers the new JSON; offline TestGrafanaDashboardsValidate exercises client_grafana_viewers. Manual runbook: docs/runbooks/test/J8.md. β†’ Test: extends J5's offline dashboards gate (validates the new JSON automatically); live access proof (client group sees only the client folder, read-only) is the J8 runbook Part C.

K. Incident Response & SecOps [B]/[A]

  • 🟦 Incident Response Runbooks (K1) β€” IR runbook library (docs/runbooks/incident/: compromise, ransomware, data leak, DDoS, vendor breach). [M2b][B][CO] 🟦 code-complete (external, gemini-work PR #12)
  • 🟦 On-Call Integration Module (K2) β€” modules/azure/oncall-integration/ β€” PagerDuty/Opsgenie + Slack (Sentinel incidents β†’ on-call). [M2b][B][CO] 🟦 code-complete (external, gemini-work PR #12)
  • 🟦 Sentinel SOAR Playbooks (K3) β€” modules/azure/sentinel-soar/ β€” active incident-response containment playbooks (Logic Apps) for a Microsoft Sentinel Deployment (J3) incident: isolate-compromised-account (Graph PATCH user accountEnabled=false), disable-risky-service-principal (Graph PATCH SP), quarantine-vm (ARM PATCH the NIC to attach a deny-all quarantine NSG). Reuses the Auto-Remediation Playbooks (S3) structural dry_run/enforce split (Compose intent-record vs. real Http action via ManagedServiceIdentity, rendered at plan time β€” a dry_run playbook is structurally incapable of running containment); the K3 addition is a per-playbook Graph/ARM audience. HTTP-trigger accepts the incident+entity payload; ships the playbooks (wiring to incidents via a Sentinel automation rule is the out-of-band step, same stance as S3). Retires the apps/soar-playbooks/ scaffold (G20). Scope vs S3: S3 fixes drift findings (low blast radius), K3 contains active incidents (high). Offline TestSentinelSoarValidate. [M4][A][CO] 🟦 code-complete (v0.66) β†’ Test: docs/runbooks/test/K3.md β€” fmt/validate + Terratest validate (A+B); live: apply the pack, prove every dry_run definition is a Compose with no Http action, destroy (C).
  • 🟦 Post-Incident Review (K4) β€” apps/post-incident-review/ β€” turns closed security/ops incidents into blameless post-incident review docs + idempotent tracking issues via the Ticket Platform Library (E7) CLI bridge (third E7 consumer, the Defender Ticketer (I5)/Production Change Log (R2) mold: pure core behind a collector seam, E7 consumed at run time). Defensive normalize across the az sentinel incident list shape + a flat shape; filters to closed ∧ β‰₯severity floor; computes time-to-detect / MTTR / duration from real timestamps; renders a standard postmortem (supplied root-cause/timeline render verbatim, missing sections become honest "to be completed" prompts β€” never fabricates a root cause). Dedupe key post-incident-review=<incidentId>. 23 jest tests. [M4][A][CA] 🟦 code-complete (v0.66) β†’ Test: docs/runbooks/test/K4.md β€” build/typecheck/test (A) + offline dry-run on the sample (B, 1/3 reviewed at Medium floor); live: real closed Sentinel incidents β†’ issues via E7, second run updates (C).
  • 🟦 Tabletop Exercise Pack (K5) β€” docs/runbooks/tabletop/ β€” five facilitated, discussion-based IR rehearsals (account compromise / ransomware / data exfiltration / DDoS / vendor breach) mapped 1:1 to the K1 incident runbooks, each with timed injects, phase-by-phase discussion questions (Identification→…→Post-Incident), an evaluation rubric, and an after-action stub that seeds a K4 post-incident review. Ships a README + facilitation guide + scenario template + after-action template + an offline validate.py gate (required sections, all five K1 phases referenced, cross-links resolve). [M4][A][CO] β†’ Test: offline validate.sh/validate.py gate. Manual runbook: docs/runbooks/test/K5.md.

L. Backup & Disaster Recovery [B]/[A]

  • 🟦 Azure Backup Policy Module (L1) β€” modules/azure/backup-policy/ β€” Azure Backup policy module [B]: creates (toggleable) a GeoRedundant Recovery Services vault + a Data Protection Backup vault and the four per-env-retention backup policies β€” VM (azurerm_backup_policy_vm), Azure Files/"Storage" (azurerm_backup_policy_file_share), SQL-in-VM (azurerm_backup_policy_vm_workload), AKS (azurerm_data_protection_backup_policy_kubernetes_cluster). Per-env profiles (dev 7d / staging 14d+5w / prod 30d+12w+12m+7y) expand daily/weekly/monthly/yearly tiers via dynamic blocks; plan-time preconditions enforce CRRβ‡’GeoRedundant and the yearlyβ‡’monthlyβ‡’weekly nesting. Defines reusable policies (not per-instance bindings); vault MIs exported for consumers. GeoRedundant + cross_region_restore_enabled = the on-ramp to Cross-Region Replication (L2). Offline TestBackupPolicyValidate gate. [M2b][B][CO] 🟦 code-complete (v0.46) β†’ Test: docs/runbooks/test/L1.md β€” fmt/validate + Terratest validate (A+B); live: apply both vaults + four policies to sandbox, assert redundancy/retention, destroy (C).
  • 🟦 Cross-Region Replication Module (L2) β€” modules/azure/cross-region-replication/ β€” cross-region replication wiring [B]: blob object replication (azurerm_storage_object_replication, sourceβ†’DR account, rule per container mapping; optionally creates the destination containers) + geo-redundant SQL failover group (azurerm_mssql_failover_group, primary↔partner server, per-env failover posture). Consumes existing accounts/servers by ARM ID (brownfield-safe wiring, not resource creation β€” same stance as the Azure Backup Policy (L1)). Per-env SQL failover: dev Manual / staging Automatic 60m / prod Automatic 120m; preconditions enforce cross-region locations differ, distinct accounts/servers, and Automatic⇔grace / Manual⇔no-grace coherence. The active-replication half of DR; L1 is the recoverability half; the Automated Restore Drill (L4) is the drill. Offline TestCrossRegionReplicationValidate gate. [M2b][B][CO] 🟦 code-complete (v0.47) β†’ Test: docs/runbooks/test/L2.md β€” fmt/validate + Terratest validate (A+B); live: apply two storage accounts + two SQL servers + the links to sandbox, assert failover group Automatic/120m + cross-region, destroy (C).
  • 🟦 DR Runbook Templates (L3) β€” docs/runbooks/dr/ β€” copy-per-workload DR templates that turn the DR machinery into executable procedures: master dr-plan-template.md + scenarios (region-failover, data-restore, sql-failover, full-region-loss) with RTO/RPO tables, az commands, validation + fail-back + sign-off blocks. Completes the DR series with the Azure Backup Policy Module (L1), Cross-Region Replication Module (L2), and Automated Restore Drill (L4). [M4][A][CO] 🟦 code-complete (v0.62)
  • 🟦 Automated Restore Drill (L4) β€” apps/restore-drill/ β€” automated restore drill [B]: standalone TS tool (pure offline logic + thin executor seam + jest, same mold as the Compliance Snapshot Collector (E0) / Drift Detector (S1) / Compliance Dashboard (S2)) that restores an Azure Backup (L1) backup (or fails over a Cross-Region Replication (L2) SQL failover group) into an ephemeral sandbox RG β†’ validates β†’ tears down β†’ records a versioned RestoreDrillReport (schemaVersion 1.0). Outcome classified passed/partial/failed (partial = recovered but RTO missed or teardown failed); measured RTO = restore+validate duration; diffReports is the recoverability-regression signal. Executors: DryRunExecutor (deterministic β€” tests/demos/workflow rehearsal) + AzureCliExecutor (live az). Reports land in the compliance/restore-drills/ evidence store; the Compliance Dashboard (S2) gains an additive --restore-drills-dir "DR restore drills" panel (gated, so its golden output is unchanged) β€” that's how pass/fail reaches the dashboard. Scheduled via .github/workflows/restore-drill.yml (monthly cron; dispatch defaults to dry-run, schedule runs live; commits the report). Teardown always runs (Sandbox Cleanup (X7) backstop). [M2b][B][CO] 🟦 code-complete (v0.48) β†’ Test: docs/runbooks/test/L4.md β€” offline classify/orchestrate/render + S2 panel wiring (A+B, 17 + 34 tests); live: real restoreβ†’validateβ†’teardown in the sandbox, dated report to the evidence store (C).
  • 🟦 RTO/RPO Doc Generator (L5) β€” apps/rto-rpo-doc-generator/ β€” generates a versioned, evidence-backed RTO/RPO objectives document [A]: standalone TS tool (pure adapt β†’ render + jest, same mold as E0/S1/S2/L4) that merges the Azure Backup Policy retention posture (L1 retention_summary), the Cross-Region Replication posture (L2 replication_summary), and the Automated Restore Drill's versioned evidence (L4 RestoreDrillReports) into one markdown report. RPO is derived β€” never asserted beyond the configuration: backup-restored workloads get a minute bound from the shortest enabled L1 cadence (daily/weekly/monthly/yearly); continuously-replicating SQL failover-group workloads get the mechanism plus the configured grace window, deliberately not a fabricated minute figure. Documents only workloads with L4 drill evidence β€” "a backup you have never restored is a hope, not a backup" β€” with a per-workload table (defined vs measured RTO, status badge met/at-risk/failing), an inline RPO-derivation trace per row, and a gap-analysis section naming every at-risk/failing workload with a concrete next step. Completes the DR series with L1/L2/L3/L4. [M4][A][CO] 🟦 code-complete (v0.63) β†’ Test: docs/runbooks/test/L5.md β€” offline build/typecheck/test (15 tests) + fixture CLI run (posture + 3-workload table + gap analysis + honest empty state); no live phase (pure doc generator).

M. Data Protection & Privacy [B]/[A]

  • 🟦 Encryption Policy Module (M1) β€” modules/azure/encryption-policy/ β€” custom azurerm_policy_set_definition Deny initiative: encryption-at-rest built-ins (storage infrastructure encryption, SQL CMK, managed-disk double-encryption + CMK). Initiative-level effect parameter (Audit/Deny/Disabled). No system-assigned identity (Deny effect, not DINE). GUIDs caller-overridable. [M2a][B][CO] β†’ Test: Terratest validate (offline). Live Auditβ†’Deny rollout + "unencrypted create denied" is manual.

  • 🟦 Customer-Managed Key Module (M2) β€” modules/azure/cmk/ β€” Customer-Managed Key: HSM-backed azurerm_key_vault_key (RSA-HSM/EC-HSM only, software keys rejected) + auto-rotation policy (rotate_before_expiry_days < expire_after_days precondition) in an EXISTING Key Vault (F5) Premium RBAC-mode vault. Optional user-assigned identity auto-granted Crypto Service Encryption User. Consumers wire to versionless key ID for transparent rotation. [M2a][B][CO] β†’ Test: Terratest validate + TestCMKModule integration (~$1 β€” Premium vault + deployer Crypto Officer + HSM key, asserts versionless ID + rotation policy).

  • 🟦 TLS Policy Module (M3) β€” modules/azure/tls-policy/ β€” custom Deny initiative: secure-transport built-ins (storage secure-transfer, storage min-TLS, App Service + Function HTTPS-only). Two initiative parameters: effect + minimumTlsVersion. The storage_min_tls reference threads BOTH via explicit parameter_values. [M2a][B][CO] β†’ Test: Terratest validate (offline). Live Auditβ†’Deny + "HTTP/TLS<1.2 create denied" is manual.

  • 🟦 Microsoft Purview Baseline (M4) β€” modules/azure/purview-baseline/ β€” the data-classification foundation: azurerm_purview_account (system identity) + self-audit diagnostics to the J1 workspace + Reader/Storage Blob Data Reader grants over caller-supplied scan_scopes so the account can register + scan in-subscription sources. Honest boundary: collections/scans/classifications are data-plane (governance portal/REST) β€” Terraform owns the account + access. Cost note: bills per capacity unit even when idle; destroy in sandboxes. [M4][A][CO] 🟦 code-complete (v0.71) β†’ Test: offline TestPurviewBaselineValidate. Runbook: docs/runbooks/test/M4.md.

  • 🟦 DLP Policies (M5) β€” policy/dlp/ β€” M365 DLP as policy-as-code (Terraform can't manage M365 DLP): three committed YAML policy definitions (PII-financial, PII-personal, PHI β€” audit-first TestWithNotifications mode, the enforce=falseβ†’true stance) + a pure-stdlib offline gate (validate.py + unit tests) + an idempotent deploy-dlp.ps1 (Security & Compliance New/Set-DlpCompliancePolicy/Rule). The "cloud storage" half of DLP is already enforced by N5/M1/M3/M6 β€” the README states the division. [M4][A][CO] 🟦 code-complete (v0.71) β†’ Test: policy/dlp/validate.sh + unit tests (offline); live deploy + test-email detection is Part C. Runbook: docs/runbooks/test/M5.md.
  • 🟦 GDPR-CCPA Evidence (M7) β€” compliance/data-inventory/ (RoPA register: per-activity system/owner/purpose/lawful-basis/retention-with-basis/residency/processors-with-DPA/dsar_lookup; PyYAML offline gate, fail-closed on enum/field violations) + docs/runbooks/privacy/dsar-workflow.md (GDPR 30-day / CCPA 45-day clocks, identity-verify-first, mechanical inventory walk, documented retention exemptions, honest backups statement) + per-request evidence records in compliance/dsar/. Finishes the M series. [M4][A][CO] 🟦 code-complete (v0.71) β†’ Test: python3 compliance/data-inventory/validate.py + worked DSAR dry-run. Runbook: docs/runbooks/test/M7.md.

  • 🟦 Data Residency Policy Module (M6) β€” modules/azure/data-residency-policy/ β€” custom Deny initiative: Allowed-locations built-ins (resources + optional resource groups) with listOfAllowedLocations. No effect parameter (Allowed-locations is intrinsic-Deny); rollout uses enforce=falseβ†’true. Standalone residency boundary distinct from the Azure Baseline (F1)'s bundled allowed-locations. [M2a][B][CO] β†’ Test: Terratest validate (offline). enforce=falseβ†’true + "out-of-region create denied" is manual.


N. Network Security [B]/[A]

  • 🟦 Landing-Zone Connectivity (N1) β€” modules/azure/landing-zone-connectivity/ β€” the connectivity-subscription edge F2 lacks, attached to the existing F2 hub vNet by name: Azure Bastion (+ AzureBastionSubnet + Standard PIP) always; an optional VPN/ExpressRoute gateway (+ GatewaySubnet) and optional Private DNS Resolver (+ delegated inbound/outbound subnets) behind enable_* flags. Owns no RG/vNet/spoke/firewall (no F2 duplication). [M4][CO] 🟦 code-complete (v0.69) β†’ Test: offline TestLandingZoneConnectivityValidate. Runbook: docs/runbooks/test/N1.md.
  • 🟦 Azure Firewall Premium (N2) β€” modules/azure/firewall-premium-policy/ β€” the Premium azurerm_firewall_policy layer F2's Standard firewall lacks: sku = "Premium" + IDPS intrusion_detection (mode + signature overrides + traffic bypass) + optional TLS inspection (tls_certificate + UserAssigned identity, KV-cert gated) + optional DNS proxy + overridable application/network rule collection groups (deny-by-default). Attaches to a Premium azurerm_firewall by firewall_policy_id. [M4][CO] 🟦 code-complete (v0.69) β†’ Test: offline TestFirewallPremiumPolicyValidate. Runbook: docs/runbooks/test/N2.md.

  • 🟦 WAF Policy Module (N3) β€” modules/azure/waf-policy/ β€” Web Application Firewall policy for Application Gateway WAF v2 and Azure Front Door Premium. OWASP 3.2 managed rule set by default (configurable to Microsoft_DefaultRuleSet for Front Door or Microsoft_BotManagerRuleSet for bot mitigation). Prevention or Detection mode. Per-rule-group overrides (disable individual managed rules to tune false positives); managed-rule exclusions for known-safe request components; map-keyed custom rules (IP block list, geo-block, string match, rate limiting via RateLimitRule). Association is the caller's responsibility β€” N3 exports policy_id for wiring to an AppGw firewall policy or Front Door security policy. Optional resource group creation. [M2b][B][CO] 🟦 code-complete (v0.60) β†’ Test: terraform validate (Terratest TestWAFPolicyValidate) β€” exercises Prevention mode + OWASP 3.2 + per-rule override + exclusion + two custom rules (IPMatch + GeoMatch). Live apply (policy created + associated with AppGw, blocked request returns 403) is the N3 runbook Part C. Manual runbook: docs/runbooks/test/N3.md.

  • 🟦 DDoS Protection (N4) β€” modules/azure/ddos-protection/ β€” Azure DDoS Network Protection plan. Creates one azurerm_network_ddos_protection_plan; callers associate VNets by adding a ddos_protection_plan { id = module.ddos.plan_id; enable = true } block to their azurerm_virtual_network resources (the F2 hub vNet, spoke vNets, or standalone VNets). Exports plan_id and Azure-reported virtual_network_ids. Optional resource group creation. Cost: ~$2,944/month for the plan β€” budget approval required before applying. [M2b][B][CO] 🟦 code-complete (v0.60) β†’ Test: terraform validate (Terratest TestDDoSProtectionValidate). Live apply (plan created + VNet associated + protection confirmed) is the N4 runbook Part C β€” incurs ~$2,944/month, destroy immediately. Manual runbook: docs/runbooks/test/N4.md.

  • 🟦 Private Endpoint Policy Module (N5) β€” modules/azure/private-endpoint-policy/ β€” custom Deny initiative: "disable public network access" built-ins (storage / Key Vault / Cosmos DB / SQL). Initiative-level effect parameter. No system-assigned identity. Pairs with the Container Registry (F4) / Key Vault (F5) (PEs) + Network Hub (F2) (Private DNS). Curated, caller-overridable GUIDs. [M2a][B][CO] β†’ Test: Terratest validate (offline). Auditβ†’Deny rollout + "public PaaS create denied" is manual.

  • 🟦 NSG Baseline Module (N6) β€” modules/azure/nsg-baseline/ β€” hardened NSG (dynamic security_rule over merged baseline_rules + custom_rules map; curated defaults deny SSH/RDP/Internet-inbound; cross-map key collision = plan-time merge error) + optional subnet associations + optional NSG flow logs + Traffic Analytics (10-min interval, gated on BOTH workspace ID AND flow-log storage account ID). Standalone counterpart to the Network Hub (F2)'s bundled NSG. [M2a][B][CO] β†’ Test: Terratest validate + TestNSGBaselineModule integration (~$0, flow logs OFF). "Flow logs landing within 10 min" is manual Part D.

  • 🟦 Zero-Trust Reference Architecture (N7) β€” docs/architecture/zero-trust-reference.md β€” maps the three zero-trust principles (verify explicitly / least privilege / assume breach) + the five pillars (identity / devices / network / applications / data) + the visibility-automation spine to concrete SnowOps assets, with an honest maturity/gap table. The engineering counterpart to the GTM Z-series platform refs. Finishes the N series. [M4][A][CO] 🟦 code-complete (v0.69) β†’ Test: content review + offline cross-link check. Runbook: docs/runbooks/test/N7.md.


O–Q. Endpoint, Vendor Risk, HR Security [A]

All items β€” Endpoint & Workforce Security (O1–O4), Vendor & Third-Party Risk (P1–P4), HR Security & Training (Q1–Q5) β€” are ⏸️ postponed to M4.


R. Change Management [B]/[A]

  • 🟩 PR Template Enforcement (R1) β€” PR template enforcement + required-fields validation workflow. [M1][QW][CA] β†’ Signed off 26/05 (Sagar Chhabra).

  • 🟦 Production Change Log (R2) β€” apps/change-log/ β€” merged PRs / squash commits β†’ categorized changelog (+ optional change-record ticket via the Ticket Platform Library (E7)). 🟦 code-complete (v0.59).

  • 🟦 Emergency Change Workflow (R3) β€” .github/workflows/emergency-change.yml β€” label-gated (emergency-change/break-glass) PR workflow that bypasses CAB pre-approval for speed but (1) validates an inline Emergency Change Record via a stdlib gate (tests/emergency-change/), (2) drives R4's apps/cab-automation in emergency mode through the E7 snowops-ticket CLI to file a retroactive CAB review ticket (dry-run unless a tracker secret is set), and (3) posts a mandatory retrospective-review checklist comment. Human process documented in docs/runbooks/change/emergency-change.md. [M4][A][CA] β†’ Test: tests/emergency-change/validate.sh (14 stdlib unit tests + valid/broken fixture proof). Manual runbook: docs/runbooks/test/R3.md.
  • 🟦 CAB Automation (R4) β€” apps/cab-automation/ β€” high-impact changes β†’ Change Advisory Board review tickets via the Ticket Platform Library (E7) CLI bridge (fourth E7 consumer, the Defender Ticketer (I5)/Production Change Log (R2) mold). Assesses each change (open PRs via gh pr list, or fixture) against a configurable CAB policy: requires CAB on a CAB label (impact:high/cab-required), an emergency label (emergency/break-glass β†’ retroactive review framing), or a high-risk path touched (modules/,live/,policy/,pipelines/,waivers/,gitops/); impact = high for label/emergency, medium for path-only β€” reasons are always the matched labels/paths, never invented. Renders a CAB review (what/who/why + matched surfaces + backout-plan prompt + approval checklist; SOC2 CC8.1 / ISO A.12.1.2 evidence), dedupe cab=<pr-number>, --fail-on-cab for branch-protection gate use. 24 jest tests. [M4][A][CA] 🟦 code-complete (v0.66) β†’ Test: docs/runbooks/test/R4.md β€” build/typecheck/test (A) + offline dry-run on the sample (B, 2/4 require CAB); live: real open PRs β†’ CAB issues via E7, second run updates (C).

S. Continuous Compliance Monitoring & Drift [B]/[A]

  • 🟦 Drift Detector (S1) β€” apps/drift-detector/ β€” Drift detection [B]: read-only TS tool that turns terraform show -json into a versioned DriftReport (schemaVersion 1.0), classifies managed-resource changes (create/update/delete/replace; no-op + data reads excluded), and files/updates one ticket per stack. diffReports is the change signal (mirrors the Compliance Snapshot Collector (E0)'s diffSnapshots). Ships the TicketPlatform interface (D15 β€” the Ticket Platform Library (E7) seed) with GitHub Issues + dry-run adapters; idempotent upsert via an embedded dedupe marker. Scheduled via .github/workflows/drift-detection.yml (daily cron, per-stack matrix). Plans only, never applies. [M2b][B][CO] 🟦 code-complete (v0.44) β†’ Test: docs/runbooks/test/S1.md β€” offline classifier/diff/ticket (A+B); live: mutate sandbox resource, next run opens/updates the issue (C).

  • 🟦 Compliance Dashboard (S2) β€” apps/compliance-dashboard/ β€” Azure Policy compliance dashboard [B]: fully offline TS tool that renders a history of Compliance Snapshot Collector (E0) ComplianceSnapshots into a versioned ComplianceDashboard (schemaVersion 1.0) plus a self-contained static HTML page + markdown summary. Current posture, latest-vs-previous regression delta (vendors E0's diffSnapshots), a trend line, and a best-effort name-based framework rollup (SOC 2 / ISO 27001 / CIS Azure / HIPAA; MCSB fans out; unmatched β†’ "Unmapped"). Reads the compliance/snapshots/ evidence store (fed by E0); never touches Azure. Scheduled via .github/workflows/compliance-dashboard.yml (E0 collect β†’ S2 render β†’ artifact; Pages opt-in). [M2b][B][CO] 🟦 code-complete (v0.45) β†’ Test: docs/runbooks/test/S2.md β€” offline aggregation/framework/render golden (A+B); live: workflow collects a snapshot and uploads a renderable dashboard (C).

  • 🟦 Auto-Remediation Playbooks (S3) β€” modules/azure/auto-remediation-playbooks/ β€” Curated pack of Logic App playbooks [A] that turn a posture/compliance/drift finding into a remediation action: each workflow exposes an HTTP trigger accepting a structured finding (findingKind/resourceId/...) and runs a single action that either composes an intent record (dry_run) or issues the real ARM REST call (enforce) via its system-assigned identity. Structural dry-run/enforce split β€” Terraform renders an entirely different action body (Compose vs. Http+ManagedServiceIdentity) at plan time, so a dry_run playbook is structurally incapable of writing to Azure regardless of RBAC (extends the B3/D21 audit-only-by-default stance). Curated pack: enforce-mandatory-tags (PATCH missing U2 tags), lock-down-public-storage (PATCH to disable public blob access + deny network), remove-risky-nsg-rule (DELETE an Internet/* management-port rule), enable-resource-diagnostics (PUT a diagnostic setting β†’ consumed J1 workspace; auto-dropped when no workspace is wired). Per-playbook enabled/mode overrides + custom_playbooks escape hatch with collision precondition. Consumes the Log Analytics Module (J1) workspace by ARM ID for run-history diagnostics ("define the link, not the instance" β€” same stance as L2/J4/X8). Distinct from K3 (Sentinel SOAR incident-response): S3 fixes drift findings, K3 contains active incidents. Offline terraform validate Terratest gate (TestAutoRemediationPlaybooksValidate). [M4][A][CO] 🟦 code-complete (v0.64) β†’ Test: docs/runbooks/test/S3.md β€” offline validate + curated-pack/override/custom-playbook exercise (A+B); live: apply in dry_run, POST a finding, confirm intent record + no Azure write; inspect enforce-mode definition + absent RBAC (C).

  • 🟦 Compliance Scorecard Generator (S4) β€” apps/compliance-scorecard-generator/ β€” Turns one E0 ComplianceSnapshot (+ optionally an S2 ComplianceDashboard for framework rollups and trend) into a versioned, branded one-page scorecard β€” scorecard.md + a self-contained scorecard.html (+ optional Pandoc scorecard.pdf) + model.json (schema v1.0). The sales/client-facing counterpart to S2's detailed technical dashboard: S4 measures and re-derives nothing from Azure β€” every figure is a re-presentation of an E0/S2 number, with one transparent extra, a documented A–F grading band (simple mean of policy-compliance % and Secure Score %, banded through a fixed five-way split A=Strong/B=Good/C=Fair/D=Weak/F=At risk, or "β€”" when neither is measured), with its derivation printed inline ("Overall 88.2% = mean of policy compliance 95.3% and Secure Score 81%, banded to grade 'B' …") β€” the same "never fabricate, show your work" stance as L5/S3. Framework badges and the trend arrow are even more conservative β€” 1:1 re-presentations of S2's already-computed pass/attention status and the sign of its real deltas (declining wins on any negative signal or the dashboard's own regressed flag), never independently derived. Branding resolves against documented neutral defaults so an unbranded run still looks professional (preparedBy/title/accentColor/logoUrl β†’ text-wordmark fallback); only clientName is required. Pure adapt β†’ render + jest, mirroring E0/S1/S2/L4/L5 β€” no live Azure phase (offline-only). PDF rendering is a near-copy of G3's Pandoc-spawn pattern (independent build, D37/D40). 24 jest tests (2 suites). [M4][A][SH] 🟦 code-complete (v0.65) β€” runbook docs/runbooks/test/S4.md


T. Trust Center & Customer-Facing [A]

Trust Center (T1), Status Page (T2), Subprocessor List (T3), Security-Questionnaire Library (T4) β€” all ⏸️ postponed to M4.


U. Cost Governance [B]/[A]

  • 🟦 Budget Alert Module (U1) β€” modules/azure/budget-alert/ β€” azurerm_consumption_budget_subscription (Monthly, configurable thresholds) + dynamic notification blocks (actual + forecasted) + optional dedicated azurerm_monitor_action_group (email/sms/webhook, reusing the Break-Glass (H7) pattern) + optional filter_tag/filter_resource_groups. 4 preconditions (≀5 notifications per Azure cap, β‰₯1 recipient). Build-tagged integration test (~$0). [M2a][B][CO] β†’ Test: Terratest validate + TestBudgetAlertModule integration asserts budget ARM ID + action-group ID + 4 notification keys + recipient_count.

  • 🟦 Tag Policy Module (U2) β€” modules/azure/tag-policy/ β€” mandatory-tag Deny initiative: Require a tag on resources + Require a tag on resource groups per tag. Per-reference literal tagName (initiative has no parameters β€” each reference needs a distinct value, unlike the Data Residency Policy (M6)'s shared parameter). Default Β§8 set minus ManagedBy. enforce=falseβ†’true rollout (Deny is intrinsic to these built-ins). [M2a][B][CO] β†’ Test: Terratest validate (offline). enforce=falseβ†’true + "untagged create denied" is manual.

  • 🟦 FinOps Dashboard (U4) β€” modules/azure/grafana-dashboards/dashboards/snowops-finops.json (extends J5) β€” FinOps cost-governance dashboard: daily billable log ingestion (GB) + top tables by ingestion (the J7 Basic-Logs lever, Usage table) + tag governance (untagged-resource count, cost allocation by CostCenter, resources by type, via Azure Resource Graph). Honest about its boundary β€” subscription $ spend needs a Cost Management export (U1 budgets + U5 anomaly alerts cover spend alerting meanwhile). [M4][CO] 🟦 code-complete (v0.68) β†’ Test: J5 dashboards-as-code gate. Runbook: docs/runbooks/test/U4.md.

  • 🟦 Cost Anomaly Detection (U5) β€” modules/azure/cost-anomaly-alert/ β€” Azure Cost Management daily anomaly alerts (azurerm_cost_anomaly_alert) β†’ email recipients; the detection complement to U1 budgets ("threshold crossed" vs "spend pattern changed"). Finishes the U cost series. [M4][CO] 🟦 code-complete (v0.68) β†’ Test: offline TestCostAnomalyAlertValidate (Terratest validate). Runbook: docs/runbooks/test/U5.md.
  • ⬜ Idle Resource Cleanup (U3) β€” Idle-resource cleanup Function App. [M5][SO] ⏸️ postponed

V. Documentation & Policy Management [B]/[A]

  • 🟦 Policy Repo Template (V1) β€” templates/policies/ β€” six ratifiable security-policy templates (Information Security, Acceptable Use, Incident Response, Business Continuity & DR, Change Management, Vendor Risk), each with the standard skeleton + {{PLACEHOLDERS}} and an Enforcement section cross-referencing the SnowOps assets that operationalize it. These satisfy the πŸ“‹ Manual document controls the control mappings flag. Offline validate.py gate (sections + placeholders + cross-link integrity). [M4][A][CO] 🟦 code-complete (v0.69) β†’ Test: python3 templates/policies/validate.py. Runbook: docs/runbooks/test/V1.md.

  • 🟦 Diagram Generator (V2) β€” apps/diagram-generator/ β€” zero-cloud TS tool: terraform output -json β†’ F0 contracts β†’ cloud-neutral StackModel β†’ d2lang architecture diagram. Shape-based contract detection (name-agnostic; classifies by fields not output names). Deterministic renderer (stable slug() node ids + sorted emission β†’ clean golden-file diffs). [M2b][B][CO] ⬜ in scope (14-asset core) β†’ Test: jest (2 suites / 9 tests) β€” adapter normalizes all F0 contracts from sample stack + golden-file byte match + determinism + empty-stack safety. Manual runbook: docs/runbooks/test/V2.md.

  • 🟦 Runbook Generator (V3) β€” apps/runbook-generator/ β€” zero-cloud TS CLI: terraform output -json β†’ shape-based adapter (all 7 F0 contracts) β†’ per-domain operational runbooks (infrastructure.md index + identity/network/compute/registry/secrets/storage/observability) with key-facts tables, Day-Zero Hardening posture digest (βœ…/⚠️), ops CLI + failure modes. Mirrors the Diagram Generator (V2)'s adaptβ†’modelβ†’render pattern. Legacy Handlebars mode kept via --templates. [M2b][B][CO] ⬜ in scope (14-asset core) β†’ Test: jest (4 suites / 16 tests) β€” parser + legacy Handlebars + shape-based adapt + model-driven render. Manual runbook: docs/runbooks/test/V3.md.

  • 🟦 Compliance Manual Generator (V4) β€” apps/compliance-manual/ β€” pure adaptβ†’render (S4/V5 mold): composes a framework-scoped manual spec (per-control requirement + implementation statement + enforcing SnowOps asset(s) + evidence refs + governing policy) plus an optional E0 snapshot (posture appendix) into a branded compliance-manual.md/.html (+ optional Pandoc PDF) + model.json. Honest about undocumented controls (never invents an implementation); coverage % derivation shown. Finishes the V series. [M4][A][SH] 🟦 code-complete (v0.69) β†’ Test: jest (coverage math, honest-missing, status derivation, HTML self-containment, determinism). Runbook: docs/runbooks/test/V4.md.

  • ⬜ Client Cloud Posture Report (V5) β€” Tier 2 of the client-facing posture-visibility initiative (D65). apps/posture-report/ β€” an offline composer (pure adapt β†’ render + jest, the E0/S2/S4/L5 mold; re-declares input contracts, no cross-package build import per D37/D40; reuses S4's branding + Pandoc-PDF pattern) that bundles the latest Compliance Snapshot (E0), Compliance Dashboard (S2), Compliance Scorecard (S4), RTO/RPO doc (L5), and architecture diagram (V2) into ONE branded, client-facing Cloud Posture Report (posture-report.html self-contained + best-effort posture-report.pdf + versioned model.json). Organized by SnowOps deliverable β€” exec summary + overall grade (S4); your guardrails (policy/Defender, E0); your data is protected (backup coverage L1, restore-drill evidence + RTO/RPO L4/L5); your compliance posture (framework rollup + trend S2); incidents we handled (reuses a K4 report when supplied); cost governance (roadmap until a U-series feed exists); architecture (V2) β€” each section tagged by package tier (Baseline/Advanced) so ungated sections become upsell teasers. Every figure is a re-presentation of what the source tools computed; missing inputs render honest "not supplied" sections (the L5/S4 never-fabricate stance). Safe to deliver β€” point-in-time, sanitizable, no client cloud access required (unlike J8). Delivered via .github/workflows/posture-report.yml (monthly + dispatch; PRIVATE artifact by default, same sensitivity stance as S2) + a QBR template under docs/client-guides/. Powers retention/QBR (Y11) and is a sales proof asset. Shares the section taxonomy with the Client Overview Dashboard (J8). Shipped: re-declared E0/S2/S4/L5/V2/K4 contracts β†’ PostureReportModel; the headline grade is the S4 scorecard verbatim when supplied, else derived from E0 like S4; cost is an honest U4/U5 roadmap note; HTML is self-contained; 27 jest tests. [M4][A][SH] 🟦 code-complete (v0.67) β†’ Test: V5 runbook β€” offline build/typecheck/test (section composition, honest-missing-input handling, tier tagging, branding, never-fabricate) + fixture CLI run producing a branded report; no live phase (pure composer over JSON/markdown artifacts).

W. Multi-Tenant Client Management [X][SO] ⏸️ ALL POSTPONED (D35)

The Client Repo Template (W1), Per-Client State Backend (W2), Per-Client Secret Scoping (W3) are marked [M2a→postponed] and are NOT part of the M2a-complete bar. Pull up only when a second concurrent client forces the multi-tenant isolation question, or after M2b/M3 are underway.

  • ⬜⏸️ Client Repo Template (W1) β€” Client repo template + provisioning (extends the GitHub Onboarder App (B1)).
  • ⬜⏸️ Per-Client State Backend (W2) β€” Per-client state backend (extends the State Backend (F6)).
  • ⬜⏸️ Per-Client Secret Scoping (W3) β€” Per-client secret scoping in SnowOps GH org (environments).
  • 🟦 Client Offboarding Playbook (W4) β€” docs/runbooks/offboarding/ β€” ownership-aware, phased offboarding playbook (the inverse of the GitHub Onboarder App (B1) + Azure Client Bootstrap Module (B2)): freeze & snapshot evidence (E0/S2/L1/L4/G6), knowledge transfer (V2/V3/F11), identity teardown (B2/H7/B5/H3/H2), repo/CI hand-back (B1/S1/H5), state & secrets transfer (F6/B4/F5), cost & monitoring (U1/K2/J4/J1/J6), decommission (terraform destroy/X7), and final attestation β€” with sign-off + client counter-sign. [M3] 🟦 code-complete (v0.62)
  • ⬜ Internal Client Dashboard (W5) β€” SnowOps internal client dashboard. [M5]

X. Testing Framework & Sandbox [X]

Nothing in F/B/E/etc. can be marked 🟩 Shipped without the Azure Sandbox Subscription (X1), Terratest Harness (X2), and Manual Test Runbooks (X6) in place.

  • 🟦 Azure Sandbox Subscription (X1) β€” SnowOps Azure sandbox subscription (Terraform-managed): isolated tenant or sub, budget-capped, auto-cleanup tag ephemeral=true. [M1]
  • 🟦 Terratest Harness (X2) β€” Terratest harness (tests/terratest/): Go-based, parallel-safe, sandbox-scoped; mandatory per F module. Currently 35 top-level tests. [M1]
  • 🟩 Conftest Test Suite (X3) β€” Conftest test suite for the Terraform OPA Policy Bundle (D3) (policy/opa/tests/). [M1]
  • 🟦 Kyverno Test Framework (X4) β€” Kyverno test framework for the Kyverno AKS Policy Bundle (D4) (policy/kyverno/tests/). 9 sub-suites / 35 assertions + run-tests.sh wrapper + pre-push hook. [M2a]
  • 🟦 Pipeline Integration Tests (X5) β€” Pipeline integration tests (test consumer repos for reusable workflows). [M2b] 🟦 code-complete (v0.57)
  • ⬜ Manual Test Runbooks (X6) β€” Manual test runbooks (docs/runbooks/test/<asset_id>.md). [ongoing]
  • 🟦 Sandbox Cleanup (X7) β€” sandbox/cleanup/ + .github/workflows/sandbox-cleanup.yml β€” nightly cleanup of ephemeral=true RGs. Three guards: (1) ephemeral=true tag only, (2) protected-name globs, (3) --min-age-hours (default 6). 12 offline assertions. Cron 03:17 UTC (always deletes); dispatch (defaults dry-run). [M2a][SO]
  • 🟦 Synthetic Monitoring (X8) β€” App Insights standard availability tests + per-test alerts. [M2b] 🟦 code-complete (v0.57)

Y. Go-To-Market & Sales Engine [X][SO] β€” all 🟦

See docs/context/04-asset-status.md Β§ Y for full table. All 14 assets (the GTM Operating Doc Y0 through the Sales Pipeline & CRM Config Y13) drafted under docs/gtm/. Human sign-offs pending.

Z. Reference Architectures [X][SOβ†’CO] β€” all 🟦

See docs/context/04-asset-status.md Β§ Z for full table. All 4 assets (the Reference-Architecture Framework Z0 through the Healthcare / HealthTech Reference Platform Z3) drafted under docs/gtm/z/. Human sign-offs pending.