Skip to content

Manual Test Runbook — H1: Azure AD Tenant Baseline

Owner: Sagar  |  Time: ~5 min (Parts A + B, offline) · +25 min Part C (tenant apply) · +15 min Part D (Graph PATCH steps)  |  Sandbox: snowops-sandbox-tenant-01

Promotes H1 (modules/azure/aad-baseline/) from 🟦 Code Complete → 🟩 Shipped. Part C costs $0 (configuration-only). Part D is the operator-side Graph PATCH step for password protection + branding (no Terraform resource available).


Prerequisites

  • Sandbox AAD tenant access (Global Administrator role active or eligible via PIM)
  • az login --tenant <SNOWOPS_SANDBOX_TENANT_ID> done; az account show confirms the tenant
  • At least one verified custom domain in the sandbox tenant (e.g., snowops-sandbox.example.com)
  • Local tooling: terraform >= 1.6, go >= 1.22, az CLI >= 2.50, jq
  • SNOWOPS_SANDBOX_TENANT_ID env var set
  • Working directory: repo root

Steps

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

  1. Confirm formatting + structural validity of the module:
terraform -chdir=modules/azure/aad-baseline fmt -check
terraform -chdir=modules/azure/aad-baseline init -backend=false -input=false
terraform -chdir=modules/azure/aad-baseline validate

Expected: Success! The configuration is valid.

  1. Confirm the example also passes:
terraform -chdir=modules/azure/aad-baseline/examples/basic fmt -check
terraform -chdir=modules/azure/aad-baseline/examples/basic init -backend=false -input=false
terraform -chdir=modules/azure/aad-baseline/examples/basic validate

Expected: Success!.

  1. Run the H1 offline Terratest case:
cd tests/terratest
go test -v -timeout 5m -run 'TestAADBaselineValidate' ./modules/azure/

Expected: 1 top-level test passes.


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

  1. Run the whole offline suite to confirm H1 hasn't regressed any earlier module:
cd tests/terratest
go test -v -timeout 10m ./...

Expected: 18 top-level tests pass.


Part C — tenant integration (real AAD apply, ~25 min, $0)

  1. Export tenant env var + cd into the H1 fixture:
export SNOWOPS_SANDBOX_TENANT_ID="<sandbox-tenant-guid>"
cd tests/terratest/fixtures/aad-baseline
  1. Apply against the sandbox tenant:
terraform init
terraform apply -var "tenant_id=$SNOWOPS_SANDBOX_TENANT_ID"
  1. Spot-check the AAD portal (Microsoft Entra ID → Security → Conditional Access → Named locations):
  2. snowops-office-ips (Trusted, 2 IP ranges)
  3. snowops-corp-vpn-egress (Untrusted, 1 IP range)
  4. snowops-allowed-countries (US, IN, GB)
  5. snowops-blocked-countries (KP, IR, Include Unknown = Yes)

  6. Spot-check the auth-strength policy (Microsoft Entra ID → Security → Authentication methods → Authentication strengths): SnowOps - Phishing Resistant MFA with FIDO2 + WHfB + X.509 multi-factor.


Part D — Graph PATCH steps (operator, ~15 min)

Apply the password protection + branding PATCHes via az rest. Both are idempotent — re-running yields the same state.

  1. Apply password protection:
terraform output -raw password_protection_patch_body | az rest \
  --method PATCH \
  --uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/PasswordProtection' \
  --headers 'Content-Type=application/json' \
  --body @-

Expected: no output (Graph returns 204 No Content on success).

  1. Apply tenant branding:

    TENANT_ID=$(terraform output -raw tenant_id 2>/dev/null || az account show --query tenantId -o tsv)
    terraform output -raw branding_patch_body | az rest \
      --method PATCH \
      --uri "https://graph.microsoft.com/v1.0/organization/$TENANT_ID/branding/localizations/0" \
      --headers 'Content-Type=application/json' \
      --body @-
    

    Expected: returns the updated branding object as JSON.

  2. Verify both via the portal:

    • Microsoft Entra ID → Protection → Authentication methods → Password protection: Lockout threshold = 10, Lockout duration = 60s, Mode = Enforced, Banned password list contains "Welcome1" + "Summer2026" etc.
    • Microsoft Entra ID → Company branding: sign-in page text shows "Authorized access only. Activity is monitored and logged ..."

Pass criteria

  • Part A — terraform validate passes for the module + example
  • Part B — full offline Terratest suite passes (18 top-level tests)
  • Part C — all 4 named locations visible in the portal
  • Part C — phishing-resistant auth-strength policy visible in the portal
  • Part D — Password protection page shows the SnowOps banned-password list (count ≥ 10)
  • Part D — sign-in page renders the SnowOps compliance disclaimer text (open a private browser window and visit https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/authorize?client_id=...&response_type=code&...)
  • All Destroy calls (Part E below) complete without error

Teardown

cd tests/terratest/fixtures/aad-baseline
terraform destroy -var "tenant_id=$SNOWOPS_SANDBOX_TENANT_ID"

Removes the named locations + auth-strength policy. The Graph PATCHes (password protection + branding) are NOT reverted by destroy — to roll them back manually:

# Reset password protection to Microsoft default (Audit mode, threshold 10).
az rest --method PATCH \
  --uri 'https://graph.microsoft.com/beta/policies/authenticationMethodsPolicy/authenticationMethodConfigurations/PasswordProtection' \
  --headers 'Content-Type=application/json' \
  --body '{"@odata.type":"#microsoft.graph.passwordAuthenticationMethodConfiguration","lockoutThreshold":10,"lockoutDurationInSeconds":60,"bannedPasswordCheckOnPremisesMode":"Audit","enableBannedPasswordCheckOnPremises":false,"enableBannedPasswordCheck":true,"bannedPasswordList":[]}'

# Reset branding to Microsoft default (empty).
az rest --method PATCH \
  --uri "https://graph.microsoft.com/v1.0/organization/$TENANT_ID/branding/localizations/0" \
  --headers 'Content-Type=application/json' \
  --body '{"signInPageText":"","usernameHintText":"","backgroundColor":"#FFFFFF"}'

Sign-off

  • Tester: _  |  Date: _  |  Result: PASS / FAIL / N/A
  • Notes: