I am trying to use nested resources with copy option as mentioned in https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#serial-copy
I have defined "lbApiVersion" inside nested resource variable block but some how the nested variable block is not recognize.
below is the ARM template i am trying and this is just example in actual scenario i want to pass array to arm template and then create multiple group of resources in loop, so in that case i need the nested variable block.
ARM -
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"numberToDeploy": {
"type": "int",
"minValue": 2,
"defaultValue": 2
}
},
"resources": [
{
"apiVersion": "2015-01-01",
"type": "Microsoft.Resources/deployments",
"name": "[concat('loop-', copyIndex())]",
"copy": {
"name": "iterator",
"count": "[parameters('numberToDeploy')]",
"mode": "serial",
"batchSize": 1
},
"properties": {
"mode": "Incremental",
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {
"lbApiVersion": "2015-06-15"
},
"resources": [
{
"apiVersion": "[variables('lbApiVersion')]",
"type": "Microsoft.Network/loadBalancers",
"name": "[concat('LB','-', copyIndex())]",
"location": "[parameters('clusterLocation')]",
"dependsOn": [
],
"properties": {
"frontendIPConfigurations": [
],
"backendAddressPools": [
],
"loadBalancingRules": [
],
"probes": [
],
"inboundNatPools": [
]
},
"tags": {
"resourceType": "Service Fabric"
}
}
],
"outputs": {
}
}
}
}
],
"outputs": {
}
}
"message": "Unable to process template language expressions for resource '/subscriptions/*************/resourceGroups/cluv2/providers/Microsoft.Resources/deployments/loop-0' at line '14' and column '10'. 'The template
variable 'lbApiVersion' is not found.
Based on my experience, when you define nested templates inline (so in the code of your existing template) they take parameter and variable values from your parent template, so just move the variable definition to your parent template
Unfortunately, you are not able to use variables and parameters in nested templates as indicated by the documentation. You can use them in external templates.
If you are trying to deploy multiple resources in an inline template, declare a variable or parameter of type object in the main template like this:
"variables" : {
"loadBalancers": [
{
"version": "2015-06-15"
},
{
"version": "2015-06-15"
}
]
}
Your copy on the Microsoft.Resources/deployments resource will look like this:
"copy": {
"name": "loadBalancerLoop",
"count": "[length(variables('loadBalancers'))]"
}
Then, in your nested resource, use the copyIndex() to grab the version
"apiVersion": "[variables('loadBalancers')[copyIndex()].version]"
Related
I am trying to use nested templates to deploy a resource group and multiple resources within it on subscription level.
Microsoft documentation has an example of deploying resource group and storage account that I'm trying to follow. I am trying to create another inner level of dependency between a Storage Account resource and a Container resource. That is, the container should only be deployed after the deployment of the storage account is finished. Here is simplified version of my template:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"rgName": {
"type": "string"
},
"rgLocation": {
"type": "string"
},
"storagePrefix": {
"type": "string",
"maxLength": 11
},
"containerName": {
"type": "string"
}
},
"variables": {
"storageName": "[concat(parameters('storagePrefix'), uniqueString(subscription().id, parameters('rgName')))]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2021-04-01",
"name": "[parameters('rgName')]",
"location": "[parameters('rgLocation')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"name": "storageDeployment",
"resourceGroup": "[parameters('rgName')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('rgName'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"name": "[variables('storageName')]",
"location": "[parameters('rgLocation')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-06-01",
"name": "[format('{0}/default/{1}', variables('storageName'), parameters('containerName'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageName'))]"
]
}
],
"outputs": {}
}
}
}
],
"outputs": {}
}
When I try to deploy this template using PowerShell script New-AzSubscriptionDeployment, I get the following error:
| InvalidTemplate - Long running operation failed with status 'Failed'. Additional Info:'Deployment template validation failed: 'The resource 'Microsoft.Storage/storageAccounts/myStorageAccount' is not defined in the template. Please see https://aka.ms/arm-template for usage details.'.'
I kind of know it has to do with the dependsOn part of the container resource. But how can I resolve this problem?
EDIT: The selected answer solves the problem with dependencies, however the issue still persists in cases where a value needs to be called using concat or listKeys expressions. Here's an example where setting the value for AzureWebJobsStorage throws an error in a nested template:
{
"type": "Microsoft.Web/sites",
[ ... ]
"dependsOn": [
"[variables('hostingPlanName')]",
"[variables('functionAppStorageAccountName')]"
],
"properties": {
"serverFarmId": "[variables('hostingPlanName')]",
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('functionAppstorageAccountName'), ';EndpointSuffix=', environment().suffixes.storage, ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('functionAppstorageAccountName')), '2021-04-01').keys[0].value)]"
}
[ ... ]
]
}
The value for AzureWebJobsStorage causes the deployment to fail with the following error:
`Status Message: The Resource 'Microsoft.Storage/storageAccounts/stfuncaedotestfeb16g' under resource group '<null>' was not found. For more details
| please go to https://aka.ms/ARMResourceNotFoundFix (Code:ResourceNotFound) CorrelationId: 55942377-6d0f-40ec-9733-33b9c3ea13de
I tried being more verbose by using resource group name (and then subscription ID), but that didn't solve the problem.
You should be able to use:
"dependsOn": [
"[variables('storageName')]"
]
Note that will only work if there is no other resource in the template with the same name - otherwise you have to manually construct the full resourceId, like:
[format('{0}/resourceGroups/{1}/providers/Microsoft.Storage/storageAccounts/{2}', subscription().id, parameters('rgName'), variables('storageName'))]
The latter form will always work, just a bit more verbose.
A bit more detail is that the resourceId function doesn't work as you would expect at subscription scope.
I am getting Template validation failed: The template resource 'reports' for type 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Common.Entities.TemplateGenericProperty`1[System.String]' at line '34' and column '79' has incorrect segment lengths. A nested resource type must have identical number of segments as its resource name. A root resource type must have segment length one greater than its resource name. Please see https://aka.ms/arm-template/#resources for usage details. when I make ARM to create containers from the array in parameters file.
Issue line: "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
Here is my ARM template file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "The name of the storage account"
}
},
"storageContaners": {
"type": "string",
"metadata": {
"description": "The name of the blob containers"
}
}
},
"functions": [],
"variables": {
},
"resources": [
{
"name": "[parameters('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"location": "[resourceGroup().location]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-04-01",
"name": "[parameters('storageContaners')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"publicAccess": "Blob"
}
}
],
"outputs": {}
}
Here is my ARM parameters file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "mystorageaccount"
},
"storageContaners": {
"value": "reports"
}
}
}
I have tried changing name to different types but no luck.
Can anybody please help me to figure it out the cause?
The name parameters under the nested resources must be one level less than the type.
Here type has 4 level(separated by 3 / ). So name must have 3 level (separated by 2 /).
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('storageContaners')]",
This applies when having nested resources under parent resource.
Is it possible to use nested arrays in Properties? Assuming top array would be a parent resource and nested array are child resources for each parent. I would like then to iterate all parents and then all childs associated for each parent.
Here's an example ARM Template. Where I'm creating a ServiceBus with Topics and Subscriptions. Each topic would have at least one subscription associated with it. It would be the easiest to define properties for Topics - Subscriptions in a nested arrays, like in example below.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBus": {
"defaultValue": {
"location": "uksouth",
"name": "myNewSB1",
"skuCapacity": 1,
"skuName": "Standard",
"skuTier": "Standard"
},
"type": "object"
},
"serviceBusTopics": {
"defaultValue": [
{
"name": "topic1",
"subscriptions": [
"topic1-sub1",
"topic1-sub2"
]
},
{
"name": "topic2",
"subscriptions": [
"topic2-subAbc"
]
},
{
"name": "topicOther",
"subscriptions": [
"topicOther-subDef1",
"topicOther-subDef2",
"topicOther-subDef3",
"topicOther-subDef4"
]
}
],
"type": "array"
}
},
"resources": [
{
"apiVersion": "2017-04-01",
"location": "[parameters('serviceBus').location]",
"name": "[parameters('serviceBus').name]",
"sku": {
"capacity": "[parameters('serviceBus').skuCapacity]",
"name": "[parameters('serviceBus').skuName]",
"tier": "[parameters('serviceBus').skuTier]"
},
"type": "Microsoft.ServiceBus/namespaces"
},
{
"apiVersion": "2017-04-01",
"copy": {
"count": "[length(parameters('serviceBusTopics'))]",
"mode": "Parallel",
"name": "topicsLoop"
},
"dependsOn": [
"[resourceId('Microsoft.ServiceBus/namespaces', parameters('serviceBus').name)]"
],
"name": "[concat(parameters('serviceBus').name, '/', parameters('serviceBusTopics')[copyIndex()].name)]",
"type": "Microsoft.ServiceBus/namespaces/topics"
},
{
"apiVersion": "2017-04-01",
"copy": {
"count": "[length(<...>)]",
"mode": "Parallel",
"name": "subscriptionsLoop"
},
"dependsOn": [
"topicsLoop"
],
"name": "[concat(parameters('serviceBus').name, '/', <...>, '/', <...>)]",
"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions"
}
]
}
so you have 2 options:
Hardcode each resource, so create 1 resource for 1 object in the array and that way you will be able to iterate, but when you add\remove objects from the array - you will need to adjust the template (not optimal, obviously)
Use a nested template. Let me elaborate a bit:
not sure how to reset the list counter, lol
Create a nested deployment in the template that iterates the base array so serviceBusTopics
pass current iteration to the nested template: "[parameters('serviceBusTopics')[copyIndex()]]"
in the nested template you are only dealing with a single object that has one of the properties of type array and you can iterate that.
I'm trying to create a conditional resource template. The devel environment is not as beefy as the production environment and I've been successful in doing this for the most part. However, I can't seem to get nested resources right.
Here's a snippet from my ARM template:
"webApp-resources": "[variables(concat('webApp-', parameters('env'), '-resources'))]",
"webApp-dev-resources": [],
"webApp-prod-resources": [
{
"name": "staging",
"type": "Microsoft.Web/sites/slots",
"location": "[resourceGroup().location]",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('webApp-name'))]"
]
}
],
The idea is simple, the resources variable is composed using the env parameter. The env parameter can be either dev or prod and while this works, I get the following error when I try to deploy this template.
{
"name": "[variables('webApp-name')]",
"type": "Microsoft.Web/sites",
...
"resources": "[variables('webApp-resources')]" // <- culprit!
},
The request content was invalid and could not be deserialized: 'Error converting value "[variables('webApp-resources')]" to type 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Templates.Schema.TemplateResource[]'. Path 'properties.template.resources[1].resources', line 195, position 64.'
I've also tried moving the resource into a variable and referencing the variable in a similar conditional manner, very similar to how we would do nested template linking but without the template linking.
resources: [
"[variables('webApp-resource')]" // <- this doesn't work!
]
This resulted in a similar error but different error if I recalled correctly.
From this I've concluded that ARM template syntax is not simply find and replace which I think is bad because it does make it harder to reason about what works and what doesn't. Because if it was, this would have resulted in a valid template that would work. Which I've verified by pasting the correct value into the resources section.
Has anyone had similar problems, how did you work around the issue?
You should be able to do this without multiple template files, but not without using nested deployments. So depending on what you're trying to avoid, try this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"env": {
"type": "string",
"allowedValues": [ "dev", "prod" ]
}
},
"variables": {
"resourceArray": "[variables(concat('resources', parameters('env')))]",
"resourcesprod": [
{
"name": "as",
"type": "Microsoft.Compute/availabilitySets",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [],
"properties": {
}
}
],
"resourcesdev": []
},
"resources": [
{
"name": "nest",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2016-09-01",
"dependsOn": [],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": "[variables('resourceArray')]"
},
"parameters": {
}
}
}
],
"outputs": {}
}
Fast question. How create installation using templates? Can you give an example? How construct 'InstallationTemplate' object.
When you want to create an ARM template based on the json schema of Microsoft.NotificationHubs:
Create a new project in Visual Studio 2015.
Choose for a Cloud project
Choose for Azure Resource Group
Choose Blank Template
Open azuredeploy.json
You will see this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
},
"resources": [
],
"outputs": {
}
}
Then begin with the resources part by typing apiVersion (note that you get intellisense already):
"resources": [
{
"apiVersion": ""
}
],
After this add the type: Just select the correct value.
After the type is set. You will get intellisense on the allowed properties.
There are more parameters available. But when using the main properties and
create parameters of the hardcoded values and at the end you will get a template like:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"namespaceLocation": {
"type": "string"
},
"namespaceName": {
"type": "string"
},
"notificationHubName": {
"type": "string"
}
},
"variables": {
},
"resources": [
{
"apiVersion": "2014-09-01",
"name": "[parameters('namespaceName')]",
"type": "Microsoft.NotificationHubs/namespaces",
"location": "[parameters('namespaceLocation')]",
"properties": {
"name": "[parameters('namespaceName')]",
"namespaceType": "NotificationHub"
},
"resources": [
{
"apiVersion": "2014-09-01",
"name": "[parameters('notificationHubName')]",
"type": "Microsoft.NotificationHubs/namespaces/notificationHubs",
"location": "[parameters('namespaceLocation')]",
"dependsOn": [
"[concat('Microsoft.NotificationHubs/namespaces/', parameters('namespaceName'))]"
],
"properties": {
"name": "[parameters('notificationHubName')]"
}
}
]
}
],
"outputs": {
}
}