Azure
intermediate
35-45 minutes
Azure Virtual Machines
Deploy applications on Azure VMs with full control over the operating system and configuration.
What you'll accomplish
A configured VM running your application with SSH access and networking
Prerequisites
- Azure account with subscription
- Azure CLI installed
- Basic Linux command knowledge
What are Azure Virtual Machines?
Azure VMs provide full control over the operating system and software:
- Complete control - Install any software
- Multiple OS options - Linux distributions, Windows Server
- Flexible sizing - From 1 vCPU to 416 vCPUs
- Persistent storage - Managed disks
- Full networking - VNets, NSGs, load balancers
When to Use VMs
| Use Case | Why VMs |
|---|---|
| Custom software | Need specific versions or configurations |
| Legacy apps | Can't containerize |
| GPU workloads | Machine learning, rendering |
| Self-managed databases | PostgreSQL, MongoDB clusters |
| Background jobs | Long-running processes |
VMs vs Other Options
| Feature | VMs | App Service | Container Apps |
|---|---|---|---|
| Control | Full | Limited | Limited |
| Maintenance | You | Microsoft | Microsoft |
| Scaling | Manual/VMSS | Automatic | Automatic |
| Cost | While running | While deployed | While running |
Step 1: Choose a VM Size
Common VM Sizes
| Series | Use Case | Example |
|---|---|---|
| B | Burstable, dev/test | B1s, B2s |
| D | General purpose | D2s_v5, D4s_v5 |
| E | Memory optimized | E2s_v5, E4s_v5 |
| F | Compute optimized | F2s_v2, F4s_v2 |
| N | GPU | NC6, NV6 |
Size Examples
| Size | vCPUs | Memory | Cost/month* |
|---|---|---|---|
Standard_B1s | 1 | 1 GB | ~$8 |
Standard_B2s | 2 | 4 GB | ~$30 |
Standard_D2s_v5 | 2 | 8 GB | ~$70 |
Standard_D4s_v5 | 4 | 16 GB | ~$140 |
*Approximate East US pricing
List Available Sizes
bashaz vm list-sizes --location eastus --output table
Step 2: Create a Virtual Machine
Basic Linux VM
bash# Create resource group az group create --name myapp-rg --location eastus # Create VM az vm create \ --resource-group myapp-rg \ --name myapp-vm \ --image Ubuntu2204 \ --size Standard_B2s \ --admin-username azureuser \ --generate-ssh-keys \ --public-ip-sku Standard
VM with Custom Options
bashaz vm create \ --resource-group myapp-rg \ --name myapp-vm \ --image Ubuntu2204 \ --size Standard_D2s_v5 \ --admin-username azureuser \ --ssh-key-values ~/.ssh/id_rsa.pub \ --os-disk-size-gb 64 \ --storage-sku Premium_LRS \ --public-ip-sku Standard \ --nsg myapp-nsg \ --vnet-name myapp-vnet \ --subnet default \ --tags Environment=Production App=MyApp
Available Images
bash# List popular images az vm image list --output table # Search for specific images az vm image list --offer Ubuntu --all --output table az vm image list --offer Debian --all --output table az vm image list --offer CentOS --all --output table # Common image URNs: # Canonical:0001-com-ubuntu-server-jammy:22_04-lts:latest # Debian:debian-11:11:latest # RedHat:RHEL:8-lvm:latest
Step 3: Configure Network Security
Create Network Security Group
bash# Create NSG az network nsg create \ --resource-group myapp-rg \ --name myapp-nsg # Allow SSH az network nsg rule create \ --resource-group myapp-rg \ --nsg-name myapp-nsg \ --name AllowSSH \ --priority 100 \ --access Allow \ --protocol Tcp \ --direction Inbound \ --source-address-prefixes '*' \ --destination-port-ranges 22 # Allow HTTP az network nsg rule create \ --resource-group myapp-rg \ --nsg-name myapp-nsg \ --name AllowHTTP \ --priority 110 \ --access Allow \ --protocol Tcp \ --direction Inbound \ --source-address-prefixes '*' \ --destination-port-ranges 80 # Allow HTTPS az network nsg rule create \ --resource-group myapp-rg \ --nsg-name myapp-nsg \ --name AllowHTTPS \ --priority 120 \ --access Allow \ --protocol Tcp \ --direction Inbound \ --source-address-prefixes '*' \ --destination-port-ranges 443
List NSG Rules
bashaz network nsg rule list \ --resource-group myapp-rg \ --nsg-name myapp-nsg \ --output table
Step 4: Connect to Your VM
Get Public IP
bashaz vm show \ --resource-group myapp-rg \ --name myapp-vm \ --show-details \ --query publicIps \ --output tsv
SSH Connection
bash# Connect via SSH ssh azureuser@<public-ip> # Or using Azure CLI az ssh vm \ --resource-group myapp-rg \ --name myapp-vm
File Transfer
bash# Copy file to VM scp local-file.txt azureuser@<public-ip>:~/ # Copy file from VM scp azureuser@<public-ip>:~/remote-file.txt ./ # Copy directory scp -r ./my-app azureuser@<public-ip>:~/
Step 5: Deploy Your Application
Node.js Application Setup
SSH into your VM and run:
bash# Update system sudo apt update && sudo apt upgrade -y # Install Node.js curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs # Install nginx sudo apt install -y nginx # Clone your application cd /var/www sudo git clone https://github.com/your-user/your-app.git cd your-app sudo npm ci --production sudo npm run build # Create systemd service sudo tee /etc/systemd/system/myapp.service << 'EOF' [Unit] Description=My Node.js Application After=network.target [Service] Type=simple User=www-data WorkingDirectory=/var/www/your-app ExecStart=/usr/bin/node server.js Restart=on-failure Environment=NODE_ENV=production Environment=PORT=3000 [Install] WantedBy=multi-user.target EOF # Start service sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp # Configure nginx reverse proxy sudo tee /etc/nginx/sites-available/myapp << 'EOF' server { listen 80; server_name _; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_cache_bypass $http_upgrade; } } EOF sudo ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t && sudo systemctl restart nginx
Using Custom Script Extension
Automate setup during VM creation:
bash# Create startup script cat > startup.sh << 'EOF' #!/bin/bash apt-get update apt-get install -y nginx nodejs npm # ... rest of setup EOF # Create VM with script az vm create \ --resource-group myapp-rg \ --name myapp-vm \ --image Ubuntu2204 \ --size Standard_B2s \ --admin-username azureuser \ --generate-ssh-keys \ --custom-data startup.sh
Step 6: Manage Your VM
View VM Details
bash# Show VM info az vm show \ --resource-group myapp-rg \ --name myapp-vm \ --output table # Show with IP addresses az vm show \ --resource-group myapp-rg \ --name myapp-vm \ --show-details \ --output table
Start, Stop, Restart
bash# Stop VM (deallocate - no charges) az vm deallocate \ --resource-group myapp-rg \ --name myapp-vm # Start VM az vm start \ --resource-group myapp-rg \ --name myapp-vm # Restart VM az vm restart \ --resource-group myapp-rg \ --name myapp-vm # Delete VM az vm delete \ --resource-group myapp-rg \ --name myapp-vm \ --yes
Resize VM
bash# List available sizes for resize az vm list-vm-resize-options \ --resource-group myapp-rg \ --name myapp-vm \ --output table # Resize (may require deallocation) az vm resize \ --resource-group myapp-rg \ --name myapp-vm \ --size Standard_D4s_v5
Step 7: Managed Disks
Add Data Disk
bash# Create and attach disk az vm disk attach \ --resource-group myapp-rg \ --vm-name myapp-vm \ --name myapp-datadisk \ --size-gb 128 \ --sku Premium_LRS \ --new # SSH and format the disk ssh azureuser@<public-ip> sudo fdisk -l # Find the new disk (e.g., /dev/sdc) sudo mkfs.ext4 /dev/sdc sudo mkdir /mnt/data sudo mount /dev/sdc /mnt/data # Add to fstab for persistence echo '/dev/sdc /mnt/data ext4 defaults,nofail 0 2' | sudo tee -a /etc/fstab
Disk Types
| Type | Use Case | IOPS |
|---|---|---|
Standard_LRS | Dev/test, backups | Up to 500 |
StandardSSD_LRS | Web servers, light workloads | Up to 6000 |
Premium_LRS | Production databases | Up to 20000 |
UltraSSD_LRS | Highest performance | Up to 160000 |
Step 8: Static IP Address
bash# Create static public IP az network public-ip create \ --resource-group myapp-rg \ --name myapp-static-ip \ --sku Standard \ --allocation-method Static # Get IP address az network public-ip show \ --resource-group myapp-rg \ --name myapp-static-ip \ --query ipAddress \ --output tsv # Associate with VM's NIC az network nic ip-config update \ --resource-group myapp-rg \ --nic-name myapp-vmVMNic \ --name ipconfig1 \ --public-ip-address myapp-static-ip
Step 9: Availability Sets and Scale Sets
Availability Set
High availability within a datacenter:
bash# Create availability set az vm availability-set create \ --resource-group myapp-rg \ --name myapp-avset \ --platform-fault-domain-count 2 \ --platform-update-domain-count 5 # Create VM in availability set az vm create \ --resource-group myapp-rg \ --name myapp-vm-1 \ --availability-set myapp-avset \ --image Ubuntu2204 \ --size Standard_B2s \ --admin-username azureuser \ --generate-ssh-keys
Virtual Machine Scale Set
Auto-scaling group of VMs:
bash# Create scale set az vmss create \ --resource-group myapp-rg \ --name myapp-vmss \ --image Ubuntu2204 \ --vm-sku Standard_B2s \ --instance-count 2 \ --admin-username azureuser \ --generate-ssh-keys \ --upgrade-policy-mode automatic \ --custom-data startup.sh # Enable autoscaling az monitor autoscale create \ --resource-group myapp-rg \ --name myapp-autoscale \ --resource myapp-vmss \ --resource-type Microsoft.Compute/virtualMachineScaleSets \ --min-count 2 \ --max-count 10 \ --count 2 # Add scale rule az monitor autoscale rule create \ --resource-group myapp-rg \ --autoscale-name myapp-autoscale \ --condition "Percentage CPU > 70 avg 5m" \ --scale out 2 az monitor autoscale rule create \ --resource-group myapp-rg \ --autoscale-name myapp-autoscale \ --condition "Percentage CPU < 30 avg 5m" \ --scale in 1
Step 10: Monitoring
Enable Monitoring
bash# Install Azure Monitor agent az vm extension set \ --resource-group myapp-rg \ --vm-name myapp-vm \ --name AzureMonitorLinuxAgent \ --publisher Microsoft.Azure.Monitor \ --version 1.0 # View metrics az monitor metrics list \ --resource /subscriptions/.../resourceGroups/myapp-rg/providers/Microsoft.Compute/virtualMachines/myapp-vm \ --metric "Percentage CPU" \ --output table
Boot Diagnostics
bash# Enable boot diagnostics az vm boot-diagnostics enable \ --resource-group myapp-rg \ --name myapp-vm # Get serial console log az vm boot-diagnostics get-boot-log \ --resource-group myapp-rg \ --name myapp-vm
Cost Optimization
Spot VMs
Up to 90% cheaper, but can be evicted:
bashaz vm create \ --resource-group myapp-rg \ --name myapp-spot-vm \ --image Ubuntu2204 \ --size Standard_D2s_v5 \ --priority Spot \ --eviction-policy Deallocate \ --max-price 0.05 \ --admin-username azureuser \ --generate-ssh-keys
Reserved Instances
1-3 year commitment for up to 72% savings:
- Go to Reservations in Azure Portal
- Select VM reservation
- Choose term and VM series
- Discounts apply automatically
Auto-Shutdown
bashaz vm auto-shutdown \ --resource-group myapp-rg \ --name myapp-vm \ --time 1900 \ --timezone "Pacific Standard Time"
Troubleshooting
Can't SSH to VM
bash# Check NSG rules az network nsg rule list \ --resource-group myapp-rg \ --nsg-name myapp-nsg \ --output table # Check VM is running az vm show \ --resource-group myapp-rg \ --name myapp-vm \ --query powerState \ --output tsv # Reset SSH configuration az vm user reset-ssh \ --resource-group myapp-rg \ --name myapp-vm
VM Won't Start
bash# Check boot diagnostics az vm boot-diagnostics get-boot-log \ --resource-group myapp-rg \ --name myapp-vm # Redeploy VM (moves to new host) az vm redeploy \ --resource-group myapp-rg \ --name myapp-vm
Reset Password
bashaz vm user update \ --resource-group myapp-rg \ --name myapp-vm \ --username azureuser \ --password NewSecurePassword123!
Note
Checkpoint: Create a VM, SSH into it, deploy your application, and configure firewall rules to make it accessible.