Azure Immutable Backups: Building Ransomware-Proof Recovery with WORM Vaults and Blob Immutability

The attack pattern is well-documented now. Ransomware operators do not just encrypt production data — they target backup repositories first. By the time you realize you are under attack, your backups are already compromised. The 2025-2026 wave of attacks specifically targets Azure Backup vaults, VMware backup proxies, and Veeam repositories before deploying the encryption payload.

Azure Backup Immutable Vaults use WORM (Write Once, Read Many) storage to make recovery points irrevocable. Even the Azure subscription owner cannot delete them until the retention period expires. Combined with soft-delete, MFA-guarded delete protection, and geo-redundant immutable copies, this is now the minimum baseline for enterprise disaster recovery.

SOC 2 Type II auditors in Malaysia are increasingly requiring immutable backup evidence. If you are serving global clients, this is not optional anymore.

How Immutable Vaults Work

An Immutable Vault enforces WORM policy at the vault level. Once enabled:

  1. No recovery point can be deleted or modified until its retention period expires
  2. No backup admin, subscription owner, or Azure Support can override the retention
  3. Soft-delete is mandatory with a minimum 14-day retention period
  4. MFA is required for any delete operation (even after retention expires)

This is not "soft immutability" — it is enforced by Azure's storage layer. The WORM policy is cryptographically verified and cannot be bypassed through the portal, CLI, API, or PowerShell.

┌─────────────────────────────────────────────┐
│              Azure Backup Vault              │
│  ┌─────────────────────────────────────────┐│
│  │        WORM Policy (Immutable)          ││
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐││
│  │  │ RP Day 1 │ │ RP Day 2 │ │ RP Day 3 │││
│  │  │ (locked) │ │ (locked) │ │ (locked) │││
│  │  └──────────┘ └──────────┘ └──────────┘││
│  │  Cannot delete until retention expires  ││
│  └─────────────────────────────────────────┘│
│  ┌─────────────────────────────────────────┐│
│  │  Soft-Delete: 14-day minimum            ││
│  │  MFA Guard: Required for any delete     ││
│  └─────────────────────────────────────────┘│
└─────────────────────────────────────────────┘

Azure offers two immutability policy types:

Time-Based Retention (TBR): Recovery points are locked for a fixed number of days. After the retention period expires, they become eligible for deletion (subject to soft-delete and MFA).

# Create an immutable vault with 30-day time-based retention
az backup vault create \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --location malaysiaeast \
  --immutability "Enabled"

# Set backup policy with 30-day retention
az backup policy set \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --name policy-immutable-30d \
  --policy '{
    "backupManagementType": "AzureIaasVM",
    "schedulePolicy": {
      "schedulePolicyType": "SimpleSchedulePolicy",
      "scheduleRunFrequency": "Daily",
      "scheduleRunTimes": ["2026-06-04T02:00:00+08:00"]
    },
    "retentionPolicy": {
      "retentionPolicyType": "LongTermRetentionPolicy",
      "dailySchedule": {
        "retentionTimes": ["2026-06-04T02:00:00+08:00"],
        "retentionDuration": {"count": 30, "durationType": "Days"}
      }
    }
  }'

Legal-Hold: Recovery points are locked indefinitely until an authorized user removes the hold. Used for compliance scenarios where retention duration is不确定 (regulatory investigations, litigation holds).

# Apply a legal hold tag
az backup item set-policy \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --policy-name policy-immutable-30d \
  --retention-override 365 \
  --legal-hold "investigation-2026"

My recommendation: Use Time-Based Retention for standard backup policies (30-90 days daily, 12 months monthly, 3-5 years yearly). Use Legal-Hold only for specific compliance scenarios — it complicates lifecycle management.

Step-by-Step Implementation

Step 1: Create the Immutable Vault

# Create a Recovery Services vault with immutability enabled
az backup vault create \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --location malaysiaeast

# Enable immutability (one-way operation — cannot be disabled)
az backup vault update \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --immutability "Enabled"

⚠️ Warning: Enabling immutability is a one-way operation. You cannot disable it. Plan your retention policy before enabling.

Step 2: Configure Backup Policy

# Create a comprehensive backup policy
cat > policy.json << 'EOF'
{
  "backupManagementType": "AzureIaasVM",
  "schedulePolicy": {
    "schedulePolicyType": "SimpleSchedulePolicy",
    "scheduleRunFrequency": "Daily",
    "scheduleRunTimes": ["2026-06-04T02:00:00+08:00"]
  },
  "retentionPolicy": {
    "retentionPolicyType": "LongTermRetentionPolicy",
    "dailySchedule": {
      "retentionTimes": ["2026-06-04T02:00:00+08:00"],
      "retentionDuration": {"count": 30, "durationType": "Days"}
    },
    "weeklySchedule": {
      "daysOfTheWeek": ["Sunday"],
      "retentionTimes": ["2026-06-04T02:00:00+08:00"],
      "retentionDuration": {"count": 12, "durationType": "Weeks"}
    },
    "monthlySchedule": {
      "retentionScheduleFormatType": "Weekly",
      "retentionScheduleWeekly": {
        "daysOfTheWeek": ["Sunday"],
        "weeksOfTheMonth": ["First"]
      },
      "retentionTimes": ["2026-06-04T02:00:00+08:00"],
      "retentionDuration": {"count": 12, "durationType": "Months"}
    },
    "yearlySchedule": {
      "retentionScheduleFormatType": "Weekly",
      "retentionScheduleWeekly": {
        "daysOfTheWeek": ["Sunday"],
        "weeksOfTheMonth": ["First"]
      },
      "monthsOfYear": ["January"],
      "retentionTimes": ["2026-06-04T02:00:00+08:00"],
      "retentionDuration": {"count": 5, "durationType": "Years"}
    }
  }
}
EOF

az backup policy create \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --name policy-immutable-30d \
  --policy @policy.json

Step 3: Enable Soft-Delete with MFA Guard

# Configure soft-delete (minimum 14 days, recommended 14-30)
az backup vault update \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --soft-delete "Enabled"

# Note: Soft-delete is automatically enabled when immutability is enabled
# with a minimum of 14 days. You cannot set it lower.

Step 4: Enable Cross-Region Restore

# For geo-redundant recovery points
az backup vault update \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --cross-region-restore "Enabled"

Step 5: Test Recovery Under Simulated Ransomware

This is the critical step most organizations skip. Test your recovery process:

# 1. Trigger an on-demand backup
az backup backup-now \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --retain-until "2026-07-04"

# 2. Verify the recovery point exists and is immutable
az backup recovery-point list \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --output table

# 3. Attempt to DELETE the recovery point (should fail)
az backup recovery-point delete \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --name "<recovery-point-id>" \
  --yes
# Expected: Error — cannot delete immutable recovery point

# 4. Restore to a test VNet
az backup restore restore-disks \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --recovery-point-id "<recovery-point-id>" \
  --storage-account-type Standard_LRS \
  --target-resource-group rg-test-restore \
  --target-vnet vnet-test \
  --target-subnet subnet-test

Cost Implications

WORM storage costs are not significantly higher than standard backup storage, but retention duration is the cost driver:

Retention Period Storage Cost (per 100GB/month) Notes
30 days daily ~$2-4 Standard LRS
90 days daily ~$6-12 Standard LRS
12 months monthly ~$2-4 Only monthly snapshots
5 years yearly ~$1-2 Only yearly snapshots

For a typical 1TB VM with daily 30-day retention, expect approximately $20-40/month for immutable backup storage in Malaysia East.

The real cost savings come from not being ransomed. The average ransomware downtime for enterprises is 24 days. If your daily revenue is $50K, that is $1.2M in downtime costs — far exceeding any backup storage premium.

Audit Evidence for SOC 2

SOC 2 Type II auditors want to see:

  1. Immutable vault configuration — Screenshot or CLI output showing immutability is enabled
  2. Recovery point deletion attempts — Logs showing deletion was blocked by WORM policy
  3. Recovery test results — Documented evidence that recovery was successful
  4. Retention policy documentation — Written policy matching the technical configuration
# Generate audit evidence report
echo "=== Immutable Vault Status ===" > audit-report.txt
az backup vault show \
  --name vault-immutable-prod \
  --resource-group rg-backup \
  --query "{Immutability:properties.immutabilitySettings.state, SoftDelete:properties.softDeleteSettings.state}" \
  --output table >> audit-report.txt

echo "" >> audit-report.txt
echo "=== Recent Recovery Points ===" >> audit-report.txt
az backup recovery-point list \
  --vault-name vault-immutable-prod \
  --resource-group rg-backup \
  --container-name vm-backup \
  --item-name vm-webserver-01 \
  --query "[].{ID:name, Created:properties.instantRpDetails.sourceResourceType, RetainedUntil:properties.policyRetentionPolicy}" \
  --output table >> audit-report.txt

echo "" >> audit-report.txt
echo "=== Deletion Attempts (last 30 days) ===" >> audit-report.txt
az monitor activity-log list \
  --start-time "$(date -d '30 days ago' +%Y-%m-%dT%H:%M:%S)" \
  --query "[?contains(operationName.value, 'delete') && contains(resourceProviderValue, 'RecoveryServices')].{Time:eventTimestamp, Operation:operationName.value, Result:status.value}" \
  --output table >> audit-report.txt

Key Takeaways

  1. Immutable Vaults are a one-way operation. Once enabled, you cannot disable immutability. Plan your retention policy before enabling.
  2. Time-Based Retention for standard backups, Legal-Hold for compliance. Do not over-complicate lifecycle management with legal holds unless you have a specific regulatory requirement.
  3. Test recovery under simulated ransomware conditions. Verify that deletion attempts fail, and that restore operations succeed to an isolated network segment.
  4. SOC 2 Type II auditors increasingly require immutable backup evidence. Configure audit reporting before the audit, not during.
  5. The cost of immutability is trivial compared to ransomware downtime. At $20-40/month for a 1TB VM, the insurance value is overwhelming.

Resources