Manual Azure Release Smoke Matrix¶
This is a maintainer-run release checklist, not an automated test. It documents representative Azure deployments without authorizing CI, contributors, or this repository to create resources. Run it only in an approved disposable subscription with an explicit subscription ID, a budget, and an operator responsible for cleanup.
Entry Gate¶
- Local format, validation, lint, security, Markdown, Pester, and curriculum checks pass first.
- CLZ-110 confirms tenant, subscription, permissions, region, and quota.
git statuscontains no secrets, state, real tfvars, or plan files.- The operator records the start time, subscription ID suffix, region, expected cost tier, and resource-group name in the release record.
- Secrets are viewed only in a private terminal and are never copied into the release record.
Use this base sequence from each selected lesson:
$subscriptionId = az account show --query id --output tsv
terraform init -lockfile=readonly
terraform fmt -check
terraform validate
terraform plan -var "subscription_id=$subscriptionId" -out tfplan
terraform show tfplan
terraform apply tfplan
Stop if the plan targets another subscription, contains an undocumented replacement, broadens administrative access, or creates resources outside the lesson's README.
Smoke Cases¶
| Lesson | Release evidence | Acceptance criteria | Cleanup gate |
|---|---|---|---|
| CLZ-120 | Resource-group output, Azure provisioning state, no-change plan | One correctly tagged group in the selected subscription; second plan is empty | Destroy; state empty; az group exists is false |
| CLZ-200 | IIS URL, VM extension state, HTTP response | Extension is Succeeded; HTTP is 200 and contains the CLZ-200 page marker; RDP CIDR is one /32 |
Destroy VM, disk, IP, network, and group; group no longer exists |
| CLZ-210 | Bastion state, VM NIC/IP evidence, Bastion RDP session | Bastion is Succeeded; VM has no public IP; only Bastion public IP exists; private RDP succeeds |
Destroy promptly because Bastion is hourly; group no longer exists |
| CLZ-260 | VMSS capacity/size, Load Balancer outbound rule, HTTP response | Requested integer capacity is healthy; HTTP is 200; explicit outbound rule exists | Destroy VMSS, Load Balancer, IP, disks, and group |
| CLZ-310/320 | Backend object, blob metadata, state lineage, consumer output keys | CLZ-310 migrates local state to clz310.tfstate; CLZ-320 reads real outputs through its typed object; placeholder values fail early |
Destroy CLZ-320 first; remove/rename CLZ-310 backend block; migrate state local; verify; destroy CLZ-310 and backend |
| CLZ-380 | SQL public-network setting, endpoint approval, in-VNet DNS/TCP result | Public access is disabled; endpoint is Approved; in-VNet normal SQL FQDN resolves privately and TCP 1433 succeeds |
Destroy SQL, endpoint, DNS, network, and group; group no longer exists |
| CLZ-390 | HTTP, NSG rules, Bastion, Key Vault, workspace, outbound rule | HTTP is 200; RDP source is Bastion subnet only; Bastion/vault/workspace succeed; explicit outbound exists | Destroy all capstone resources; state empty; group no longer exists |
Lesson-Specific Commands¶
CLZ-120¶
$resourceGroup = terraform output -raw resource_group_name
az group show --name $resourceGroup --query '{state:properties.provisioningState,tags:tags}' --output json
terraform plan -var "subscription_id=$subscriptionId" -detailed-exitcode
The final command must exit 0, not 2.
CLZ-200¶
$url = terraform output -raw iis_url
(Invoke-WebRequest -Uri $url -UseBasicParsing).StatusCode
az vm extension list --resource-group (terraform output -raw resource_group_name) --output table
CLZ-210¶
$resourceGroup = terraform output -raw resource_group_name
az network bastion show --resource-group $resourceGroup --name (terraform output -raw bastion_name) --query provisioningState --output tsv
az vm show --ids (terraform output -raw private_windows_vm_id) --show-details --query '{private:privateIps,public:publicIps,power:powerState}' --output json
Complete one Bastion RDP session. Do not add temporary VM public access to make the test pass. General Windows update/activation egress is intentionally absent from this lesson and is assessed as a documented production delta.
CLZ-260¶
$resourceGroup = terraform output -raw resource_group_name
(Invoke-WebRequest -Uri (terraform output -raw vmss_load_balancer_url) -UseBasicParsing).StatusCode
az vmss list --resource-group $resourceGroup --query '[].{capacity:sku.capacity,size:sku.name,state:provisioningState}' --output table
$lb = az network lb list --resource-group $resourceGroup --query '[0].name' --output tsv
az network lb outbound-rule list --resource-group $resourceGroup --lb-name $lb --output table
CLZ-310 and CLZ-320¶
Follow both lesson READMEs without shortcutting the state transitions. Capture only nonsensitive output keys and state lineage/serial, not state content.
# CLZ-310 after migration
$backend = terraform output -json backend_configuration | ConvertFrom-Json
az storage blob show --auth-mode login --account-name $backend.storage_account_name --container-name $backend.container_name --name $backend.key --query name --output tsv
# CLZ-320
terraform output -json shared_output_keys
For cleanup, destroy CLZ-320, return to CLZ-310, rename/remove untracked
backend.tf, run terraform init -migrate-state, verify local state, remove
runtime backend files, and only then destroy the backend resources.
CLZ-380¶
$server = terraform output -raw sql_server_name
$resourceGroup = terraform output -raw resource_group_name
az sql server show --resource-group $resourceGroup --name $server --query publicNetworkAccess --output tsv
az network private-endpoint list --resource-group $resourceGroup --query '[].privateLinkServiceConnections[0].privateLinkServiceConnectionState.status' --output tsv
# Run from an approved client in the linked VNet:
Resolve-DnsName "$server.database.windows.net"
Test-NetConnection "$server.database.windows.net" -Port 1433
Azure control-plane success from the operator laptop does not replace the in-VNet DNS and TCP data-plane evidence.
CLZ-390¶
$resourceGroup = terraform output -raw resource_group_name
(Invoke-WebRequest -Uri (terraform output -raw capstone_load_balancer_url) -UseBasicParsing).StatusCode
az network nsg rule list --resource-group $resourceGroup --nsg-name (terraform output -raw web_network_security_group_name) --output table
az network bastion list --resource-group $resourceGroup --query '[].provisioningState' --output tsv
az keyvault show --name (terraform output -raw key_vault_name) --query properties.provisioningState --output tsv
Exit Gate¶
- Every selected lesson records pass/fail and a concise failure category.
- Every created resource group is confirmed absent after destroy.
- Terraform state lists no managed resources after cleanup, except a deliberately retained backend that has a named owner and expiry.
- The operator checks Azure Resource Graph/cost views for orphaned VM, disk, public IP, Bastion, NAT, Private Endpoint, SQL, and Log Analytics resources.
- Runtime tfvars, backend files, plans, and state are removed locally; the committed dependency lockfiles remain.
- A failed cleanup blocks release until an owner and remediation time are recorded.