Skip to content

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 via az 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 login done; 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_ID exported
  • 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)

  1. 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.

  1. Offline Terratest case:
cd tests/terratest
go test -v -timeout 5m ./modules/azure/... -run TestLogAnalyticsCostValidate

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_days rejected 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)

  1. 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.

  1. Apply the example against a REAL J1 workspace. Pick a table you know exists in that workspace (e.g. AzureActivity is 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 = false and pass only table_plans for tables you know exist.

  1. 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.

  1. 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.

  1. 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; TestLogAnalyticsCostValidate passes
  • Part B — full offline suite passes
  • (Part C) apply flips an Analytics + a Basic table; az ... table show confirms plan (and Basic reports retentionInDays: 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 explicit table_plans. Documented in the module README.
  • retention_in_days rejected at plan — set on a Basic table; Basic interactive retention is fixed at 8 days. Move it to total_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