E4 — Resource Graph Query Library: Test Runbook¶
Asset: Resource Graph Query Library (E4)
Location: apps/evidence-collector/src/resource-graph/
Owner: SnowOps (SH)
Milestone: M4
Overview¶
The Resource Graph library contains 7 pre-built KQL queries mapped to SOC 2 CC
and ISO 27001 A.x controls. The mapper converts query results to EvidenceItem[]
for consumption by any EvidencePlatform.
Queries:
| ID | Name | Controls |
|----|------|---------|
| storage-cmk | Storage CMK Encryption | SOC2 CC6.7, ISO A.8.24 |
| public-network-access | Public Network Access Disabled | SOC2 CC6.6, ISO A.8.20 |
| storage-tls | Storage TLS 1.2+ | SOC2 CC6.7, ISO A.8.24 |
| owner-role-assignments | No Direct User Owner Assignments | SOC2 CC6.1, ISO A.8.5 |
| defender-plans | Defender Plans Enabled | SOC2 CC7.1, ISO A.8.8 |
| diagnostic-settings | Diagnostic Settings Coverage | SOC2 CC7.1, ISO A.8.15 |
| required-tags | Required Tags Compliance | SOC2 CC8.1, ISO A.5.9 |
Part A — Offline: query evaluator unit tests¶
Expected: All tests pass. Tests cover: - 7 query evaluators with pass and fail scenarios - Mapper: item count per (result × control mapping) - Mapper: observedAt + subscriptionId stamping - Mapper: unknown queryId skipped
Part B — Offline: TypeScript type-check¶
Expected: No type errors.
Part C — Live: Resource Graph execution (requires Azure subscription)¶
Prerequisites:
- az login with Reader on the sandbox subscription
- AZURE_SUBSCRIPTION_ID set
cd apps/evidence-collector
npm run build
AZURE_SUBSCRIPTION_ID=<your-sub-id> \
node -e "
import('./dist/resource-graph/index.js').then(async ({ RESOURCE_GRAPH_QUERIES, runAllQueries, mapQueryResultsToEvidence }) => {
const { FetchAzureRestClient } = await import('./dist/azure-rest.js');
const { execFileSync } = await import('node:child_process');
const token = execFileSync('az', ['account','get-access-token','--resource','https://management.azure.com','--query','accessToken','-o','tsv'], { encoding: 'utf8' }).trim();
const client = new FetchAzureRestClient({ baseUrl: 'https://management.azure.com', accessToken: token });
const sub = process.env.AZURE_SUBSCRIPTION_ID;
const results = await runAllQueries(client, RESOURCE_GRAPH_QUERIES, sub);
const items = mapQueryResultsToEvidence(RESOURCE_GRAPH_QUERIES, results, new Date().toISOString(), sub);
console.log(JSON.stringify(items.map(i => ({ framework: i.framework, controlId: i.controlId, status: i.status, evidence: i.evidence })), null, 2));
});
"
Expected: 14 EvidenceItems (7 queries × 2 controls each, approximately).
Each item shows a status of pass or fail and an evidenceSummary with
counts from your sandbox subscription.
Sign-Off Block¶
| Step | Executed by | Date | Result |
|---|---|---|---|
| Part A (unit tests) | ⬜ | ||
| Part B (typecheck) | ⬜ | ||
| Part C (live Resource Graph) | ⬜ |
Signed off: _____ Date: ___
Failure Modes¶
| Failure | Cause | Remediation |
|---|---|---|
Azure REST POST failed: 403 |
Insufficient permissions | Assign Reader on the subscription |
Azure REST POST failed: 400 |
Invalid KQL syntax | Check the KQL query against the Azure portal Resource Graph Explorer |
| Results truncated | Query returns > 1000 rows | Add | take 1000 or refine the filter |