Working with ARM Templates Part 1: Deploying a Recovery Services Vault with Daily VM Backup Policy

Azure Resource Manager (ARM) templates are a powerful way to deploy your infrastructure using code. Using ARM templates, we have the ability to customize our resources and spin them up on demand, or in an automated fashion. Integration of ARM templates into a DevOps pipeline, for instance Azure DevOps, has certainly been a target for many enterprise Azure customers in recent years. Recently, I ran into the need to build two Recovery Services Vault templates — The first one needed to have an associated VM backup policy associated with it, and second had the requirement of an associated SQL Server backup policy. It sounds easy enough, but there were certainly a few “gotcha’s” that I ran into. This, coupled with the fact that there weren’t many good examples in the Google machine, made customization a little more difficult than it needed to be. Here are a few of the challenges I faced:

  • Although there is a good VM policy template in GitHub that I started with (https://github.com/Azure/azure-quickstart-templates/tree/master/101-recovery-services-weekly-backup-policy-create), I had the need to customize this policy to ONLY include a daily backup with a 30-day retention policy. There was no need for weekly, monthly, or yearly backups.
  • By default, Recovery Services Vaults store backups utilizing GRS – or Geo-Redundant Storage. This can’t be set up when the RSV is being created, but CAN be configured after the fact from the portal, OR by using a “DependsOn” statement within the ARM template.  

Without further ado, let’s dive into the Parameters file first.

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vaultName": {
"type": "string",
"metadata": {
"description": "Name of the Vault"
}
},
"vaultRG": {
"type": "string",
"metadata": {
"description": "Name of the Resource Group"
}
},
"skuTier": {
"type": "string",
"defaultValue": "Standard",
"allowedValues": [
"Standard"
],
"metadata": {
"description": "SKU tier for the vault"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"vmBackupPolicyName": {
"type": "string",
"defaultValue": "HourlyLogBackup",
"metadata": {
"description": "Name of the SQL VM Backup Policy"
}
},
"scheduleRunTimes": {
"type": "array",
"metadata": {
"description": "Times in day when backup should be triggered. e.g. 01:00 or 13:00. Must be an array, however for IaaS VMs only one value is valid. This will be used in LTR too for daily, weekly, monthly and yearly backup."
}
},
"timeZone": {
"type": "string",
"metadata": {
"description": "Any Valid timezone, for example:UTC, Pacific Standard Time. Refer: https://msdn.microsoft.com/en-us/library/gg154758.aspx"
}
},
"dailyRetentionDurationCount": {
"type": "int",
"metadata": {
"description": "Number of days you want to retain the backup"
}
}
},

As you can see, these values have been customized to accept values of new deployments without changing the template file at all. Everything is fairly self-explanatory here, so let’s move on to the “parameters” section of the template JSON.

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vaultName": {
"type": "string",
"metadata": {
"description": "Name of the Vault"
}
},
"vaultRG": {
"type": "string",
"metadata": {
"description": "Name of the Resource Group"
}
},
"skuTier": {
"type": "string",
"defaultValue": "Standard",
"allowedValues": [
"Standard"
],
"metadata": {
"description": "SKU tier for the vault"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"vmBackupPolicyName": {
"type": "string",
"defaultValue": "HourlyLogBackup",
"metadata": {
"description": "Name of the SQL VM Backup Policy"
}
},
"scheduleRunTimes": {
"type": "array",
"metadata": {
"description": "Times in day when backup should be triggered. e.g. 01:00 or 13:00. Must be an array, however for IaaS VMs only one value is valid. This will be used in LTR too for daily, weekly, monthly and yearly backup."
}
},
"timeZone": {
"type": "string",
"metadata": {
"description": "Any Valid timezone, for example:UTC, Pacific Standard Time. Refer: https://msdn.microsoft.com/en-us/library/gg154758.aspx"
}
},
"dailyRetentionDurationCount": {
"type": "int",
"metadata": {
"description": "Number of days you want to retain the backup"
}
}
},

I pulled all the parameters out for the weekly, monthly, and yearly backups. Not much else to note here. Onto the “resources” section of the template.

"resources": [
{
"type": "Microsoft.RecoveryServices/vaults",
"apiVersion": "2018-01-10",
"name": "[parameters('vaultName')]",
"location": "[parameters('location')]",
"sku": {
"name": "RS0",
"tier": "[parameters('skuTier')]"
},
"properties": {
},
"dependsOn": [
]
},
{
"name": "[concat(parameters('vaultName'), '/vaultstorageconfig')]",
"type": "Microsoft.RecoveryServices/vaults/backupstorageconfig",
"apiVersion": "2016-12-01",
"location": "[parameters('location')]",
"properties": {
"storageModelType": "LocallyRedundant",
"storageType": "LocallyRedundant",
"storageTypeState": "Unlocked"
},
"dependsOn": [
"[resourceId(parameters('vaultRG'), 'Microsoft.RecoveryServices/vaults', parameters('vaultName'))]"
]
},
{
"apiVersion": "2016-06-01",
"name": "[concat(parameters('vaultName'), '/', parameters('vmBackupPolicyName'))]",
"type": "Microsoft.RecoveryServices/vaults/backupPolicies",
"dependsOn": [
"[concat('Microsoft.RecoveryServices/vaults/', parameters('vaultName'))]"
],
"location": "[parameters('location')]",
"properties": {
"backupManagementType": "AzureIaasVM",
"schedulePolicy": {
"scheduleRunFrequency": "Daily",
"scheduleRunDays": null,
"scheduleRunTimes": "[parameters('scheduleRunTimes')]",
"schedulePolicyType": "SimpleSchedulePolicy"
},
"retentionPolicy": {
"dailySchedule": {
"retentionTimes": "[parameters('scheduleRunTimes')]",
"retentionDuration": {
"count": "[parameters('dailyRetentionDurationCount')]",
"durationType": "Days"
}
},
"retentionPolicyType": "LongTermRetentionPolicy"
},
"timeZone": "[parameters('timeZone')]"
}
}
]
}

This is the template section where we create the VM backup policy. Note that the policy depends on the creation of the Recovery Services Vault prior to creation and association with the RSV. That’s the end of the first template. Let’s run it and check our work.

We can see that the vault has been created, and the storage replication type is set to “Locally-redundant”.

Here’s the VM backup policy that was created. We can see it correctly lines up with the configuration we’ve put into place. Success!

Stay tuned for Part 2 where we create an RSV with SQL Backup Policy using an ARM template, located here: https://blogs.stratum-tech.com/2019/03/working-with-arm-templates-part-2:-deploying-a-recovery-services-vault-with-custom-sql-backup-policy/

Working with ARM Templates Part 1: Deploying a Recovery Services Vault with Daily VM Backup Policy

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.