Skip to content

Azure Cloud Setup Guide: Before Your First Runbook

Owner: Sagar  |  Time: ~30 min  |  Phase: 0 (Pre-Flight)


Overview

This guide sets up everything needed to run real Azure cloud-level tests. Complete this once before executing any runbooks in SEQUENCING.md.

After this runbook, you'll have: - Azure subscription credentials configured - Terratest harness validated locally - Ready to run F1 (Baseline) as your first cloud test


Prerequisites

  • Azure subscription created (new or existing)
  • Permissions: Owner on the subscription (minimum) or Contributor + User Access Administrator
  • GitHub repo cloned: git clone https://github.com/snowopscloud/snowops-automation.git
  • Local machine with: bash, Git, Docker (or Go 1.22+ locally)

Part A — Identify Your Azure IDs (2 min)

Step 1: Get your subscription ID

az login
az account list --query "[].{name:name, id:id, tenantId:tenantId}" -o table

Note the subscription ID and tenant ID for the one you want to use as sandbox.

Step 2: Export as environment variables

Add to your shell profile (~/.zshrc or ~/.bash_profile):

export SNOWOPS_SANDBOX_SUBSCRIPTION_ID="<your-subscription-guid-here>"
export SNOWOPS_SANDBOX_TENANT_ID="<your-tenant-guid-here>"

Verify they load:

source ~/.zshrc  # or ~/.bash_profile
echo "Subscription: $SNOWOPS_SANDBOX_SUBSCRIPTION_ID"
echo "Tenant: $SNOWOPS_SANDBOX_TENANT_ID"

Both should print valid GUIDs (e.g., 12345678-1234-1234-1234-123456789012).


Part B — Configure Azure Authentication (5 min)

Choose one approach below. Option A (OIDC) is recommended for CI/CD; Option B (Service Principal) is easiest for local testing.

Option A: Azure AD Federated OIDC (for CI/CD)

Skip this if you're testing locally. Use for GitHub Actions later.

GitHub Actions workflows use OIDC to avoid long-lived secrets:

# Create a managed identity
az identity create --name snowops-github-actions --resource-group "<your-rg>"

# Configure federated credentials for GitHub
# (This is done in the workflow templates; see: .github/workflows/terraform-plan-apply.yml)

Reference: .github/workflows/terraform-plan-apply.yml (already configured with OIDC).

Option B: Service Principal with Environment Variables (Local Testing) ⭐

  1. Create a service principal:
SUBSCRIPTION_ID="$SNOWOPS_SANDBOX_SUBSCRIPTION_ID"
TENANT_ID="$SNOWOPS_SANDBOX_TENANT_ID"

az ad sp create-for-rbac \
  --name "snowops-terraform" \
  --role "Contributor" \
  --scopes "/subscriptions/$SUBSCRIPTION_ID" \
  --query "{clientId:appId, clientSecret:password, tenantId:tenant}" \
  --output json
  1. Save the output and add to your shell profile:
export ARM_CLIENT_ID="<client-id-from-above>"
export ARM_CLIENT_SECRET="<client-secret-from-above>"
export ARM_TENANT_ID="$SNOWOPS_SANDBOX_TENANT_ID"
export ARM_SUBSCRIPTION_ID="$SNOWOPS_SANDBOX_SUBSCRIPTION_ID"
  1. Test the connection:
az login --service-principal -u "$ARM_CLIENT_ID" -p "$ARM_CLIENT_SECRET" --tenant "$ARM_TENANT_ID"
az account show

Expected: your subscription details print. ✅

Option C: az login Interactively (Simplest)

az login
az account set --subscription "$SNOWOPS_SANDBOX_SUBSCRIPTION_ID"
az account show  # Verify

Terraform automatically picks up credentials from az login.


Part C — Install Local Tooling (5 min)

macOS (Homebrew)

brew install terraform terragrunt kubectl helm checkov gitleaks go
brew tap terraform-linters/tflint && brew install terraform-linters/tap/tflint
brew install conftest

Linux (Ubuntu/Debian)

# Terraform
wget https://releases.hashicorp.com/terraform/1.6.0/terraform_1.6.0_linux_amd64.zip
unzip terraform_1.6.0_linux_amd64.zip && sudo mv terraform /usr/local/bin/

# Go 1.22+
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz

# Other tools
sudo apt-get install -y kubectl helm gitleaks checkov

Verify Installation

terraform --version  # >= 1.6
go version           # >= 1.22
az --version         # >= 2.50
kubectl version --client
helm version

All should print versions without error.


Part D — Run Schema Validation Tests (No Cloud, 5 min)

Before touching Azure, validate that the Terraform modules compile correctly:

cd snowops-automation/tests/terratest
go test -v -timeout 5m ./...

Expected output: All tests pass (or skip) with no errors. Example:

=== RUN   TestBaselineValidate
baseline: Terraform validate
baseline: Terraform plan (no apply)
--- PASS: TestBaselineValidate (3.50s)
=== RUN   TestAKSSecureValidate
...
ok      github.com/snowopscloud/snowops-automation/tests/terratest  45.12s

If this fails: - Run terraform init in each fixtures/*/ directory - Check that SNOWOPS_SANDBOX_SUBSCRIPTION_ID and SNOWOPS_SANDBOX_TENANT_ID are exported - Verify no syntax errors: terraform validate in modules/azure/baseline


Part E — Understand the Testing Harness (3 min)

The repository uses Terratest (Go + Terraform) for integration testing:

tests/terratest/
├── modules/azure/          # 35+ module test files (*_test.go, *_validate_test.go)
├── sandbox/                # Sandbox-level integration tests
├── fixtures/               # 44 minimal Terraform configs for each module
└── helpers/                # Test utilities (terraform.go, azure.go)

Test Pattern: - *_validate_test.go — schema + plan-only (no cloud, no credentials) - *_test.go — full apply → assert → destroy (real Azure) - Both use -tags integration flag to gate cloud tests

Your first cloud test will be F1 (Baseline):

go test -v -tags integration -timeout 25m ./... -run TestBaselineModule

This: 1. Creates a resource group in your sandbox subscription 2. Deploys a Log Analytics workspace + Azure Policy 3. Validates outputs (workspace ID, policy assignment scope) 4. Destroys everything cleanly


For stateful testing, Terraform can store remote state in Azure Storage. The template exists:

cd sandbox
cp backend.hcl.example backend.hcl
cp terraform.tfvars.example terraform.tfvars

# Edit both files:
# backend.hcl:      update storage account name, container, key
# terraform.tfvars: update your subscription ID, resource group, etc.

For now, local state is fine (tests use in-memory state). You'll set this up properly when you reach Phase 8 (Core Infrastructure).


Part G — Verify Everything Works (3 min)

Run this final sanity check:

# 1. Env vars
echo "Sub: $SNOWOPS_SANDBOX_SUBSCRIPTION_ID"
echo "Tenant: $SNOWOPS_SANDBOX_TENANT_ID"

# 2. Azure CLI
az account show --query "{subscriptionId:id, displayName:name}" -o table

# 3. Terraform
cd snowops-automation
terraform -v

# 4. Go & Terratest
cd tests/terratest
go version
go test -v -timeout 5m ./... -run TestBaselineValidate

Expected: All commands print output without error. ✅


What's Next?

Immediately After This Runbook

  1. Read docs/runbooks/SEQUENCING.md (full sequence of 44 runbooks)
  2. Start Phase 1 with V2 (offline, ~10 min, no cloud credentials)
  3. Proceed through phases in order (dependencies matter!)
  4. When you reach F1 (Phase 8, item 16), you'll run your first cloud test

Milestone: First Cloud Test (F1 Baseline)

Once this setup is complete, F1 is your next real cloud test:

cd tests/terratest
go test -v -tags integration -timeout 25m ./modules/azure/... -run TestBaselineModule

Then follow docs/runbooks/test/F1.md (Parts A, B, C).


Troubleshooting

Error Solution
go test: can't load package: ... Run go mod download in tests/terratest/
az: command not found Install Azure CLI: brew install azure-cli (macOS) or apt install azure-cli (Linux)
SNOWOPS_SANDBOX_SUBSCRIPTION_ID not set Export in your shell: export SNOWOPS_SANDBOX_SUBSCRIPTION_ID="..."
unauthorized: authentication required Check Azure credentials: az account show should return your subscription
terraform: command not found Add /usr/local/bin to $PATH or reinstall Terraform
Terratest hangs on InitAndApply Azure provisioning is slow (5–15 min for large resources like AKS). Don't interrupt.

Sign-off

  • Tester:Sagar Chhabra_  |  Date: _14/6/2026___  |  Result: PASS
  • Notes: (e.g., "Env vars configured, schema tests pass, ready for Phase 1")