Workload Backup (SQL / SAP HANA)
Overview
Azure VM Workload Backup protects database workloads running inside a VM — primarily SQL Server in Azure VM and SAP HANA in Azure VM. It is distinct from VM-level backup (VM Backup) in that it uses application-aware backup operations (Full / Differential / Incremental / Log) instead of crash-consistent disk snapshots. A single workload policy bundles multiple protection policies (e.g. a daily Full plus an hourly Log) under one logical schedule.
The backup_policy_vm_workload module declares these policies; protected-item registration (attaching a specific database to a policy) is not yet implemented in this repository — that step is done out-of-band in the Azure portal or via the workload extension once the policy exists.
Module Structure
| Module | Azure Resource | Purpose |
|---|---|---|
backup_policy_vm_workload |
azurerm_backup_policy_vm_workload |
Workload-aware backup policy (SQL / SAP HANA), nested under a Recovery Services Vault |
The module reads directly from the recovery_services_vault root variable — there is no separate top-level variable for workload policies.
Architecture
- Recovery Services Vault owns the policy — see Backup Overview.
- Workload policies are declared inside a vault under
backup_policy_vm_workload = { ... }. Each entry produces oneazurerm_backup_policy_vm_workloadresource. - Protection policies are nested under each workload policy — one per backup type (
Full,Differential,Incremental,Log). Each carries its own schedule and retention. - Composite key
<vault_key>.<policy_key>identifies a workload policy across vaults (same convention as standard VM backup policies — see Composite Keys).
Usage
1. Declare a Workload Policy Under a Vault
recovery_services_vault = {
epic = {
resource_group = "recoveryvault"
storage_mode_type = "GeoRedundant"
backup_policy_vm_workload = {
sqldaily = {
workload_type = "SQLDataBase"
settings = {
compression_enabled = true
}
protection_policies = {
full = {
policy_type = "Full"
backup = {
frequency = "Daily"
time = "20:30"
}
retention_daily = {
count = 30
}
retention_weekly = {
count = 8
weekdays = ["Sunday"]
}
}
log = {
policy_type = "Log"
backup = {
frequency_in_minutes = 60
}
simple_retention = {
count = 14
}
}
}
}
}
}
}
2. Accept the Built-in Default
If protection_policies is omitted, the variable default kicks in — a Full daily at 20:30 with 14-day / 4-week retention, plus a Log every 60 minutes with 7-day retention. This is intended as a sane starting point for SQL workloads:
backup_policy_vm_workload = {
daily = {} # uses the default protection_policies
explicitName = {
name = "bpol-sql-customname-dev"
}
}
Override the default by declaring protection_policies with the specific tiers you want — see step 1 for a full example.
3. Register Databases Against the Policy
Database-level registration (azurerm_backup_protected_vm_workload_sql_database) is not managed here. After the policy exists, register individual SQL databases via:
- Azure Portal → Recovery Services Vault → Backup items → SQL in Azure VM
az backup protection enable-for-azurewl --workload-type MSSQL ...
Once a database is registered, future runs of terraform plan will not see or churn the protection — it lives outside the state managed here.
Variable Reference
backup_policy_vm_workload (nested under recovery_services_vault.<vault_key>)
| Field | Type | Description | Default |
|---|---|---|---|
name |
string | Override the resource name | Prefix + key + suffix |
workload_type |
string | "SQLDataBase" or "SAPHanaDatabase" |
"SQLDataBase" |
settings |
object | Policy-wide settings (see below) | {} |
protection_policies |
map(object) | One entry per backup type (see below) | A sensible full + log default — see Accept the Built-in Default |
settings
| Field | Type | Description | Default |
|---|---|---|---|
time_zone |
string | Timezone for backup schedule | Root var.timezone |
compression_enabled |
bool | Enable native SQL compression on backup | false |
protection_policies.<key>
Each entry describes one backup tier. The policy_type determines which retention block is allowed.
| Field | Type | Description | Default |
|---|---|---|---|
policy_type |
string | "Full", "Differential", "Incremental", or "Log" |
Required |
backup |
object | Schedule (see below) | Required |
retention_daily |
object | { count } — used by Full (Daily) |
null |
retention_weekly |
object | { count, weekdays } — used by Full |
null |
retention_monthly |
object | Monthly retention (see below) | null |
retention_yearly |
object | Yearly retention (see below) | null |
simple_retention |
object | { count } — used by Log, Differential, Incremental |
null |
Full policies use the daily/weekly/monthly/yearly retention blocks. Log, Differential, and Incremental use simple_retention (a single flat count of days). Mixing them on the wrong policy_type will fail at apply.
backup (schedule)
| Field | Type | Description | Default |
|---|---|---|---|
frequency |
string | "Daily" or "Weekly" — used by Full / Differential / Incremental |
null |
frequency_in_minutes |
number | Minutes between backups — used by Log (typical: 15, 30, 60, 120, 240) |
null |
time |
string | "HH:MM" start time for daily/weekly |
null |
weekdays |
list(string) | Days for Weekly frequency |
null |
Set frequency or frequency_in_minutes, never both. Log policies must use frequency_in_minutes; everything else uses frequency.
retention_monthly / retention_yearly
The workload module uses a format_type switch instead of the mutually-exclusive forms used by backup_policy_vm.
| Field | Type | Description | Default |
|---|---|---|---|
count |
number | Periods to retain | Required |
format_type |
string | "Daily" (absolute, use monthdays) or "Weekly" (relative, use weekdays + weeks) |
Required |
monthdays |
list(number) | Absolute day-of-month — format_type = "Daily" only |
null |
weekdays |
list(string) | Weekday names — format_type = "Weekly" only |
null |
weeks |
list(string) | "First"–"Fourth", "Last" — format_type = "Weekly" only |
null |
months |
list(string) | retention_yearly only — which months to retain |
Required for yearly |
Naming Convention
Workload policies use the backup_policy_vm_workload prefix/suffix slot:
name_prefixes = {
backup_policy_vm_workload = "prod-"
}
name_suffixes = {
backup_policy_vm_workload = "-eastus2-bpol-sql"
}
Policy key sqldaily → prod-sqldaily-eastus2-bpol-sql.
Set name on the policy entry to bypass prefix/suffix composition entirely.
Backup policy names allow letters, numbers, and hyphens only. Periods are rejected by the Backup API even though the error message is misleading — keep them out of both the prefix/suffix and the policy key.
Limitations
- No protected-item resource: this repository does not yet manage
azurerm_backup_protected_vm_workload_sql_database(or the SAP HANA equivalent). Database registration is a manual step. - No
azurerm_backup_workload_containerwiring either — Azure auto-discovers SQL/HANA workloads inside a registered VM once you enable the workload extension via the portal/CLI. - Workload policies are not interchangeable with VM policies: do not set
backup_policy = "epic.sqldaily"on awindows_vmsentry expecting workload protection — thebackup_protected_vmmodule only resolves againstmod_backup_policy_vm, notmod_backup_policy_vm_workload, and will fail at plan time with a key-not-found error.
Related
- Backup Overview — Recovery Services Vault, diagnostic settings
- VM Backup — VM-level (crash-consistent) backup, including disk exclusion