From 37e899e89cf14affc7253213cd134015ad10daad Mon Sep 17 00:00:00 2001 From: rishabkumar7 Date: Thu, 1 Oct 2026 14:08:01 -0500 Subject: [PATCH 1/2] fix(azure): check VM size availability at plan time - Add the azapi provider to look up the requested VM size in the Microsoft.Compute/skus API, which azurerm has no data source for. - Fail at plan time with a clear message when the size is not offered in the region, is restricted for the subscription (NotAvailableForSubscription, common on Azure for Students), or is Arm64 (the lab uses an x64 Ubuntu image). - Raise the bootstrap dpkg lock timeout from 120s to 300s to match the AWS and GCP bootstraps. - Document the plan-time messages, x64 size options, Azure for Students region policy and credit errors, and the "resource group already exists" error in TROUBLESHOOTING.md. - Replace the README's external Spot-instance workaround link with the in-repo Azure for Students section. Refs #99 --- TROUBLESHOOTING.md | 78 ++++++++++++++++++++++++++++++++++++++++++++-- azure/README.md | 5 +-- azure/main.tf | 46 +++++++++++++++++++++++++-- 3 files changed, 122 insertions(+), 7 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index af3b657..99ffa1a 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -14,6 +14,8 @@ This guide shows examples of errors you might see when deploying or using the li - [Azure](#azure) - [Azure: SkuNotAvailable / Capacity errors](#azure-skunotavailable--capacity-errors) - [Azure: Quota limit errors](#azure-quota-limit-errors) +- [Azure: Azure for Students errors](#azure-azure-for-students-errors) +- [Azure: Resource group already exists](#azure-resource-group-already-exists) - [GCP](#gcp) - [GCP: API not enabled / Billing errors](#gcp-api-not-enabled--billing-errors) - [GCP: Machine type not offered / Zone capacity errors](#gcp-machine-type-not-offered--zone-capacity-errors) @@ -263,17 +265,31 @@ terraform apply \ The Terraform commands in this Azure section assume you are running them from the `azure/` directory. -The default VM size is `Standard_B1s`, and the default region is `East US`. +The default VM size is `Standard_B1s`, and the default region is `East US`. The lab uses an x64 Ubuntu image, so the VM size must be x64. Arm64 sizes such as `Standard_B2pts_v2` won't work. ### Azure: SkuNotAvailable / Capacity errors -If Terraform fails with an error like: +`terraform plan` checks the VM size before creating anything. If the size can't be used, it stops with one of these messages: + +```text +VM size is not offered in . +``` + +```text +VM size is not available for your subscription in (NotAvailableForSubscription). +``` + +The second one is common on Azure for Students, where `Standard_B1s` is often restricted in `East US`. Switch region or VM size and retry. + +If the check passes but `terraform apply` still fails with an error like: ```text SkuNotAvailable: The requested VM size ... is currently not available in location "your location" ``` -This usually means the selected region does not currently have capacity for that SKU, or your subscription is restricted in that region. +the region is temporarily out of capacity for that size. Retry later, or switch region or VM size. + +To find a size that works, x64 sizes that are small enough for the lab include `Standard_B1s`, `Standard_B1ms`, `Standard_B2s`, and `Standard_B2ats_v2`. Check whether `Standard_B1s` is available in a region: @@ -330,6 +346,62 @@ terraform apply \ -var azure_vm_size="Standard_B1ms" ``` +### Azure: Azure for Students errors + +Azure for Students subscriptions have extra limits beyond VM size restrictions. + +**Allowed regions.** Many Student subscriptions only allow deployments to a small set of regions. Deploying elsewhere fails with an error like: + +```text +RequestDisallowedByAzure: Resource 'ctf-resources' was disallowed by Azure: This policy maintains a set of best available regions where your subscription can deploy resources. +``` + +List the regions your subscription allows: + +```sh +az policy assignment list \ + --query "[].parameters.listOfAllowedLocations.value" \ + -o json +``` + +If that prints an empty list, the restriction is set above your subscription and isn't visible to you. Try another region, such as `eastus2`, `westus2`, or `centralus`. + +Retry with an allowed region: + +```sh +terraform apply \ + -var subscription_id="YOUR_AZURE_SUBSCRIPTION_ID" \ + -var az_region="eastus2" +``` + +**Credits used up.** When the Student credit runs out, the subscription is disabled and deployments fail with errors like `ReadOnlyDisabledSubscription`. Check the subscription state: + +```sh +az account show --query state -o tsv +``` + +If it isn't `Enabled`, check your remaining credit in the Azure portal under **Subscriptions**, or upgrade to Pay-As-You-Go. + +### Azure: Resource group already exists + +If Terraform fails with: + +```text +A resource with the ID "/subscriptions/.../resourceGroups/ctf-resources" already exists +``` + +a lab from an earlier deploy is still there, but this Terraform directory has no record of it (for example, you deployed from another clone or deleted `terraform.tfstate`). Check what's in it first: + +```sh +az resource list --resource-group ctf-resources -o table +``` + +If it only contains old lab resources, delete it and retry. This permanently deletes everything in the group: + +```sh +az group delete --name ctf-resources +``` + ## GCP The Terraform commands in this GCP section assume you are running them from the `gcp/` directory. Add `-var gcp_project="YOUR_GCP_PROJECT_ID"` to each `terraform apply`. diff --git a/azure/README.md b/azure/README.md index 4aed476..c894671 100644 --- a/azure/README.md +++ b/azure/README.md @@ -22,7 +22,7 @@ Deploy the lab here, then follow the [Playing the Lab guide](../GUIDE.md) to con 4. A GitHub fork of this repository > [!NOTE] -> If you have an Azure Student account, you may encounter errors. See [this workaround](https://github.com/g-now-zero/l2c-guides/blob/main/posts/ctf-azure-spot-instances-guide.md). +> Azure for Students subscriptions often restrict VM sizes and regions. If you're using one, see [Azure for Students errors](../TROUBLESHOOTING.md#azure-azure-for-students-errors). ## Deploy the Lab @@ -56,10 +56,11 @@ If deployment fails, see [TROUBLESHOOTING.md](../TROUBLESHOOTING.md#azure). ### VM size / capacity errors -If `terraform apply` fails with `SkuNotAvailable` or quota/capacity errors, switching region and/or VM size (`azure_vm_size`, default `Standard_B1s`) is usually the fastest fix. See: +`terraform plan` checks that the VM size (`azure_vm_size`, default `Standard_B1s`) is available for your subscription in your region. If it isn't, or `terraform apply` fails with `SkuNotAvailable` or quota errors, switching region and/or VM size is usually the fastest fix. See: - [Azure: SkuNotAvailable / Capacity errors](../TROUBLESHOOTING.md#azure-skunotavailable--capacity-errors) - [Azure: Quota limit errors](../TROUBLESHOOTING.md#azure-quota-limit-errors) +- [Azure: Azure for Students errors](../TROUBLESHOOTING.md#azure-azure-for-students-errors) ## Play the Lab diff --git a/azure/main.tf b/azure/main.tf index 481049a..b7b21f2 100644 --- a/azure/main.tf +++ b/azure/main.tf @@ -7,6 +7,10 @@ terraform { source = "hashicorp/azurerm" version = "~> 5.6" } + azapi = { + source = "Azure/azapi" + version = "~> 2.13" + } null = { source = "hashicorp/null" version = "~> 3.0" @@ -44,6 +48,10 @@ variable "setup_release_tag" { } locals { + # "East US" -> "eastus", the form the SKU API uses + az_location = lower(replace(var.az_region, " ", "")) + vm_size_sku = try(data.azapi_resource_list.vm_size.output.skus[0], null) + setup_asset_name = "linux-ctfs-setup.tar.gz" setup_release_base = var.setup_release_tag == "latest" ? "https://github.com/learntocloud/linux-ctfs/releases/latest/download" : "https://github.com/learntocloud/linux-ctfs/releases/download/${var.setup_release_tag}" setup_release_url = "${local.setup_release_base}/${local.setup_asset_name}" @@ -107,7 +115,7 @@ locals { apt_get_update_with_retry() { local attempt for attempt in 1 2 3 4 5; do - if apt-get -o DPkg::Lock::Timeout=120 -o Acquire::Retries=3 update; then + if apt-get -o DPkg::Lock::Timeout=300 -o Acquire::Retries=3 update; then return 0 fi echo "apt-get update failed. Attempt $${attempt}/5." @@ -119,7 +127,7 @@ locals { wait_for_cloud_init apt_get_update_with_retry - apt-get -o DPkg::Lock::Timeout=120 -o Acquire::Retries=3 install -y ca-certificates curl tar gzip coreutils + apt-get -o DPkg::Lock::Timeout=300 -o Acquire::Retries=3 install -y ca-certificates curl tar gzip coreutils cd "$${WORK_DIR}" download_with_retry "$${SETUP_URL}" "$${ASSET_NAME}" @@ -151,6 +159,25 @@ provider "azurerm" { resource_provider_registrations = "core" } +# azapi is only used to look up VM size availability, which azurerm can't do. +provider "azapi" { + subscription_id = var.subscription_id + skip_provider_registration = true +} + +# Student and free subscriptions often can't use some VM sizes in some regions +# (SkuNotAvailable). Look the size up at plan time so that fails early. +data "azapi_resource_list" "vm_size" { + type = "Microsoft.Compute/skus@2021-07-01" + parent_id = "/subscriptions/${var.subscription_id}" + query_parameters = { + "$filter" = ["location eq '${local.az_location}'"] + } + response_export_values = { + skus = "value[?resourceType=='virtualMachines' && name=='${var.azure_vm_size}'].{restrictions: restrictions[?type=='Location'].reasonCode, architecture: capabilities[?name=='CpuArchitectureType'].value | [0]}" + } +} + # Create a resource group resource "azurerm_resource_group" "ctf_rg" { name = "ctf-resources" @@ -271,6 +298,21 @@ resource "azurerm_linux_virtual_machine" "ctf_vm" { } custom_data = var.use_local_setup ? base64encode(local.local_bootstrap_script) : null + + lifecycle { + precondition { + condition = local.vm_size_sku != null + error_message = "VM size ${var.azure_vm_size} is not offered in ${var.az_region}. Try a different azure_vm_size (e.g. Standard_B1s or Standard_B1ms) or az_region. See TROUBLESHOOTING.md." + } + precondition { + condition = try(length(local.vm_size_sku.restrictions), 0) == 0 + error_message = "VM size ${var.azure_vm_size} is not available for your subscription in ${var.az_region} (${join(", ", try(local.vm_size_sku.restrictions, []))}). This is common on Azure for Students. Try a different azure_vm_size or az_region. See TROUBLESHOOTING.md." + } + precondition { + condition = try(local.vm_size_sku.architecture, "x64") == "x64" + error_message = "VM size ${var.azure_vm_size} is ${try(local.vm_size_sku.architecture, "unknown")}, but the lab uses an x64 Ubuntu image. Choose an x64 size such as Standard_B1s, Standard_B1ms, or Standard_B2ats_v2." + } + } } resource "azurerm_virtual_machine_extension" "release_setup" { From 0c3ee8192447e1cd328d58f7781064244dc3f58e Mon Sep 17 00:00:00 2001 From: rishabkumar7 Date: Thu, 1 Oct 2026 14:30:12 -0500 Subject: [PATCH 2/2] fix(azure): handle missing SKU architecture and scope az commands to the target subscription --- TROUBLESHOOTING.md | 13 +++++++++---- azure/main.tf | 6 ++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md index 99ffa1a..0e873e1 100644 --- a/TROUBLESHOOTING.md +++ b/TROUBLESHOOTING.md @@ -360,11 +360,13 @@ List the regions your subscription allows: ```sh az policy assignment list \ + --subscription "YOUR_AZURE_SUBSCRIPTION_ID" \ + --disable-scope-strict-match \ --query "[].parameters.listOfAllowedLocations.value" \ -o json ``` -If that prints an empty list, the restriction is set above your subscription and isn't visible to you. Try another region, such as `eastus2`, `westus2`, or `centralus`. +If that prints an empty list, the restriction isn't visible to your account. Try another region, such as `eastus2`, `westus2`, or `centralus`. Retry with an allowed region: @@ -377,7 +379,7 @@ terraform apply \ **Credits used up.** When the Student credit runs out, the subscription is disabled and deployments fail with errors like `ReadOnlyDisabledSubscription`. Check the subscription state: ```sh -az account show --query state -o tsv +az account show --subscription "YOUR_AZURE_SUBSCRIPTION_ID" --query state -o tsv ``` If it isn't `Enabled`, check your remaining credit in the Azure portal under **Subscriptions**, or upgrade to Pay-As-You-Go. @@ -393,13 +395,16 @@ A resource with the ID "/subscriptions/.../resourceGroups/ctf-resources" already a lab from an earlier deploy is still there, but this Terraform directory has no record of it (for example, you deployed from another clone or deleted `terraform.tfstate`). Check what's in it first: ```sh -az resource list --resource-group ctf-resources -o table +az resource list \ + --subscription "YOUR_AZURE_SUBSCRIPTION_ID" \ + --resource-group ctf-resources \ + -o table ``` If it only contains old lab resources, delete it and retry. This permanently deletes everything in the group: ```sh -az group delete --name ctf-resources +az group delete --subscription "YOUR_AZURE_SUBSCRIPTION_ID" --name ctf-resources ``` ## GCP diff --git a/azure/main.tf b/azure/main.tf index b7b21f2..66a984d 100644 --- a/azure/main.tf +++ b/azure/main.tf @@ -51,6 +51,8 @@ locals { # "East US" -> "eastus", the form the SKU API uses az_location = lower(replace(var.az_region, " ", "")) vm_size_sku = try(data.azapi_resource_list.vm_size.output.skus[0], null) + # Sizes that don't report an architecture are x64 + vm_size_architecture = coalesce(try(local.vm_size_sku.architecture, null), "x64") setup_asset_name = "linux-ctfs-setup.tar.gz" setup_release_base = var.setup_release_tag == "latest" ? "https://github.com/learntocloud/linux-ctfs/releases/latest/download" : "https://github.com/learntocloud/linux-ctfs/releases/download/${var.setup_release_tag}" @@ -309,8 +311,8 @@ resource "azurerm_linux_virtual_machine" "ctf_vm" { error_message = "VM size ${var.azure_vm_size} is not available for your subscription in ${var.az_region} (${join(", ", try(local.vm_size_sku.restrictions, []))}). This is common on Azure for Students. Try a different azure_vm_size or az_region. See TROUBLESHOOTING.md." } precondition { - condition = try(local.vm_size_sku.architecture, "x64") == "x64" - error_message = "VM size ${var.azure_vm_size} is ${try(local.vm_size_sku.architecture, "unknown")}, but the lab uses an x64 Ubuntu image. Choose an x64 size such as Standard_B1s, Standard_B1ms, or Standard_B2ats_v2." + condition = local.vm_size_architecture == "x64" + error_message = "VM size ${var.azure_vm_size} is ${local.vm_size_architecture}, but the lab uses an x64 Ubuntu image. Choose an x64 size such as Standard_B1s, Standard_B1ms, or Standard_B2ats_v2." } } }