Virtual Machines
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 \
...
Availability Zones (Recommended)
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
- Public IP on every VM — use Bastion and private subnets
- B-series for production sustained load — CPU credits exhaust
- No availability zones — single zone failure = outage
- Over-provisioned disk tier — Standard SSD sufficient for most web servers
- Ignoring Azure Advisor — free right-sizing recommendations
- 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.