2
votes

I'm trying to add the functionality to add the VM to a recovery services vault to my existing Azure ARM JSON template that I use for deployment.

I've used the code from the following GitHub template and this works if the recovery services vault is in the same resource group as VM but not if its in a different one.

https://github.com/Azure/azure-quickstart-templates/tree/master/101-recovery-services-backup-vms

The code is below:

{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
    "existingVirtualMachinesResourceGroup": {
        "type": "string",
        "metadata": {
            "description": "Resource group where the virtual machines are located. This can be different than resource group of the vault. "
        }
    },
    "existingVirtualMachines": {
        "type": "array",
        "metadata": {
            "description": "Array of Azure virtual machines. e.g. [\"vm1\",\"vm2\",\"vm3\"]"
        }
    },
    "existingRecoveryServicesVault": {
        "type": "string",
        "metadata": {
            "description": "Recovery services vault name where the VMs will be backed up to. "
        }
    },
    "existingBackupPolicy": {
        "type": "string",
        "defaultValue": "DefaultPolicy",
        "metadata": {
            "description": "Backup policy to be used to backup VMs. Backup POlicy defines the schedule of the backup and how long to retain backup copies. By default every vault comes with a 'DefaultPolicy' which canbe used here."
        }
    }
},
"variables": {
    "backupFabric": "Azure",
    "v2VmType": "Microsoft.Compute/virtualMachines",
    "v2VmContainer": "iaasvmcontainer;iaasvmcontainerv2;",
    "v2Vm": "vm;iaasvmcontainerv2;"
},
"resources": [
    {
        "name": "[concat(parameters('existingRecoveryServicesVault'), '/', variables('backupFabric'), '/', variables('v2VmContainer'), concat(parameters('existingVirtualMachinesResourceGroup'),';',parameters('existingVirtualMachines')[copyIndex()]), '/', variables('v2Vm'), concat(parameters('existingVirtualMachinesResourceGroup'),';',parameters('existingVirtualMachines')[copyIndex()]))]",
        "apiVersion": "2016-06-01",
        "location": "[resourceGroup().location]",
        "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
        "copy": {
            "name": "v2VmsCopy",
            "count": "[length(parameters('existingVirtualMachines'))]"
        },
        "properties": {
            "protectedItemType": "[variables('v2VmType')]",
            "policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies',parameters('existingRecoveryServicesVault'),parameters('existingBackupPolicy') )]",
            "sourceResourceId": "[resourceId(subscription().subscriptionId,parameters('existingVirtualMachinesResourceGroup'),'Microsoft.Compute/virtualMachines',parameters('existingVirtualMachines')[copyIndex()])]"
        }
    }
]

}

There is no variable or parameter to define the recovery services vaults resource group.

I've also looked at the following GitHub template that also adds a VM to a recovery services vault but again this doesn't seem to have the ability to use different resource groups.

https://github.com/Azure/azure-quickstart-templates/tree/master/201-recovery-services-backup-classic-resource-manager-vms

I've tried googling but so far I haven't been able to find an answer to this so is it possible?

Thanks

3
I've found the answer to this if it's useful to anyone else. As already mentioned the recovery services vault will use the same resource group as defined for the template. To be able to define a different template for the RSV this needs to be done using a nested template.OPW

3 Answers

3
votes

I've found the answer to this if it's useful to anyone else. As already mentioned the recovery services vault will use the same resource group as defined for the template. To be able to define a different template for the RSV this needs to be done using a nested template.

I have used the following nested template to replace the recovery services resource in my original post, the resource group required for the recovery services vault is defined by "resourceGroup": "[parameters('nestedTemplateRecoveryServicesResourceGroup')]",

{
"apiVersion": "2017-05-10",
        "name": "nestedTemplateRecoveryServices",
        "type": "Microsoft.Resources/deployments",
        "resourceGroup": "[parameters('nestedTemplateRecoveryServicesResourceGroup')]",
        "dependsOn": ["[concat('Microsoft.Compute/virtualMachines/', parameters('vmName'))]"],
        "properties": {
            "mode": "Incremental",
            "template": {
                "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                "contentVersion": "1.0.0.0",
                "parameters": {},
                "variables": {},
                "resources": [
                        {
                        "name": "[concat(parameters('existingRecoveryServicesVault'), '/', variables('backupFabric'), '/', variables('v2VmContainer'), concat(parameters('existingVirtualMachinesResourceGroup'),';',parameters('existingVirtualMachines')), '/', variables('v2Vm'), concat(parameters('existingVirtualMachinesResourceGroup'),';',parameters('existingVirtualMachines')))]",
                        "apiVersion": "2016-06-01",
                        "location": "[resourceGroup().location]",
                        "type": "Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems",
                        "properties": {
                            "protectedItemType": "[variables('v2VmType')]",
                            "policyId": "[resourceId('Microsoft.RecoveryServices/vaults/backupPolicies',parameters('existingRecoveryServicesVault'),parameters('existingBackupPolicy') )]",
                            "sourceResourceId": "[resourceId(subscription().subscriptionId,parameters('existingVirtualMachinesResourceGroup'),'Microsoft.Compute/virtualMachines',parameters('existingVirtualMachines'))]"
                            }
                        }
                    ]
                },
               "parameters": {},
               "outputs": {}
        }
}
1
votes

Here's the JSON I use to achieve this - it scales when using copyIndex()...as included in the resource below. I've adapted this from Microsoft's own JSON template on the Azure Portal since they improved the VM deployment to include the option to backup during deployment.

I typically provide an array of VM names and I name other resources from those names using copy loops. Technically they're related to or children of the VM when being built so it seems most applicable to use the VM name as the basis of the name of an associated resource. I use copyIndex() to push in the name of the VM being iterated in that loop (index) which means all the sub-resources and nested templates can also use those parameters.

Anyway, this is the resource (a nested template, as you have to). Associated variables and parameters below.

{
  "apiVersion": "2017-05-10",
  "name": "[concat(parameters('virtualMachineNames')[copyIndex()], '-' , 'BackupIntent')]",
  "type": "Microsoft.Resources/deployments",
  "resourceGroup": "[parameters('DestinationRSVResourceGroup')]",
  "copy": {
    "name": "AzureBackupLoop",
    "count": "[length(parameters('virtualMachineNames'))]"
  },
  "dependsOn": [
    "NameOfPreviousLoop"
  ],
  "properties": {
    "mode": "Incremental",
    "template": {
      "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
      "contentVersion": "1.0.0.0",
      "resources": [
        {
          "name": "[concat(parameters('DestinationRecoveryServicesVault'), '/', 'Azure', '/', variables('v2Vm'), resourceGroup().name, ';', parameters('virtualMachineNames')[copyIndex()])]",
          "apiVersion": "2017-07-01",
          "type": "Microsoft.RecoveryServices/vaults/backupFabrics/backupProtectionIntent",
          "properties": {
            "friendlyName": "[concat(parameters('virtualMachineNames')[copyIndex()], 'BackupIntent')]",
            "protectionIntentItemType": "AzureResourceItem",
            "policyId": "[resourceId(parameters('DestinationRSVResourceGroup'), 'Microsoft.RecoveryServices/vaults/backupPolicies', parameters('DestinationRecoveryServicesVault'), parameters('existingBackupPolicy'))]",
            "sourceResourceId": "[resourceId(resourceGroup().name, 'Microsoft.Compute/virtualMachines', parameters('virtualMachineNames')[copyIndex()])]"
          }
        }
      ]
    }
  }
}

The variables section from this template looks as follows:

  "variables": {
    "v2Vm": "vm;iaasvmcontainerv2;",
  },

And finally the parameters (relevant to this resource):

"parameters": {
    "DestinationRecoveryServicesVault": {
      "type": "string",
      "metadata": {
        "description": "Name of the recovery services vault that the VM is to be backed up in to."
      }
    },
    "existingBackupPolicy": {
      "type": "string",
      "metadata": {
        "description": "Name of the backup policy that the VM will use."
      }
    },
    "DestinationRSVResourceGroup": {
      "type": "string",
      "metadata": {
        "description": "Resource group of the RSV."
      }
    }
    "virtualMachineNames": {
      "type": "array",
      "metadata": {
        "description": "An array of names for the VMs."
      }
    },
  },
0
votes

If you are going to use that Nested Template make sure you add the VM that you provisioned in the first template as a DependsOn. This way when it runs the incremental deployment the VM is already there.