Comprehensive Guide: Secure Azure VM Backup Solution with Premium SSD RAID
This guide walks through setting up a hardened backup solution in Azure using VMs with Premium SSDs in RAID configuration, accessible only through a jumpbox, with mount scripts for both Linux and Windows servers.
1. Azure Infrastructure Design
Architecture Overview
Internet → Azure Firewall → Jumpbox VM → Backup Storage VMs with RAID
↘ Client VMs (Windows/Linux)Network Architecture
- Virtual Network: Primary network (10.0.0.0/16)
- Subnets:
- Jumpbox subnet (10.0.0.0/24)
- Storage subnet (10.0.1.0/24)
- Clients subnet (10.0.2.0/24)
- NSGs: Restrict traffic between subnets
- Azure Firewall: Control inbound/outbound traffic
2. Setting Up the Azure Environment
Create Resource Group
az group create --name BackupSolutionRG --location eastusCreate Virtual Network and Subnets
# Create VNet
az network vnet create \
--resource-group BackupSolutionRG \
--name BackupVNet \
--address-prefix 10.0.0.0/16 \
--subnet-name JumpboxSubnet \
--subnet-prefix 10.0.0.0/24
# Create Storage Subnet
az network vnet subnet create \
--resource-group BackupSolutionRG \
--vnet-name BackupVNet \
--name StorageSubnet \
--address-prefix 10.0.1.0/24
# Create Clients Subnet
az network vnet subnet create \
--resource-group BackupSolutionRG \
--vnet-name BackupVNet \
--name ClientsSubnet \
--address-prefix 10.0.2.0/24Configure Network Security Groups (NSGs)
# Create NSG for Jumpbox
az network nsg create \
--resource-group BackupSolutionRG \
--name JumpboxNSG
# Allow SSH to jumpbox from your IP only
az network nsg rule create \
--resource-group BackupSolutionRG \
--nsg-name JumpboxNSG \
--name AllowSSH \
--priority 100 \
--source-address-prefixes <YOUR-IP>/32 \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges 22 \
--access Allow \
--protocol Tcp \
--description "Allow SSH from admin IP"
# Create NSG for Storage subnet
az network nsg create \
--resource-group BackupSolutionRG \
--name StorageNSG
# Allow traffic only from jumpbox subnet to storage
az network nsg rule create \
--resource-group BackupSolutionRG \
--nsg-name StorageNSG \
--name AllowFromJumpbox \
--priority 100 \
--source-address-prefixes 10.0.0.0/24 \
--source-port-ranges '*' \
--destination-address-prefixes '*' \
--destination-port-ranges 22 3389 445 139 \
--access Allow \
--protocol '*' \
--description "Allow from jumpbox subnet"
# Associate NSGs with subnets
az network vnet subnet update \
--resource-group BackupSolutionRG \
--vnet-name BackupVNet \
--name JumpboxSubnet \
--network-security-group JumpboxNSG
az network vnet subnet update \
--resource-group BackupSolutionRG \
--vnet-name BackupVNet \
--name StorageSubnet \
--network-security-group StorageNSG3. Creating the Jumpbox VM
# Create public IP for jumpbox
az network public-ip create \
--resource-group BackupSolutionRG \
--name JumpboxPublicIP \
--allocation-method Static \
--sku Standard
# Create jumpbox VM
az vm create \
--resource-group BackupSolutionRG \
--name JumpboxVM \
--image UbuntuLTS \
--admin-username azureuser \
--generate-ssh-keys \
--vnet-name BackupVNet \
--subnet JumpboxSubnet \
--public-ip-address JumpboxPublicIP \
--nsg "" \
--size Standard_B2s4. Creating the Storage VM with Premium SSD RAID
# Create storage VM without public IP
az vm create \
--resource-group BackupSolutionRG \
--name StorageVM \
--image UbuntuLTS \
--admin-username azureuser \
--generate-ssh-keys \
--vnet-name BackupVNet \
--subnet StorageSubnet \
--public-ip-address "" \
--nsg "" \
--size Standard_D4s_v3
# Add 4 Premium SSDs (P30 = 1TB each)
for i in {0..3}
do
az vm disk attach \
--resource-group BackupSolutionRG \
--vm-name StorageVM \
--name StorageDisk$i \
--new \
--size-gb 1024 \
--sku Premium_LRS \
--caching ReadWrite
done5. Configuring RAID on the Storage VM
SSH to the Jumpbox first, then to the Storage VM:
# From your local machine to jumpbox
ssh azureuser@<jumpbox-public-ip>
# From jumpbox to storage VM
ssh azureuser@<storage-vm-private-ip>On the Storage VM, set up RAID 10:
# Install mdadm utility
sudo apt update
sudo apt install -y mdadm
# Check available disks
lsblk
# Create RAID array (assuming disks are sdc, sdd, sde, sdf)
sudo mdadm --create --verbose /dev/md0 --level=10 --raid-devices=4 /dev/sdc /dev/sdd /dev/sde /dev/sdf
# Verify RAID creation
sudo mdadm --detail /dev/md0
# Create filesystem
sudo mkfs.ext4 -F /dev/md0
# Create mount point
sudo mkdir -p /mnt/raid
# Mount the RAID array
sudo mount /dev/md0 /mnt/raid
# Set permissions
sudo chmod 777 /mnt/raid
# Configure automatic mounting at boot
echo '/dev/md0 /mnt/raid ext4 defaults,nofail,discard 0 0' | sudo tee -a /etc/fstab
# Save RAID configuration
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm/mdadm.conf
sudo update-initramfs -u6. Hardening the VMs
Common Security Measures for Both VMs
# Update system
sudo apt update && sudo apt upgrade -y
# Install security packages
sudo apt install -y unattended-upgrades fail2ban ufw
# Configure automatic updates
sudo dpkg-reconfigure -plow unattended-upgrades
# Configure fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Setup UFW (uncomplicated firewall)
# On jumpbox
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enable
# On storage VM
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 10.0.0.0/24 to any port 22
sudo ufw allow from 10.0.2.0/24 to any port 445
sudo ufw allow from 10.0.2.0/24 to any port 139
sudo ufw enable
# Disable root SSH access
sudo sed -i 's/PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sudo systemctl restart sshd
# Set stronger SSH configuration
sudo bash -c 'cat > /etc/ssh/sshd_config.d/hardening.conf << EOF
Protocol 2
PermitRootLogin no
MaxAuthTries 3
MaxSessions 2
PasswordAuthentication no
PermitEmptyPasswords no
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding no
EOF'
sudo systemctl restart sshdAdditional Storage VM Hardening
# Restrict RAID mount permissions as needed
sudo chmod 750 /mnt/raid
sudo chown azureuser:azureuser /mnt/raid
# Setup access control for shared folders
sudo apt install -y acl
sudo setfacl -m d:u::rwx,d:g::r-x,d:o:--- /mnt/raid7. Configuring File Sharing Services
Configure Samba for Windows Clients
# Install Samba
sudo apt install -y samba
# Configure Samba
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
sudo bash -c 'cat > /etc/samba/smb.conf << EOF
[global]
workgroup = WORKGROUP
server string = Backup Server
security = user
map to guest = never
encrypt passwords = yes
smb encrypt = required
# Limit network interfaces
interfaces = lo 10.0.1.0/24
bind interfaces only = yes
# Logging
log file = /var/log/samba/log.%m
max log size = 1000
logging = file
# Performance tuning
socket options = TCP_NODELAY IPTOS_LOWDELAY
read raw = yes
write raw = yes
strict locking = no
[BackupShare]
path = /mnt/raid/backup
valid users = @backup
writable = yes
browsable = yes
create mask = 0660
directory mask = 0770
EOF'
# Create backup directory and group
sudo mkdir -p /mnt/raid/backup
sudo groupadd backup
sudo useradd -m backupuser -G backup
sudo smbpasswd -a backupuser
sudo chown -R backupuser:backup /mnt/raid/backup
sudo chmod -R 770 /mnt/raid/backup
# Restart Samba
sudo systemctl restart smbd nmbdConfigure NFS for Linux Clients
# Install NFS server
sudo apt install -y nfs-kernel-server
# Configure NFS exports
echo '/mnt/raid/backup 10.0.2.0/24(rw,sync,no_subtree_check,no_root_squash)' | sudo tee -a /etc/exports
# Create directories
sudo mkdir -p /mnt/raid/backup/linux
sudo chown -R nobody:nogroup /mnt/raid/backup/linux
sudo chmod -R 775 /mnt/raid/backup/linux
# Restart NFS server
sudo exportfs -a
sudo systemctl restart nfs-kernel-server8. Client Mount Scripts
Windows Client Mount Script (PowerShell)
Create a PowerShell script (mount-backup.ps1):
# Save this as mount-backup.ps1
$BackupServer = "10.0.1.4" # Storage VM's private IP
$Username = "backupuser"
$Password = ConvertTo-SecureString "YourStrongPasswordHere" -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential($Username, $Password)
# Check if already connected
$Connected = Get-SmbConnection -ServerName $BackupServer -ErrorAction SilentlyContinue
if (-not $Connected) {
# Establish SMB connection
New-SmbMapping -LocalPath "Z:" -RemotePath "\\$BackupServer\BackupShare" -Credential $Credential -Persistent $true
Write-Host "Backup share mounted successfully as Z: drive"
} else {
Write-Host "Backup share is already connected"
}
# Schedule backup (example using robocopy)
$SourceDir = "C:\ImportantData"
$BackupDir = "Z:\$env:COMPUTERNAME"
# Create computer-specific folder if it doesn't exist
if (!(Test-Path $BackupDir)) {
New-Item -Path $BackupDir -ItemType Directory
}
# Run robocopy with logging
robocopy $SourceDir $BackupDir /MIR /Z /W:5 /R:2 /MT:8 /LOG:"C:\Logs\backup_$(Get-Date -Format 'yyyyMMdd').log"Linux Client Mount Script (Bash)
Create a bash script (mount-backup.sh):
#!/bin/bash
# Save this as mount-backup.sh
BACKUP_SERVER="10.0.1.4" # Storage VM's private IP
MOUNT_POINT="/mnt/backup"
BACKUP_DIR="/mnt/backup/$HOSTNAME"
SOURCE_DIR="/data"
# Ensure mount point exists
sudo mkdir -p $MOUNT_POINT
# Check if already mounted
if ! mount | grep -q "$MOUNT_POINT"; then
echo "Mounting backup share..."
sudo mount -t nfs $BACKUP_SERVER:/mnt/raid/backup/linux $MOUNT_POINT
else
echo "Backup share is already mounted"
fi
# Create host-specific directory
mkdir -p $BACKUP_DIR
# Run backup with rsync
rsync -avz --delete --stats --log-file=/var/log/backup-$(date +%Y%m%d).log $SOURCE_DIR/ $BACKUP_DIR/
echo "Backup completed at $(date)"Make the script executable:
chmod +x mount-backup.sh9. Setting Up Scheduled Backups
Windows Task Scheduler
- Open Task Scheduler on the Windows client
- Create a new task:
- Name: "Backup to Azure Storage"
- Trigger: Daily at your preferred time
- Action: Start a program
- Program/script:
powershell.exe - Arguments:
-ExecutionPolicy Bypass -File "C:\Scripts\mount-backup.ps1"
Linux Cron Job
# Edit crontab
crontab -e
# Add this line to run daily at 1 AM
0 1 * * * /path/to/mount-backup.sh >> /var/log/backup-cron.log 2>&110. Monitoring and Maintenance
Setup Disk Health Monitoring
# Install smartmontools
sudo apt install -y smartmontools
# Check disk health
sudo smartctl -a /dev/sdc
sudo smartctl -a /dev/sdd
sudo smartctl -a /dev/sde
sudo smartctl -a /dev/sdf
# Setup monitoring service
sudo bash -c 'cat > /etc/smartd.conf << EOF
/dev/sdc -a -o on -S on -s (S/../.././02|L/../../6/03) -m root
/dev/sdd -a -o on -S on -s (S/../.././02|L/../../6/03) -m root
/dev/sde -a -o on -S on -s (S/../.././02|L/../../6/03) -m root
/dev/sdf -a -o on -S on -s (S/../.././02|L/../../6/03) -m root
EOF'
sudo systemctl enable smartd
sudo systemctl start smartdRAID Monitoring Script
Create /usr/local/bin/raid-check.sh:
#!/bin/bash
# RAID monitoring script
ADMIN_EMAIL="admin@example.com"
RAID_DEVICE="/dev/md0"
# Check RAID status
raid_status=$(mdadm --detail $RAID_DEVICE | grep "State :" | awk '{print $3}')
if [[ "$raid_status" != "clean" ]] && [[ "$raid_status" != "active" ]]; then
echo "RAID status is $raid_status on $(hostname) at $(date)" | mail -s "RAID ALERT: $HOSTNAME" $ADMIN_EMAIL
fi
# Check disk space
disk_usage=$(df -h $RAID_DEVICE | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$disk_usage" -gt 90 ]; then
echo "Disk usage is at ${disk_usage}% on $(hostname) at $(date)" | mail -s "DISK SPACE ALERT: $HOSTNAME" $ADMIN_EMAIL
fiMake it executable and add to crontab:
sudo chmod +x /usr/local/bin/raid-check.sh
# Add to root's crontab
sudo crontab -e
# Add this line to run every hour
0 * * * * /usr/local/bin/raid-check.sh11. Setting Up Backup Rotation Policy
Create /usr/local/bin/backup-rotate.sh:
#!/bin/bash
# Backup rotation script
BACKUP_DIR="/mnt/raid/backup"
DAILY_KEEP=7
WEEKLY_KEEP=4
MONTHLY_KEEP=6
# Current date variables
DAY=$(date +%d)
WEEKDAY=$(date +%u)
MONTH=$(date +%m)
# Create timestamp directory
TIMESTAMP=$(date +%Y%m%d-%H%M)
mkdir -p "$BACKUP_DIR/snapshots/$TIMESTAMP"
# Use hardlinks for efficiency
cp -al "$BACKUP_DIR/latest/" "$BACKUP_DIR/snapshots/$TIMESTAMP/"
# Update 'latest' symlink
rm -f "$BACKUP_DIR/latest"
ln -s "snapshots/$TIMESTAMP" "$BACKUP_DIR/latest"
# Create daily, weekly, monthly snapshots
if [ "$WEEKDAY" = "7" ]; then # Sunday
# Create weekly snapshot
cp -al "$BACKUP_DIR/snapshots/$TIMESTAMP/" "$BACKUP_DIR/weekly/week-$(date +%U)-$YEAR/"
fi
if [ "$DAY" = "01" ]; then
# Create monthly snapshot
cp -al "$BACKUP_DIR/snapshots/$TIMESTAMP/" "$BACKUP_DIR/monthly/month-$MONTH-$YEAR/"
fi
# Cleanup old snapshots
# ... (implement rotation logic based on DAILY_KEEP, WEEKLY_KEEP, MONTHLY_KEEP)
# This would search for and remove old directories based on your retention policyMake it executable and add to crontab:
sudo chmod +x /usr/local/bin/backup-rotate.sh
sudo crontab -e
# Add this line to run daily at 3 AM
0 3 * * * /usr/local/bin/backup-rotate.sh12. Disaster Recovery Procedures
Document Recovery Process
Create /mnt/raid/README.md:
# Backup System Disaster Recovery Guide
## RAID Failure Scenarios
### If a single disk fails:
1. Identify the failed disk:mdadm --detail /dev/md0
2. Mark the disk as failed:mdadm /dev/md0 --fail /dev/sdX
3. Remove the failed disk:mdadm /dev/md0 --remove /dev/sdX
4. Add a new disk:mdadm /dev/md0 --add /dev/sdY
5. Monitor rebuild progress:cat /proc/mdstat
### Complete RAID Failure:
1. Create new RAID array
2. Restore from offline backup (Azure Backup/Azure Storage)
## Network Access Failure:
1. Check NSG rules in Azure Portal
2. Verify jumpbox connectivity
3. Test internal network connectivity13. Additional Azure Enhancements
Enable Azure Backup for VM-level Protection
# Create Recovery Services Vault
az backup vault create \
--resource-group BackupSolutionRG \
--name BackupVault \
--location eastus
# Enable VM backup
az backup protection enable-for-vm \
--resource-group BackupSolutionRG \
--vault-name BackupVault \
--vm StorageVM \
--policy-name DefaultPolicyEnable Azure Disk Encryption
# Create Key Vault
az keyvault create \
--name BackupKeyVault \
--resource-group BackupSolutionRG \
--location eastus \
--enabled-for-disk-encryption true
# Enable disk encryption on StorageVM
az vm encryption enable \
--resource-group BackupSolutionRG \
--name StorageVM \
--disk-encryption-keyvault BackupKeyVault
# Monitor encryption progress
az vm encryption show --resource-group BackupSolutionRG --name StorageVMConclusion
This guide has walked through setting up a secure Azure VM-based backup solution with:
- Proper network segmentation and a jumpbox for secure access
- Premium SSD RAID configuration for performance and redundancy
- Hardened VMs with tight security controls
- File sharing services for both Windows and Linux clients
- Mount scripts and scheduled backup jobs
- Monitoring, maintenance, and disaster recovery procedures
This solution provides a robust backup system with control over your data while leveraging Azure's infrastructure reliability. Regular testing and updates are essential to maintain security and reliability over time.