Manual Test Runbook — B5: PIM for Azure Resources (Owner / Contributor / UAA)
Owner: Sagar | Time: ~5 min (Parts A + B) · +20 min (optional Part C sandbox apply) · +25 min (optional Part D live activation drill) | Sandbox: snowops-sandbox-01
Promotes B5 (
modules/azure/pim-azure-resources/) from 🟦 Code Complete → 🟩 Shipped. Parts A + B are 100% offline. Parts C + D require Entra ID P2 on the sandbox tenant (PIM is a P2 feature) and mutate the subscription's real PIM config for Owner / Contributor / UAA — run in sandbox only, reset after.
Prerequisites
- Sandbox subscription access active (PIM activated if required)
-
az logindone;az account showconfirms the sandbox subscription is selected - Identity has Owner on the sandbox subscription (needed to write eligible role assignments + role-management policies)
- Entra ID P2 on the sandbox tenant (Parts C + D only — PIM requires P2)
- Local tooling:
terraform >= 1.6,az CLI >= 2.50 -
SNOWOPS_SANDBOX_SUBSCRIPTION_IDandSNOWOPS_SANDBOX_TENANT_IDenv vars set - (Parts C + D) four real sandbox AAD groups: tier-0 eligible, tier-1 eligible, tier-0 approver, break-glass (the break-glass group must already hold permanent Owner — wire via B3); a test user who is a member of an eligible group for the drill
- Working directory: repo root
Steps
Part A — terraform fmt + validate (offline, ~2 min)
- Module:
terraform -chdir=modules/azure/pim-azure-resources fmt -recursive -check
terraform -chdir=modules/azure/pim-azure-resources init -backend=false -input=false
terraform -chdir=modules/azure/pim-azure-resources validate
Expected: Success! The configuration is valid.
- Example:
terraform -chdir=modules/azure/pim-azure-resources/examples/basic fmt -check
terraform -chdir=modules/azure/pim-azure-resources/examples/basic init -backend=false -input=false
terraform -chdir=modules/azure/pim-azure-resources/examples/basic validate
Expected: Success! The configuration is valid.
- B5-targeted offline Terratest:
Expected: --- PASS: TestPIMAzureResourcesValidate.
Part B — full offline suite + pre-commit (offline, ~3 min)
- Full offline suite (confirms B5 hasn't regressed F0–F6 / H1–H3 / B2–B4):
Expected: 22 top-level tests pass (the 21 from v0.28 plus
TestPIMAzureResourcesValidate).
- pre-commit + pre-push on every new file:
pre-commit run --files \
modules/azure/pim-azure-resources/versions.tf \
modules/azure/pim-azure-resources/variables.tf \
modules/azure/pim-azure-resources/main.tf \
modules/azure/pim-azure-resources/outputs.tf \
modules/azure/pim-azure-resources/README.md \
modules/azure/pim-azure-resources/examples/basic/versions.tf \
modules/azure/pim-azure-resources/examples/basic/variables.tf \
modules/azure/pim-azure-resources/examples/basic/main.tf \
modules/azure/pim-azure-resources/examples/basic/outputs.tf \
tests/terratest/fixtures/pim-azure-resources/main.tf \
tests/terratest/fixtures/pim-azure-resources/variables.tf \
tests/terratest/fixtures/pim-azure-resources/outputs.tf \
tests/terratest/modules/azure/pim_azure_resources_validate_test.go \
docs/runbooks/test/B5.md
pre-commit run --hook-stage pre-push --all-files
Expected: every hook PASS; FINAL_EXIT=0.
Part C — sandbox apply (requires Entra ID P2, ~20 min)
Mutates the sandbox subscription's PIM config for Owner / Contributor / UAA. Reset in Teardown.
- Confirm env + the four group IDs, then apply the example:
export SNOWOPS_SANDBOX_SUBSCRIPTION_ID="<sandbox-subscription-guid>"
export SNOWOPS_SANDBOX_TENANT_ID="<sandbox-tenant-guid>"
terraform -chdir=modules/azure/pim-azure-resources/examples/basic apply \
-var subscription_id=$SNOWOPS_SANDBOX_SUBSCRIPTION_ID \
-var tenant_id=$SNOWOPS_SANDBOX_TENANT_ID \
-var tier0_eligible_group_object_id=<tier0-eligible-group> \
-var tier1_eligible_group_object_id=<tier1-eligible-group> \
-var tier0_approver_group_object_id=<approver-group> \
-var break_glass_group_object_id=<break-glass-group>
- Watch for:
azurerm_pim_eligible_role_assignment.tier0["Owner/..."]+["User Access Administrator/..."]: Creation complete.azurerm_pim_eligible_role_assignment.tier1["Contributor/..."]: Creation complete.azurerm_role_management_policy.tier0["Owner"]+["User Access Administrator"]: Creation complete.-
azurerm_role_management_policy.tier1["Contributor"]: Creation complete. -
Verify the eligibility + activation policy landed in the portal or via CLI:
# Eligible assignments at subscription scope
az role assignment list --scope "/subscriptions/$SNOWOPS_SANDBOX_SUBSCRIPTION_ID" \
--include-classic-administrators false -o table # active assignments
# (PIM eligible assignments show in Entra → PIM → Azure resources → Roles)
Expected (portal): under PIM → Azure resources → → Roles, the eligible group appears as Eligible for Owner + UAA (tier-0) and Contributor (tier-1); the role settings for Owner/UAA show Require approval = Yes, MFA + justification + ticket on activation, 8h max; Contributor shows MFA + justification, 4h, no approval.
Part D — live activation drill (optional, requires P2 + a test user, ~25 min)
-
Tier-1 (no approval): sign in as a test user in the tier-1 eligible group → PIM → My roles → Azure resources → Activate Contributor. Expect an MFA prompt + a justification field, max 4h, and immediate activation (no approval gate).
-
Tier-0 (approval): as a test user in the tier-0 eligible group, activate Owner (or UAA). Expect MFA + justification + a ticket field + the request entering Pending approval. As a member of the approver group, approve it under PIM → Approve requests. Confirm the role activates only after approval, for max 8h.
-
Confirm activation expiry: after the window (or by deactivating manually), the active assignment disappears and the principal reverts to eligible-only.
Pass criteria
- Part A —
terraform validatepasses for module + example - Part A —
TestPIMAzureResourcesValidatepasses - Part B — full offline suite passes (22 top-level tests)
- Part B — pre-commit + pre-push all green
- Part C — eligible assignments created for tier-0 (Owner, UAA) + tier-1 (Contributor)
- Part C — role-management policies show approval=Yes/8h for tier-0, no-approval/4h for tier-1, MFA+justification on both
- (Part D) tier-1 activation: MFA prompt, immediate, ≤4h
- (Part D) tier-0 activation: MFA + ticket, pending approval, activates only after an approver approves, ≤8h
- Break-glass group retains permanent Owner throughout (never gated by PIM)
Teardown
terraform -chdir=modules/azure/pim-azure-resources/examples/basic destroy \
-var subscription_id=$SNOWOPS_SANDBOX_SUBSCRIPTION_ID \
-var tenant_id=$SNOWOPS_SANDBOX_TENANT_ID \
-var tier0_eligible_group_object_id=<tier0-eligible-group> \
-var tier1_eligible_group_object_id=<tier1-eligible-group> \
-var tier0_approver_group_object_id=<approver-group> \
-var break_glass_group_object_id=<break-glass-group>
Destroy removes the eligible assignments and resets the role-management policies to Azure defaults (looser than SnowOps). The break-glass group's permanent Owner is NOT touched (owned by B3 / out-of-band), so the subscription never loses its standing administrator.
Sign-off
- Tester: _ | Date: _ | Result: PASS / FAIL / N/A
- Notes: