The challenge
Unlike AWS, Azure has no default network — you cannot create a VM until an entire networking stack exists. And free-tier B-series sizes are capacity-restricted on some subscriptions, so a naive apply simply fails.
Our approach
We provision the stack in dependency order: resource group, virtual network, subnet, public IP, network security group, NIC, the NSG-to-NIC association, and finally the Linux VM. Authentication is SSH-key-only (password login disabled). For subscriptions where the free B1s size is unavailable, we ship a working Spot-instance recipe as a committed, secret-free variables file.
Technical specifics
- Terraform ≥ 1.5.0 with azurerm ~> 4.0; eight resources declared explicitly (no count/for_each).
- VNet 10.0.0.0/16 and subnet 10.0.1.0/24, with an NSG opening SSH/HTTP/HTTPS.
- Default size Standard_B1s (1 vCPU, 1 GiB) on Ubuntu 24.04 LTS; Spot fallback Standard_B2ats_v2.
- Spot configured for capacity-only eviction (max bid -1, deallocate policy) to cut cost.
- Serialized apply with -parallelism=1; auth via an az login session and the subscription id from the CLI.
Example configuration
The network stack is built explicitly before the VM; a Spot block turns on capacity-only eviction where the free size is unavailable.
resource "azurerm_virtual_network" "vnet" {
name = "freetier-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
}
resource "azurerm_subnet" "subnet" {
name = "freetier-subnet"
resource_group_name = azurerm_resource_group.rg.name
virtual_network_name = azurerm_virtual_network.vnet.name
address_prefixes = ["10.0.1.0/24"]
}resource "azurerm_linux_virtual_machine" "vm" {
name = var.name
size = "Standard_B2ats_v2" # Spot fallback; free tier = Standard_B1s
priority = "Spot"
max_bid_price = -1 # evict on capacity only, never on price
eviction_policy = "Deallocate"
admin_username = "azureuser"
# ...network_interface_ids, os_disk, ssh key (password auth disabled)...
}
# export ARM_SUBSCRIPTION_ID="$(az account show --query id -o tsv)"
# terraform apply -auto-approve -parallelism=1Outcome
One Ubuntu 24.04 VM with a public IP reachable on SSH/HTTP/HTTPS, fully declared in version-controlled files, reproducible, and torn down with a single command — with Spot pricing available where it helps.