22# Copyright (c) Microsoft Corporation.
33# Licensed under the MIT License.
44#
5- # setup_azure_backup.sh — Automate Azure Backup setup for SAP HANA (HSR)
5+ # setup_azure_backup.sh — Automate Azure Backup setup for SAP HANA (HSR or standalone )
66#
77# Steps:
88# 1. Create a Recovery Services vault
9- # 2. Run the pre-registration script on both VMs via az vm run-command
10- # 3. Register both VMs (containers) to the vault
9+ # 2. Run the pre-registration script on VM(s) via az vm run-command
10+ # 3. Register VM(s) (containers) to the vault
1111# 4. Discover protectable HANA databases
1212# 5. Create a backup policy (full + log + differential)
1313# 6. Enable protection on discovered databases
1414#
15+ # Supports both HSR (two-VM) and standalone (single-VM) modes.
16+ # When --secondary-vm and --hsr-unique-id are omitted, runs in standalone mode.
17+ #
1518# Reference:
1619# https://learn.microsoft.com/en-us/azure/backup/quick-backup-hana-cli
1720# https://learn.microsoft.com/en-us/azure/backup/sap-hana-database-with-hana-system-replication-backup
@@ -23,6 +26,7 @@ set -euo pipefail
2326# ──────────────────────────────────────────────
2427SUBSCRIPTION=" ${SUBSCRIPTION:- } "
2528RESOURCE_GROUP=" ${RESOURCE_GROUP:- } "
29+ VM_RESOURCE_GROUP=" ${VM_RESOURCE_GROUP:- } " # RG containing the VMs (defaults to RESOURCE_GROUP)
2630LOCATION=" ${LOCATION:- } "
2731VAULT_NAME=" ${VAULT_NAME:- } "
2832PRIMARY_VM=" ${PRIMARY_VM:- } "
@@ -34,6 +38,7 @@ HSR_UNIQUE_ID="${HSR_UNIQUE_ID:-}"
3438POLICY_NAME=" ${POLICY_NAME:- sap-hana-backup-policy} "
3539STORAGE_REDUNDANCY=" ${STORAGE_REDUNDANCY:- GeoRedundant} "
3640DATABASE_NAMES=" ${DATABASE_NAMES:- } " # comma-separated, e.g. "SYSTEMDB,DB1"
41+ HA_MODE=false # auto-detected: true when secondary-vm + hsr-unique-id provided
3742
3843# Pre-registration script URL
3944PREREG_SCRIPT_URL=" https://aka.ms/ScriptForPermsOnHANA"
@@ -60,15 +65,18 @@ Usage: setup_azure_backup.sh [OPTIONS]
6065
6166Required:
6267 --subscription Azure Subscription ID
63- --resource-group Resource group containing the HANA VMs
68+ --resource-group Resource group for the Recovery Services vault
6469 --location Azure region (e.g., eastus2)
6570 --vault-name Recovery Services vault name
6671 --primary-vm Primary HANA VM name
67- --secondary-vm Secondary HANA VM name
6872 --sid SAP HANA System ID (e.g., HDB)
73+
74+ HSR mode (both required for HA):
75+ --secondary-vm Secondary HANA VM name
6976 --hsr-unique-id Unique HSR identifier (6-35 chars, alphanumeric)
7077
7178Optional:
79+ --vm-resource-group Resource group containing the HANA VMs (default: same as --resource-group)
7280 --instance-number HANA instance number (default: 00)
7381 --backup-key hdbuserstore key for backup user (default: AZUREWLBACKUPHANAUSER)
7482 --policy-name Backup policy name (default: sap-hana-backup-policy)
@@ -80,7 +88,7 @@ Optional:
8088 -h, --help Show this help
8189
8290Examples:
83- # Full setup for HSR
91+ # Full setup for HSR (two VMs, same resource group)
8492 ./setup_azure_backup.sh \
8593 --subscription "aaaa-bbbb-cccc" \
8694 --resource-group "sap-hana-rg" \
@@ -92,6 +100,16 @@ Examples:
92100 --instance-number "00" \
93101 --hsr-unique-id "HSRProd01"
94102
103+ # Standalone (non-HA) single VM, vault in a different RG
104+ ./setup_azure_backup.sh \
105+ --subscription "aaaa-bbbb-cccc" \
106+ --resource-group "vault-rg" \
107+ --vm-resource-group "vm-rg" \
108+ --location "eastus2" \
109+ --vault-name "hana-vault" \
110+ --primary-vm "hana-vm1" \
111+ --sid "HDB"
112+
95113 # Skip vault creation, use existing
96114 ./setup_azure_backup.sh \
97115 --subscription "aaaa-bbbb-cccc" \
@@ -115,6 +133,7 @@ parse_args() {
115133 case " $1 " in
116134 --subscription) SUBSCRIPTION=" $2 " ; shift 2 ;;
117135 --resource-group) RESOURCE_GROUP=" $2 " ; shift 2 ;;
136+ --vm-resource-group) VM_RESOURCE_GROUP=" $2 " ; shift 2 ;;
118137 --location) LOCATION=" $2 " ; shift 2 ;;
119138 --vault-name) VAULT_NAME=" $2 " ; shift 2 ;;
120139 --primary-vm) PRIMARY_VM=" $2 " ; shift 2 ;;
@@ -142,21 +161,35 @@ validate_params() {
142161 [[ -z " $LOCATION " ]] && missing+=(" --location" )
143162 [[ -z " $VAULT_NAME " ]] && missing+=(" --vault-name" )
144163 [[ -z " $PRIMARY_VM " ]] && missing+=(" --primary-vm" )
145- [[ -z " $SECONDARY_VM " ]] && missing+=(" --secondary-vm" )
146164 [[ -z " $SID " ]] && missing+=(" --sid" )
147- [[ -z " $HSR_UNIQUE_ID " ]] && missing+=(" --hsr-unique-id" )
148165
149166 if [[ ${# missing[@]} -gt 0 ]]; then
150167 fatal " Missing required parameters: ${missing[*]} "
151168 fi
152169
153- # Validate HSR unique ID format (6-35 chars, must have digit+lowercase+uppercase)
154- if [[ ${# HSR_UNIQUE_ID} -lt 6 || ${# HSR_UNIQUE_ID} -gt 35 ]]; then
155- fatal " HSR_UNIQUE_ID must be 6-35 characters. Got: ${# HSR_UNIQUE_ID} "
170+ # Default VM_RESOURCE_GROUP to RESOURCE_GROUP if not specified
171+ VM_RESOURCE_GROUP=" ${VM_RESOURCE_GROUP:- $RESOURCE_GROUP } "
172+
173+ # Determine HA vs standalone mode
174+ if [[ -n " $SECONDARY_VM " && -n " $HSR_UNIQUE_ID " ]]; then
175+ HA_MODE=true
176+ info " Mode: HSR (High Availability) — two VMs"
177+ elif [[ -n " $SECONDARY_VM " || -n " $HSR_UNIQUE_ID " ]]; then
178+ fatal " HSR mode requires both --secondary-vm and --hsr-unique-id. Provide both or neither."
179+ else
180+ HA_MODE=false
181+ info " Mode: Standalone (single VM, non-HA)"
182+ fi
183+
184+ # Validate HSR unique ID format when in HA mode
185+ if [[ " $HA_MODE " == " true" ]]; then
186+ if [[ ${# HSR_UNIQUE_ID} -lt 6 || ${# HSR_UNIQUE_ID} -gt 35 ]]; then
187+ fatal " HSR_UNIQUE_ID must be 6-35 characters. Got: ${# HSR_UNIQUE_ID} "
188+ fi
156189 fi
157190
158- # Validate combined VM name + RG length <= 84
159- local combined_len=$(( ${# PRIMARY_VM} + ${# RESOURCE_GROUP } ))
191+ # Validate combined VM name + VM RG length <= 84
192+ local combined_len=$(( ${# PRIMARY_VM} + ${# VM_RESOURCE_GROUP } ))
160193 if [[ $combined_len -gt 84 ]]; then
161194 fatal " Combined VM name + resource group length ($combined_len ) exceeds 84 characters."
162195 fi
@@ -181,9 +214,9 @@ get_vm_resource_id() {
181214 local vm_name=" $1 "
182215 az vm show \
183216 --name " $vm_name " \
184- --resource-group " $RESOURCE_GROUP " \
217+ --resource-group " $VM_RESOURCE_GROUP " \
185218 --query " id" -o tsv 2> /dev/null \
186- || fatal " VM '$vm_name ' not found in resource group '$RESOURCE_GROUP '"
219+ || fatal " VM '$vm_name ' not found in resource group '$VM_RESOURCE_GROUP '"
187220}
188221
189222# ──────────────────────────────────────────────
@@ -269,13 +302,28 @@ run_prereg_script() {
269302 # MDC port format: 3<instance_number>13
270303 port=" 3${INSTANCE_NUMBER} 13"
271304
272- info " Running pre-registration script on both VMs..."
273- info " SID=$SID , Instance=$INSTANCE_NUMBER , Port=$port , HSR_ID=$HSR_UNIQUE_ID "
305+ # Build VM list
306+ local -a vm_list=(" $PRIMARY_VM " )
307+ if [[ " $HA_MODE " == " true" ]]; then
308+ vm_list+=(" $SECONDARY_VM " )
309+ fi
310+
311+ info " Running pre-registration script on ${# vm_list[@]} VM(s)..."
312+ info " SID=$SID , Instance=$INSTANCE_NUMBER , Port=$port "
313+ if [[ " $HA_MODE " == " true" ]]; then
314+ info " HSR_ID=$HSR_UNIQUE_ID "
315+ fi
274316
275- for vm_name in " $PRIMARY_VM " " $SECONDARY_VM " ; do
317+ # Build HSR-specific flags for the pre-registration script
318+ local hsr_flags=" "
319+ if [[ " $HA_MODE " == " true" ]]; then
320+ hsr_flags=" -hn ${HSR_UNIQUE_ID} "
321+ fi
322+
323+ for vm_name in " ${vm_list[@]} " ; do
276324 info " Running pre-registration script on '$vm_name '..."
277325 az vm run-command invoke \
278- --resource-group " $RESOURCE_GROUP " \
326+ --resource-group " $VM_RESOURCE_GROUP " \
279327 --name " $vm_name " \
280328 --command-id RunShellScript \
281329 --scripts "
@@ -293,7 +341,7 @@ run_prereg_script() {
293341 -n ${INSTANCE_NUMBER} \
294342 -sk SYSTEMKEY \
295343 -bk ${BACKUP_KEY} \
296- -hn ${HSR_UNIQUE_ID } \
344+ ${hsr_flags } \
297345 -p ${port} \
298346 -sn
299347 " \
@@ -309,7 +357,13 @@ run_prereg_script() {
309357register_containers () {
310358 info " Registering VM containers to vault..."
311359
312- for vm_name in " $PRIMARY_VM " " $SECONDARY_VM " ; do
360+ # Build VM list
361+ local -a vm_list=(" $PRIMARY_VM " )
362+ if [[ " $HA_MODE " == " true" ]]; then
363+ vm_list+=(" $SECONDARY_VM " )
364+ fi
365+
366+ for vm_name in " ${vm_list[@]} " ; do
313367 local resource_id
314368 resource_id=$( get_vm_resource_id " $vm_name " )
315369
@@ -341,7 +395,7 @@ register_containers() {
341395
342396 info " Container registration complete."
343397
344- # Verify both registered
398+ # Verify registered containers
345399 info " Registered containers:"
346400 az backup container list \
347401 --resource-group " $RESOURCE_GROUP " \
@@ -356,7 +410,7 @@ register_containers() {
356410discover_databases () {
357411 info " Initiating database discovery on primary VM..."
358412
359- local container_name=" VMAppContainer;Compute;${RESOURCE_GROUP } ;${PRIMARY_VM} "
413+ local container_name=" VMAppContainer;Compute;${VM_RESOURCE_GROUP } ;${PRIMARY_VM} "
360414
361415 az backup protectable-item initialize \
362416 --resource-group " $RESOURCE_GROUP " \
@@ -512,19 +566,21 @@ POLICY_EOF
512566enable_protection () {
513567 info " Enabling backup protection on discovered databases..."
514568
515- # Get HSR container parent name for --server-name parameter
516- local hsr_parent
517- hsr_parent=$( az backup protectable-item list \
518- --resource-group " $RESOURCE_GROUP " \
519- --vault-name " $VAULT_NAME " \
520- --workload-type SAPHANA \
521- --query " [?properties.protectableItemType=='HanaHSRContainer'].properties.parentName" \
522- -o tsv 2> /dev/null | head -1)
569+ # In HA mode, look for HSR container parent for --server-name parameter
570+ local hsr_parent=" "
571+ if [[ " $HA_MODE " == " true" ]]; then
572+ hsr_parent=$( az backup protectable-item list \
573+ --resource-group " $RESOURCE_GROUP " \
574+ --vault-name " $VAULT_NAME " \
575+ --workload-type SAPHANA \
576+ --query " [?properties.protectableItemType=='HanaHSRContainer'].properties.parentName" \
577+ -o tsv 2> /dev/null | head -1)
523578
524- if [[ -z " $hsr_parent " ]]; then
525- warn " No HSR container found. Databases may need to be protected individually."
526- else
527- info " HSR container parent: $hsr_parent "
579+ if [[ -z " $hsr_parent " ]]; then
580+ warn " No HSR container found. Databases may need to be protected individually."
581+ else
582+ info " HSR container parent: $hsr_parent "
583+ fi
528584 fi
529585
530586 # Get list of SAPHanaDatabase items
@@ -605,17 +661,25 @@ main() {
605661 validate_params
606662 check_az_cli
607663
664+ local mode_label=" Standalone"
665+ if [[ " $HA_MODE " == " true" ]]; then
666+ mode_label=" HSR (High Availability)"
667+ fi
668+
608669 info " ============================================="
609- info " Azure Backup Setup for SAP HANA (HSR) "
670+ info " Azure Backup Setup for SAP HANA — $mode_label "
610671 info " ============================================="
611- info " Vault: $VAULT_NAME "
612- info " Resource Group: $RESOURCE_GROUP "
613- info " Location: $LOCATION "
614- info " Primary VM: $PRIMARY_VM "
615- info " Secondary VM: $SECONDARY_VM "
616- info " SID: $SID "
617- info " HSR Unique ID: $HSR_UNIQUE_ID "
618- info " Policy: $POLICY_NAME "
672+ info " Vault: $VAULT_NAME "
673+ info " Vault RG: $RESOURCE_GROUP "
674+ info " VM RG: $VM_RESOURCE_GROUP "
675+ info " Location: $LOCATION "
676+ info " Primary VM: $PRIMARY_VM "
677+ if [[ " $HA_MODE " == " true" ]]; then
678+ info " Secondary VM: $SECONDARY_VM "
679+ info " HSR Unique ID: $HSR_UNIQUE_ID "
680+ fi
681+ info " SID: $SID "
682+ info " Policy: $POLICY_NAME "
619683 info " ============================================="
620684 echo " "
621685
0 commit comments