The Azure quickstart gets a VM running in five minutes. It also skips every decision that matters once that VM is carrying real traffic: which SKU family, how it authenticates to other services, and how its network exposure is scoped. This is the baseline I use on every Azure project, past the quickstart.
Region and availability design
Azure's region list is large, but the decision that actually affects uptime is Availability Zones vs. Availability Sets:
- Availability Zones spread VMs across physically separate datacenters within a region, each with independent power and networking. Use these whenever the region supports them — they're the stronger guarantee.
- Availability Sets spread VMs across fault domains within a single datacenter. Use this only in regions without zone support.
az vm create \
--resource-group prod-rg \
--name app-vm-1 \
--zone 1 \
--image Ubuntu2204 \
--size Standard_D4s_v5 \
--vnet-name prod-vnet \
--subnet app-subnet \
--nsg app-nsg \
--public-ip-address "" \
--generate-ssh-keys
--public-ip-address "" is deliberate — application VMs shouldn't have public IPs at all; they sit behind a Load Balancer or Application Gateway, and administrative access goes through Azure Bastion, not a public SSH endpoint.
Choosing a VM SKU family
Azure's naming tells you the shape of the machine once you know the pattern:
| Series | Use case | Comparable to |
|---|---|---|
Dsv5 |
General purpose, balanced CPU/memory | AWS m6i, Alibaba ecs.g7 |
Fsv2 |
Compute-optimized | AWS c6i |
Esv5 |
Memory-optimized | AWS r6i |
Lsv3 |
Storage-optimized, local NVMe | AWS i4i |
For a typical containerized service, Dsv5 covers it. Move to Fsv2 for CPU-bound work (build agents, encoding pipelines) and Esv5 for in-memory workloads that outgrow a managed cache tier.
Identity first: Managed Identity, not stored credentials
The single highest-leverage security decision on any Azure VM is enabling a system-assigned Managed Identity and granting it exactly the roles it needs via Azure RBAC — never embedding a service principal's client secret on the VM itself.
az vm identity assign --resource-group prod-rg --name app-vm-1
az role assignment create \
--assignee-object-id $(az vm show -g prod-rg -n app-vm-1 --query identity.principalId -o tsv) \
--role "Storage Blob Data Reader" \
--scope /subscriptions/<sub-id>/resourceGroups/prod-rg/providers/Microsoft.Storage/storageAccounts/appstorage
From inside the VM, any SDK that supports DefaultAzureCredential picks this identity up automatically — no key vault lookups, no rotation burden, no secret that can leak in a log line.
Network Security Groups: default-deny, then open narrowly
az network nsg rule create \
--resource-group prod-rg \
--nsg-name app-nsg \
--name allow-lb-only \
--priority 100 \
--source-address-prefixes AzureLoadBalancer \
--destination-port-ranges 8080 \
--access Allow \
--protocol Tcp
Two habits that prevent the incidents I've actually seen happen:
- Never create a rule with
--source-address-prefixes '*'for anything other than a public load balancer's health probe. It's the fastest way to accidentally expose an internal service. - Tag every resource on creation —
Environment,Owner,CostCenter. Retrofitting tags across a resource group after the fact is a genuinely miserable Friday.
Wiring it into the rest of the stack
An Azure VM in isolation isn't useful on its own. What I connect immediately:
- Azure Monitor Agent, installed via VM extension, so CPU/memory/disk alerts exist before the first incident rather than after.
- Azure Bastion for administrative access — no public SSH/RDP surface, ever.
- A VNet with private subnets for anything that isn't a load balancer or Bastion host itself.
What I'd tell someone starting today
Provision this through Terraform or Bicep from the very first VM, even for a single-instance proof of concept. The moment a second environment exists — staging next to production — doing this by hand in the portal is exactly the thing that drifts quietly and becomes the incident nobody can explain six months later. I cover the Terraform version of this same VM in a follow-up article.