Azure Virtual Machines (VMs) provide on-demand, scalable computing resources in the cloud. VMs give you full control over the operating system — ideal for legacy applications, custom software, and workloads that require specific OS configurations.

VM Components

Component Description
VM Size vCPU, RAM, and optional GPU/local storage (e.g., Standard_D2s_v5)
OS Disk Boot volume (Windows or Linux)
Data Disks Additional persistent storage
NIC Network interface connecting to VNet subnet
NSG Network Security Group (firewall rules)
Availability Set/Zone High availability placement

Create a Linux VM

  # Create VM with SSH key
az vm create \
  --resource-group rg-webapp-dev \
  --name vm-web-01 \
  --image Ubuntu2204 \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard \
  --vnet-name vnet-webapp \
  --subnet subnet-web \
  --nsg-rule SSH \
  --tags environment=dev project=webapp

# Get public IP
az vm show --name vm-web-01 --resource-group rg-webapp-dev \
  --show-details --query publicIps -o tsv

# Connect via SSH
ssh azureuser@<public-ip>
  

Windows VM

  az vm create \
  --resource-group rg-webapp-dev \
  --name vm-windows-01 \
  --image Win2022Datacenter \
  --size Standard_D2s_v5 \
  --admin-username azureadmin \
  --admin-password 'ComplexP@ssw0rd!' \
  --public-ip-sku Standard
  

VM Size Families

Series Profile Use Case Example
B-series Burstable Dev/test, low CPU average Standard_B2s (2 vCPU, 4 GB)
D-series General purpose Web servers, apps Standard_D2s_v5
E-series Memory optimized Databases, caches Standard_E4s_v5
F-series Compute optimized Batch processing Standard_F4s_v2
N-series GPU ML, rendering Standard_NC6s_v3
  # List available sizes in a region
az vm list-sizes --location eastus --output table
  

Use Azure VM Selector to compare. Azure Hybrid Benefit saves up to 40% if you have existing Windows Server licenses.

Managed Disks

Disk Type IOPS Use Case Cost
Standard HDD Up to 500 Backup, infrequent access Lowest
Standard SSD Up to 6,000 Web servers, dev/test Medium
Premium SSD Up to 20,000 Production databases Higher
Premium SSD v2 Configurable IOPS High-performance workloads Flexible
Ultra Disk Up to 160,000 Mission-critical databases Highest
  # Add data disk
az vm disk attach \
  --resource-group rg-webapp-dev \
  --vm-name vm-web-01 \
  --disk datadisk1 \
  --size-gb 128 \
  --sku Premium_LRS \
  --new

# On the VM:
sudo mkfs -t ext4 /dev/sdc
sudo mkdir /data && sudo mount /dev/sdc /data
  

Network Security Groups

  # Create NSG with rules
az network nsg create --resource-group rg-webapp-dev --name nsg-web

az network nsg rule create \
  --resource-group rg-webapp-dev \
  --nsg-name nsg-web \
  --name AllowHTTPS \
  --priority 100 \
  --destination-port-ranges 443 \
  --access Allow \
  --protocol Tcp

az network nsg rule create \
  --resource-group rg-webapp-dev \
  --nsg-name nsg-web \
  --name AllowSSH \
  --priority 110 \
  --source-address-prefixes YOUR_IP/32 \
  --destination-port-ranges 22 \
  --access Allow \
  --protocol Tcp
  

Best practice: Place VMs in private subnets; access via Azure Bastion (no public IP needed).

High Availability

Availability Sets (Legacy)

Spread VMs across fault domains and update domains within a region:

  az vm create \
  --resource-group rg-webapp-prod \
  --name vm-web-01 \
  --availability-set web-availability-set \
  ...
  

Deploy VMs across physically separate data centers:

  az vm create \
  --resource-group rg-webapp-prod \
  --name vm-web-zone1 \
  --zone 1 \
  --size Standard_D2s_v5 \
  ...

az vm create \
  --resource-group rg-webapp-prod \
  --name vm-web-zone2 \
  --zone 2 \
  --size Standard_D2s_v5 \
  ...
  

Virtual Machine Scale Sets (VMSS)

Auto-scaling group of identical VMs behind a load balancer:

  az vmss create \
  --resource-group rg-webapp-prod \
  --name vmss-web \
  --image Ubuntu2204 \
  --upgrade-policy-mode automatic \
  --instance-count 2 \
  --vm-sku Standard_D2s_v5 \
  --load-balancer web-lb \
  --vnet-name vnet-webapp \
  --subnet subnet-web \
  --nsg nsg-web

# Auto-scale rule
az monitor autoscale create \
  --resource-group rg-webapp-prod \
  --resource vmss-web \
  --resource-type Microsoft.Compute/virtualMachineScaleSets \
  --name autoscale-web \
  --min-count 2 --max-count 10 --count 2

az monitor autoscale rule create \
  --resource-group rg-webapp-prod \
  --autoscale-name autoscale-web \
  --condition "Percentage CPU > 70 avg 5m" \
  --scale out 2
  

Azure Bastion (Secure Access)

Access VMs without public IPs:

  az network bastion create \
  --resource-group rg-webapp-prod \
  --name bastion-host \
  --vnet-name vnet-webapp \
  --public-ip-address bastion-pip \
  --location eastus

# Connect via Portal or CLI
az network bastion ssh \
  --name bastion-host \
  --resource-group rg-webapp-prod \
  --target-resource-id /subscriptions/.../vm-web-01 \
  --auth-type ssh-key \
  --username azureuser \
  --ssh-key ~/.ssh/id_rsa
  

Real-World Scenario: Three-Tier Application

Tier VM Config Networking
Web (VMSS) 2-10× Standard_D2s_v5, zones 1+2 Public subnet, behind App Gateway
App (VMSS) 2-6× Standard_D4s_v5 Private subnet, NSG allows web tier only
Database Azure SQL (PaaS preferred) Private endpoint, no public access

Use PaaS (App Service, Azure SQL) when possible; VMs for workloads requiring OS control.

VM vs App Service vs AKS

Criteria VM App Service AKS
OS control Full None (PaaS) Container-level
Management You patch OS Microsoft manages You manage containers
Scaling VMSS Built-in auto-scale HPA + cluster autoscaler
Cost Predictable Tier-based Cluster + nodes
Best for Legacy, custom OS Web apps, APIs Microservices

Common Mistakes

  1. Public IP on every VM — use Bastion and private subnets
  2. B-series for production sustained load — CPU credits exhaust
  3. No availability zones — single zone failure = outage
  4. Over-provisioned disk tier — Standard SSD sufficient for most web servers
  5. Ignoring Azure Advisor — free right-sizing recommendations
  6. Manual patching — enable Azure Update Manager

Troubleshooting

Issue Check Fix
Cannot SSH/RDP NSG rules, public IP, Bastion config Verify NSG allows your IP on port 22/3389
VM not starting Boot diagnostics Enable boot diagnostics; check serial console
Disk full Disk usage metrics Expand disk: az disk update --size-gb 256
High CPU Process analysis Resize VM or add VMSS instances
Extension failed Extension logs az vm extension list --vm-name vm-web-01 -g rg-webapp-dev

Best Practices

  • Deploy across Availability Zones for production
  • Use VMSS instead of individual VMs for scalable tiers
  • Access via Azure Bastion — no public IPs on VMs
  • Enable Azure Monitor Agent for metrics and logs
  • Use Managed Disks with Premium SSD for production
  • Apply Azure Hybrid Benefit for Windows/SQL licensing savings
  • Automate patching with Azure Update Manager
  • Deallocate dev VMs when not in use (stop billing for compute)

Next: Azure App Service.