Use ARM functions from PowerShell - json

TL;DR
The following line works fine as part of an ARM script ran in an Azure DevOps ARM Template Deployment task but, when ran from PowerShell, it fails to pull the subscription and the resource group.
"serverFarmId": "[concat('/subscriptions/', subscription(), '/resourcegroups/', resourceGroup(), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
how can I set the subscription and resource group variables so the ARM functions can pull the values?
P.S. It's ok If I have to first get them and then set them manually for the script to consume, as long as I can use the same script for DevOps and PowerShell.
Here's the resources section of the template which, again, when deployed from a pipeline it pulls the subscription and the resource group from whatever is set in the task parameters.
"resources": [
{
"apiVersion": "2018-11-01",
"name": "[parameters('name')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"tags": {},
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[parameters('name')]",
"siteConfig": {
"metadata": [
{
"name": "CURRENT_STACK",
"value": "[parameters('currentStack')]"
}
]
},
"serverFarmId": "[concat('/subscriptions/', subscription(), '/resourcegroups/', resourceGroup(), '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]",
"clientAffinityEnabled": true
}
},
{
"apiVersion": "2018-11-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"kind": "",
"tags": {},
"dependsOn": [],
"properties": {
"name": "[parameters('hostingPlanName')]"
},
"sku": {
"Tier": "[parameters('sku')]",
"Name": "[parameters('skuCode')]"
}
}
]

subscription() and resourceGroup() do return objects based on your context. You should consider querying attributes instead like : subscription().subscriptionId and resourceGroup().name considering how you are constructing your ID. I am not sure why it works in your ARM template though.
As a side comment, you could make use of the resourceId([subscriptionId], [resourceGroupName], resourceType, resourceName1, [resourceName2], ...) function as explained here, under the hood it performs the same as you did (concat).

Related

Azure Data Factory Dataset Structure Format

I am creating a Microsoft.DataFactory/factories/datasets resource via Terraform. Here is the template from the official docs:
{
"type": "Microsoft.DataFactory/factories/datasets",
"apiVersion": "2018-06-01",
"name": "string",
"properties": {
"annotations": [ object ],
"description": "string",
"folder": {
"name": "string"
},
"linkedServiceName": {
"parameters": {},
"referenceName": "string",
"type": "LinkedServiceReference"
},
"parameters": {},
"schema": {},
"structure": {},
"type": "string"
// For remaining properties, see Dataset objects
}
}
I am stuck on the proper format for structure. The first few entries are fine.
"structure": [
{
"name": "id",
"type": "String"
},
{
"name": "partitionKey",
"type": "String"
},
{
"name": "createdDate",
"type": "Int64"
}
But then I need to translate a complex object and I am not sure how to go about it. The object looks something like:
"properties": {
"title": "Blah",
"tags": [
"A tag",
"Another tag"
],
"description": [
"So",
"Many",
"Words"
]
}
How do I parse this?
"name": "properties",
"type": "Object"
Will that suffice? Do I need to go into the nested keys? Any pointers would be much appreciated!
First and foremost: why do you try to deploy datasets from Terraform? Terraform is an infrastructure as code (IaC) software tool and is intended to deploy Infrastructure only (e.g. ADF instance, Azure SQL Server, endpoints, etc).
ADF's pipelines, datasets, Linked Services are typically application-type of objects and they should be deployed like that separately, after Infrastructure.
You can use #adftools to deploy all ADF objects directly from your artefact or code.

Azure ARM Template - Create KeyVault Secrets in Keyvault in different Resource Group

I am deploying a Virtual Machine in Azure. The Username and Password are created automatically and passed as parameters at deployment. The resource group where the vm is deployed is also passed as parameter so can be anything.
My Keyvault is in a specific resource group and the username and password of the vm should be stored here.
When the Keyvault is in the same resource group as the vm it works fine. But when it is in a different resource group I get following error:
"error": {
"code": "ParentResourceNotFound",
"message": "Can not perform requested operation on nested resource. Parent resource 'mykeyvault' not found."
}
} undefined
This is the part of the ARM template where I am creating the secrets.
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[concat(variables('keyVaultName'), '/', variables('AdminUsername'))]",
"apiVersion": "2018-02-14",
"properties": {
"contentType": "Secret",
"value": "[variables('AdminUsername')]"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('VMName'))]"
]
},
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[concat(variables('keyVaultName'), '/', parameters('VMName'),'-AdminPassword')]",
"apiVersion": "2018-02-14",
"properties": {
"contentType": "Secret",
"value": "[parameters('AdminPassword')]"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('VMName'))]"
]
},
I also tried to replace the keyVaultName variable with the resourceID of the keyvault but this gives a different error "Incorrect Segment Lengths"
that happens because ARM Templates deploys resources to the specific resource group. If the KV is in the different resource group you need to use a nested deployment and target that resource group, something like this:
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[parameters('kvResourceGroup')]",
"dependsOn": [
"[resourceId('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",
"resources": [
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[format('{0}/{1}', variables('keyVaultName'), variables('AdminUsername'))]",
"apiVersion": "2018-02-14",
"properties": {
"contentType": "Secret",
"value": "[variables('AdminUsername')]"
}
},
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[format('{0}/{1}-AdminPassword', variables('keyVaultName'), parameters('VMName'))]",
"apiVersion": "2018-02-14",
"properties": {
"contentType": "Secret",
"value": "[parameters('AdminPassword')]"
}
}
]
}
}
},

Apply Azure RBAC to a resource using ARM

Is there a way to apply RBAC rules at the resource level via ARM? I was able to follow this Microsoft guide to add a user/role at the resource group level, but not at the resource. In particular, I am trying to add a new reader role to AppInsights via ARM. However, when I adjust the scope, the template just fails with this error:
"error": {
"code": "InvalidCreateRoleAssignmentRequest",
"message": "The request to create role assignment '{guid}' is not valid. Role assignment scope '/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/Microsoft.Insights/components/{resourceGroupName}' must match the scope specified on the URI '/subscriptions/{resourceGroupName}/resourcegroups/{resourceGroupName}'."
}
I am left wondering what the scope variable is for if it cannot be changed. Is there some other place I should be modifying the scope to get this working?
Thanks in advance!
The key is to drop the scope property, and instead nest the role assignment under the desired resource by using Microsoft.FooResource/BarSubType/providers/roleAssignments as the type, and using the following format for the name: {resourceName}/Microsoft.Authorization/{uniqueRoleAssignmentGuid}. Note that the GUID should be stable but unique to this role assignment, one easy option is guid(subscription().subscriptionId, 'some-sub-identifier-if-you-wish').
Here is a template that shows you how to apply RBAC to a single resource, using a user-assigned managed identity defined in the same template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": { "type": "string" },
"userAssignedIdentityName": { "type": "string" }
},
"variables": {
"ContributorRoleDefinition": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
},
"resources": [
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"name": "[parameters('userAssignedIdentityName')]",
"location": "[resourceGroup().location]",
"apiVersion": "2018-11-30"
},
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[parameters('storageAccountName')]",
"location": "[resourceGroup().location]",
"apiVersion": "2016-12-01",
"sku": { "name": "Standard_LRS" },
"kind": "Storage",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2017-05-01",
"name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', guid(subscription().subscriptionId, 'foo'))]",
"properties": {
"roleDefinitionId": "[variables('ContributorRoleDefinition')]",
"principalId": "[reference(parameters('userAssignedIdentityName'), '2018-11-30').principalId]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
]
}
]
}
]
}
Source: https://www.henrybeen.nl/creating-an-authorization-rule-using-an-arm-template/
You apply RBAC rules at the resource level via an ARM and there is example template that applies RBAC rules at Azure VM here:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string",
"metadata": {
"description": "Principal ID associated with the subscription ID"
}
},
"virtualMachineName": {
"type": "string",
"metadata": {
"description": "Name of the virtual machine"
}
},
"builtInRoleType": {
"type": "string",
"metadata": {
"description": "Built In Role Type for the Virtual Machine"
},
"allowedValues": [
"Owner",
"Contributor",
"Reader",
"Virtual Machine Contributor"
]
},
"guid": {
"type": "string",
"metadata": {
"description": "A new GUID used to identify the role"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"Owner": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
"Contributor": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Reader": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
"Virtual Machine Contributor": "[concat('/subscriptions/',subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'd73bb868-a0df-4d4d-bd69-98a00b01fccb')]",
"resourceName": "[concat(parameters('virtualMachineName'), '/Microsoft.Authorization/', parameters('guid'))]"
},
"resources": [
{
"type": "Microsoft.Compute/virtualMachines/providers/roleAssignments",
"apiVersion": "2017-05-01",
"name": "[variables('resourceName')]",
"properties": {
"roleDefinitionId": "[variables(parameters('builtInRoleType'))]",
"principalId": "[parameters('principalId')]"
}
}
]
}
Hope this will help you.
Microsoft has finally provided documentation explaining this:
https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-template#resource-scope
It is possible to apply RBAC on resource level using ARM.
The example what you referred shows how to apply RBAC on a particular resource group, where the scope is the path of the resource group.
Here, you are trying to assign a role to a particular resource. Changing the scope from resource group to resource (AppInsights) will work.
From the exception, I can see that the path of the resource may not be in the expected format.
The path of AppInsights should be in the following format,
/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/microsoft.insights/components/{insightName}
Hope framing the scope like this helps!
Agree that the documentation on this issue is less than useful. I have an array of Role IDs that I wanted to add as Owners at the App Insight resource, without making the users Owners at the Resource Group level. I didn't want to use the nested resource approach as I wanted to iterate over an array of objects to dynamically create the roles, so after tweaking the type, name, and scope attributes, the following resource block is what ended up working for me:
{
"comments": "Add the Application Insights resource",
"apiVersion": "2014-04-01",
"name": "[variables('appInsightsName')]",
"type": "Microsoft.Insights/components",
"location": "[resourceGroup().location]",
"properties": {
"ApplicationId": "[variables('appInsightsName')]"
}
},
{
"comments": "Add the IAM roles to the App Insights resource",
"condition": "[parameters('isProduction')]",
"type": "Microsoft.Insights/components/providers/roleAssignments",
"name": "[concat(variables('appInsightsName'),'/Microsoft.Authorization/',guid(parameters('roleAssignments')[copyIndex()].principalId))]",
"apiVersion": "2017-05-01",
"location": "[resourceGroup().location]",
"properties": {
"roleDefinitionId": "[concat(subscription().Id, '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", // Owner Role
"principalId": "[parameters('roleAssignments')[copyIndex()].principalId]",
"scope": "[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
},
"copy": {
"name": "appInsightsRoleAssignments",
"count": "[length(parameters('roleAssignments'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Insights/components', variables('appInsightsName'))]"
]
}

createUiDefinition.json for ARM Azure web app

I have created the ARM template for azure web app. I need to publish the ARM template to azure market place. I have used the azure publish portal https://publish.windowsazure.com/workspace/multi-resource-solutions for publishing the ARM template.
To on-board the ARM template to azure market place zip file must contain a mainTemplate.json and createUiDefinition.json. I found some samples for createUiDefination.json file in https://github.com/Azure/azure-quickstart-templates but all the createUiDefination.json is for VM. I am unable to find the samples or tutorials for createUiDefination.json for Azure web app.
I need to validate the azure web app site name is already exists or not. Also need to create or use the app service plan.
Is there is any tutorial or sample for creating createUiDefination.json for azure web app?
I need to validate the azure web app site name is already exists or
not.
This is not possible, you need to add to the site name a unique String, to ensure that the site name is globally unique.
For example you could use in your ARM template this function: uniqueString()
Similar question was answered by a Microsoft employee.
Also need to create or use the app service plan.
Add an App Service Plan to your Azure Resource Manager Template. For example like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"type": "string",
"minLength": 1
},
"skuName": {
"type": "string",
"defaultValue": "F1",
"allowedValues": [
"F1",
"D1",
"B1",
"B2",
"B3",
"S1",
"S2",
"S3",
"P1",
"P2",
"P3",
"P4"
],
"metadata": {
"description": "Describes plan's pricing tier and capacity. Check details at https://azure.microsoft.com/en-us/pricing/details/app-service/"
}
},
"skuCapacity": {
"type": "int",
"defaultValue": 1,
"minValue": 1,
"metadata": {
"description": "Describes plan's instance count"
}
}
},
"variables": {
"webSiteName": "[concat('webSite', uniqueString(resourceGroup().id))]"
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "HostingPlan"
},
"sku": {
"name": "[parameters('skuName')]",
"capacity": "[parameters('skuCapacity')]"
},
"properties": {
"name": "[parameters('hostingPlanName')]"
}
},
{
"apiVersion": "2015-08-01",
"name": "[variables('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "Website"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[variables('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
}
}
]
}
Validating an app service's name is possible in createUiDefinition.json.
The crux of it is the Microsoft.Solutions.ArmApiControl, which can be used to call ARM apis as part of the validation of a text box. Here is an example:
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
"handler": "Microsoft.Azure.CreateUIDef",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{}
],
"steps": [
{
"name": "domain",
"label": "Domain Names",
"elements": [
{
"name": "domainInfo",
"type": "Microsoft.Common.InfoBox",
"visible": true,
"options": {
"icon": "Info",
"text": "Pick the domain name that you want to use for your app."
}
},
{
"name": "appServiceAvailabilityApi",
"type": "Microsoft.Solutions.ArmApiControl",
"request": {
"method": "POST",
"path": "[concat(subscription().id, '/providers/Microsoft.Web/checknameavailability?api-version=2021-02-01')]",
"body": "[parse(concat('{\"name\":\"', concat('', steps('domain').domainName), '\", \"type\": \"Microsoft.Web/sites\"}'))]"
}
},
{
"name": "domainName",
"type": "Microsoft.Common.TextBox",
"label": "Domain Name Word",
"toolTip": "The name of your app service",
"placeholder": "yourcompanyname",
"constraints": {
"validations": [
{
"regex": "^[a-zA-Z0-9]{4,30}$",
"message": "Alphanumeric, between 4 and 30 characters."
},
{
"isValid": "[not(equals(steps('domain').appServiceAvailabilityApi.nameAvailable, false))]",
"message": "[concat('Error with the url: ', steps('domain').domainName, '. Reason: ', steps('domain').appServiceAvailabilityApi.reason)]"
},
{
"isValid": "[greater(length(steps('domain').domainName), 4)]",
"message": "The unique domain suffix should be longer than 4 characters."
},
{
"isValid": "[less(length(steps('domain').domainName), 30)]",
"message": "The unique domain suffix should be shorter than 30 characters."
}
]
}
},
{
"name": "section1",
"type": "Microsoft.Common.Section",
"label": "URLs to be created:",
"elements": [
{
"name": "domainExamplePortal",
"type": "Microsoft.Common.TextBlock",
"visible": true,
"options": {
"text": "[concat('https://', steps('domain').domainName, '.azurewebsites.net - The main app service URL')]"
}
}
],
"visible": true
}
]
}
],
"outputs": {
"desiredDomainName": "[steps('domain').domainName]"
}
}
}
You can paste that in to the createUiDefinition.json sandbox azure provides to test it out.
To look at how to do an Azure App Service instead of a VM, look in to documentation like this and use it as the maintemplate.json in the app.zip package.

Azure ARM JSON template - Add VM to Recovery Services Vault in different Resource Group

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
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": {}
}
}
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."
}
},
},
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.