Manual Test Runbook — J7: Cost-Controlled Log Strategy (Basic-Logs tier)¶
Owner: Sagar | Time: ~7 min (Parts A + B offline) · +15 min (optional Part C live apply) | Sandbox: snowops-sandbox-01
Promotes J7 (
modules/azure/log-analytics-cost/) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are offline ($0). Part C flips per-table plans on a REAL J1 workspace and confirms them viaaz monitor log-analytics workspace table show. J7 operates on an existing workspace by ID — it never creates the workspace, so Part C needs a J1 workspace (with the target tables already present) standing.
Prerequisites¶
- Sandbox subscription access active (PIM activated if required)
-
az logindone; sandbox subscription selected - Identity has Log Analytics Contributor (or Contributor) on the J1 workspace
- A real J1 workspace ARM ID handy (
module.log_analytics.id), with the target tables already created/connected -
SNOWOPS_SANDBOX_SUBSCRIPTION_ID+SNOWOPS_SANDBOX_TENANT_IDexported - Local tooling:
terraform >= 1.6,go >= 1.22,az CLI >= 2.50,jq - Working directory: repo root
Steps¶
Part A — terraform fmt + validate (offline, ~3 min)¶
- Module + example:
terraform -chdir=modules/azure/log-analytics-cost fmt -recursive -check
terraform -chdir=modules/azure/log-analytics-cost init -backend=false -input=false
terraform -chdir=modules/azure/log-analytics-cost validate
terraform -chdir=modules/azure/log-analytics-cost/examples/basic init -backend=false -input=false
terraform -chdir=modules/azure/log-analytics-cost/examples/basic validate
Expected: Success! for both. The example uses a synthetic workspace_id,
so validate runs with no cloud creds.
- Offline Terratest case:
Expected: PASS — exercises the curated default strategy (Basic/Analytics
split), a Basic-tier override on a custom table, an Analytics-tier retention
override, and the cost_strategy_summary output, offline.
Note: the validate gate also confirms the variable validations compile (plan ∈ {Basic, Analytics};
retention_in_daysrejected on Basic; bounds). To eyeball a rejection, temporarily add a{ plan = "Basic", retention_in_days = 30 }entry to the fixture and re-run validate — it must fail with the "retention_in_days may only be set on Analytics-plan tables" message. Revert after.
Part B — full Terratest suite (offline, ~4 min)¶
bash cd tests/terratest && go test -count=1 -timeout 15m ./...
Expected: the full suite green (the new TestLogAnalyticsCostValidate
included).
Part C — live apply against a real J1 workspace (sandbox, ~15 min, ~$0)¶
The point of Part C is to prove the plan actually flips on a real table and that the Basic/Analytics split lands. There is no per-resource charge for the table plan itself; only ingestion bills, so this window costs effectively $0.
- Apply the example against a REAL J1 workspace. Pick a table you know exists
in that workspace (e.g.
AzureActivityis present once activity logs flow):
cd modules/azure/log-analytics-cost/examples/basic
terraform init -input=false
WS="<REAL J1 workspace ARM ID>"
terraform apply -auto-approve -var "workspace_id=$WS"
Expected: applies clean. terraform output cost_strategy_summary shows the
Basic/Analytics split, recommended_daily_quota_gb = 50, and
daily_quota_managed_by_j7 = false.
If a table in the default strategy does not exist in your workspace yet, the apply fails at that resource ("table not found") — that is the expected data-plane behaviour (table existence resolves at apply, not plan). Either connect the data source first, or set
use_default_strategy = falseand pass onlytable_plansfor tables you know exist.
- Confirm the plan landed via the data plane:
# Parse the workspace ARM ID into RG + name.
RG=$(echo "$WS" | sed -E 's#.*/resourceGroups/([^/]+)/.*#\1#')
WSN=$(echo "$WS" | sed -E 's#.*/workspaces/([^/]+)$#\1#')
# A table you put on Analytics (e.g. AzureActivity):
az monitor log-analytics workspace table show \
--resource-group "$RG" --workspace-name "$WSN" \
--name AzureActivity \
--query "{name:name, plan:plan, retentionInDays:retentionInDays, totalRetentionInDays:totalRetentionInDays}" -o jsonc
# A table you put on Basic (e.g. ContainerLogV2, if present):
az monitor log-analytics workspace table show \
--resource-group "$RG" --workspace-name "$WSN" \
--name ContainerLogV2 \
--query "{name:name, plan:plan, retentionInDays:retentionInDays, totalRetentionInDays:totalRetentionInDays}" -o jsonc
Expected: the Analytics table reports "plan": "Analytics" with your
interactive retention; the Basic table reports "plan": "Basic" with
retentionInDays: 8 (Azure's fixed interactive floor) and your
totalRetentionInDays.
- Confirm the recommended daily cap. J7 does NOT set it — verify it is the J1 workspace owner's lever:
az monitor log-analytics workspace show \
--resource-group "$RG" --workspace-name "$WSN" \
--query "workspaceCapping.dailyQuotaGb" -o tsv
Expected: whatever J1 set (or -1.0 for unlimited). J7 leaving it untouched
is the correct behaviour — the cap belongs on J1's daily_quota_gb.
- Revert / destroy:
cd modules/azure/log-analytics-cost/examples/basic
terraform destroy -auto-approve -var "workspace_id=$WS"
Expected: clean destroy. The table resources are removed from Terraform, which reverts them to the workspace's default plan/retention management — it does not delete the tables or their data. The J1 workspace is untouched.
Pass criteria¶
- Part A — module + example validate;
TestLogAnalyticsCostValidatepasses - Part B — full offline suite passes
- (Part C) apply flips an Analytics + a Basic table;
az ... table showconfirmsplan(and Basic reportsretentionInDays: 8); daily cap untouched by J7; destroy reverts cleanly - All test changes reverted; workspace + data intact
Failure mode¶
- Apply fails "table not found" — the named table isn't in the workspace
yet (data-plane fact, surfaces at apply). Connect the data source or use
use_default_strategy = false+ an explicittable_plans. Documented in the module README. retention_in_daysrejected at plan — set on a Basic table; Basic interactive retention is fixed at 8 days. Move it tototal_retention_in_days. This is a variable validation, by design.- A J4 log alert on a Basic table stops evaluating — Basic tables cannot back scheduled-query rules. Keep any alerted table on Analytics; the curated default already does so for security/audit tables.
Cost impact¶
No per-resource charge for the table plan or this test window — only ingestion
and retention bill, and Part C ingests nothing new. In production J7 reduces
spend by moving high-volume tables to the cheaper Basic ingestion tier and
bounding retention; the saving is the per-GB tier delta × your ingested volume
(pull rates from the Azure Monitor pricing page and volume from
Usage | where IsBillable | summarize sum(Quantity) by DataType). No dollar
figure is asserted here — it depends on the workspace's ingestion mix.
Removal path¶
terraform destroy (Part C step 7) removes the managed table resources and
reverts each table to the workspace's default management — data and the J1
workspace are untouched. Verified clean in Part C.
Sign-Off¶
| Field | Value |
|---|---|
| Part A (validate + Terratest case) | ☐ PASS |
| Part B (offline suite) | ☐ PASS |
| Part C (live plan flip + table show) | ☐ PASS / ☐ skipped |
| Tester | |
| Date | |
| Result | ☐ PASS |