Skip to content

Manual Test Runbook — S3: Auto-Remediation Playbooks

Owner: Sagar  |  Time: ~8 min (Parts A + B offline) · +15 min (optional Part C integration apply)  |  Sandbox: snowops-sandbox-01

Promotes S3 (modules/azure/auto-remediation-playbooks/) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are offline ($0). Part C applies the curated pack (in dry_run) against a sandbox RG (~$0 — Logic Apps Consumption bills per execution; the apply lasts minutes and POSTs at most a handful of test findings) and destroys.


Prerequisites

  • Sandbox subscription access active (PIM activated if required)
  • az login done; sandbox subscription selected
  • Identity has Contributor on the sandbox sub (RG + Logic App create)
  • SNOWOPS_SANDBOX_SUBSCRIPTION_ID + SNOWOPS_SANDBOX_TENANT_ID exported
  • Local tooling: terraform >= 1.6, go >= 1.22, az CLI >= 2.50
  • Working directory: repo root

Steps

Part A — terraform fmt + validate (offline, ~3 min)

  1. Module + example:
terraform -chdir=modules/azure/auto-remediation-playbooks fmt -recursive -check
terraform -chdir=modules/azure/auto-remediation-playbooks init -backend=false -input=false
terraform -chdir=modules/azure/auto-remediation-playbooks validate

terraform -chdir=modules/azure/auto-remediation-playbooks/examples/basic init -backend=false -input=false
terraform -chdir=modules/azure/auto-remediation-playbooks/examples/basic validate

Expected: Success! for both.

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

Expected: PASS — exercises the full curated pack (incl. the workspace-gated enable-resource-diagnostics playbook), a mode override flipping lock-down-public-storage to enforce, an enabled override dropping remove-risky-nsg-rule, and a custom playbook with the collision precondition intact, offline.

Part B — full Terratest suite (offline, ~5 min)

  1. bash cd tests/terratest && go test -count=1 -timeout 15m ./...

Expected: the full suite green (the new TestAutoRemediationPlaybooksValidate included).

Part C — integration apply (sandbox, ~15 min, ~$0)

No build-tagged integration test ships for S3 — exercising a real ARM write needs a disposable target resource (e.g. a throwaway storage account to PATCH), and the "POST a finding → observe the run-history record / real write" criterion belongs here, not in a teardown-on-completion test.

  1. Apply the fixture (stays in dry_run pack-wide — safe to point at a real workspace ARM ID):
cd tests/terratest/fixtures/auto-remediation-playbooks
terraform init -input=false
# Optionally point log_analytics_workspace_id at a REAL sandbox workspace
# ARM ID first (edit main.tf) to also exercise the diagnostic-settings path.
terraform apply -auto-approve \
  -var "subscription_id=$SNOWOPS_SANDBOX_SUBSCRIPTION_ID" \
  -var "tenant_id=$SNOWOPS_SANDBOX_TENANT_ID"
  1. Confirm the workflows exist with the expected shape:
RG=snowops-s3-test-rg
az logic workflow list --resource-group "$RG" \
  --query "[].{name:name, state:state}" -o table

Expected: 4 workflows — *-enforce-mandatory-tags, *-lock-down-public-storage, *-enable-resource-diagnostics, *-quarantine-orphaned-disk (the custom playbook). *-remove-risky-nsg-rule is absent (disabled via override).

  1. POST a synthetic finding to a dry_run playbook's trigger and confirm it composes an intent record with no Azure write attempted:
ENDPOINT=$(az logic workflow show -g "$RG" -n snowops-s3-test-enforce-mandatory-tags \
  --query accessEndpoint -o tsv)/triggers/when-a-finding-is-reported/paths/invoke?api-version=2016-06-01
curl -s -X POST "$ENDPOINT" -H 'Content-Type: application/json' -d '{
  "findingId": "f-test-1",
  "findingKind": "missing-required-tags",
  "resourceId": "/subscriptions/'"$SNOWOPS_SANDBOX_SUBSCRIPTION_ID"'/resourceGroups/'"$RG"'/providers/Microsoft.Storage/storageAccounts/doesnotexist",
  "detectedAt": "2026-06-07T12:00:00Z",
  "proposedTags": { "CostCenter": "shared" }
}'
az logic workflow run list -g "$RG" -n snowops-s3-test-enforce-mandatory-tags -o table

Expected: the run Succeeded; its evaluate-and-remediate action output shows mode: "dry_run" and the wouldDo summary — no Http action exists in the definition, so nothing in Azure changed.

  1. (Optional, enforce-path check) Inspect *-lock-down-public-storage — confirm its definition contains a real Http action authenticated via ManagedServiceIdentity, and that its system-assigned identity (from principal_ids output) currently has no write RBAC role — i.e. running it would 403, not silently succeed:
az logic workflow show -g "$RG" -n snowops-s3-test-lock-down-public-storage \
  --query "definition.actions['evaluate-and-remediate'].type" -o tsv   # expect: Http
PRINCIPAL_ID=$(terraform output -json principal_ids | jq -r '."lock-down-public-storage"')
az role assignment list --assignee "$PRINCIPAL_ID" -o table             # expect: empty
  1. Destroy:
terraform destroy -auto-approve \
  -var "subscription_id=$SNOWOPS_SANDBOX_SUBSCRIPTION_ID" \
  -var "tenant_id=$SNOWOPS_SANDBOX_TENANT_ID"

Expected: clean destroy — playbooks hold no state of their own; the RG and every workflow are removed. (The J1 workspace, if a real one was wired, is untouched.)


Pass criteria

  • Part A — module + example validate; TestAutoRemediationPlaybooksValidate passes
  • Part B — full offline suite passes
  • (Part C) fixture applies the pack; workflow count + the disabled rule + the override + the custom playbook match; destroys clean
  • (Part C) a dry_run playbook composes an intent record and performs no Azure write
  • (Part C, optional) an enforce-mode playbook's definition carries a real Http action but its identity has no granted RBAC (403-not-silent-success)
  • All test resources removed

Failure mode

A playbook in enforce mode whose identity hasn't been granted the matching write RBAC role yet "does nothing" when triggered — by design (see the README safety model). The opposite failure — a dry_run playbook somehow mutating Azure — is structurally impossible: no Http action exists in its definition.

Cost impact

Logic Apps (Consumption) bill per action execution — a few cents per 1,000 runs. Cost tracks finding volume; idle playbooks cost nothing. Part C apply + a handful of test triggers is ~$0.

Removal path

terraform destroy (Part C step 8) removes every workflow and the RG if S3 created it. Verified clean in Part C.


Sign-Off

Field Value
Part A (validate) ☐ PASS
Part B (offline suite) ☐ PASS
Part C (integration apply) ☐ PASS / ☐ skipped
Part C dry-run check ☐ PASS / ☐ skipped
Tester
Date
Result ☐ PASS