Inline object as a parameter value to a linked template deployment - azure

I'd like to be able to pass an inline object as the value of a parameter to a linked template. The use case would be that I have a template that deploys a service bus (or some other resource) and a template that deploys a web application. I want to build a template that marries the two components. I'd like the web app template to have an object parameter called userProvidedAppSettings that I can union with some defaults and then assign that resulting object as the properties value of a Microsoft.Web/site/config/appsettings resource.
It appears that you can not currently use the reference or listkeys functions in an inline object value for a parameter, see the userProvidedAppSettings in the example below.
Is this possible and I'm not using a proper convention? I haven't seen anything in the documentation about this.
{
"apiVersion": "[parameters('apiVersion')]",
"name": "[variables('serviceBusDeploymentName')]",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[parameters('templateOneUri')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"environment": { "value": "[parameters('environment')]" },
"appName": { "value": "[parameters('appName')]" }
}
}
},
{
"apiVersion": "[parameters('apiVersion')]",
"name": "[variables('applicationDeploymentName')]",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[parameters('templateTwoUri')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"environment": { "value": "[parameters('environment')]" },
"appName": { "value": "[parameters('appName')]" },
"userProvidedAppSettings" : { "value": { "serviceBusConnectionString": "[reference(variables('serviceBusDeploymentName')).outputs.connectionString.value]" } }
}
}
}
EDIT:
To clarify, this is about the behavior of linked template parameter values. I am specifically asking about this:
"parameters": {
// Allowed:
"param1": { "value": "[parameters('environment')]" },
"param2": { "value": "[reference('otherDeployment').outputs.something.value]" },
"param3": { "value": { "this": "is allowed",
"inline": "is allowed" } },
// NOT Allowed
"param4": { "value": { "this": "is NOT allowed".
"foo": "[reference('otherDeployment').outputs.something.value]" } }
}
reference outputs are allowed as values, inline objects are allowed as values, but inline objects whose values include a reference (or implicit reference from list functions) are NOT allowed. I'm wondering if this is either possible through a different convention or if this should be added to a list of desired features.

For your issue, not sure but you can have a try about links and nested templates. You can get the value of the link template in the main template.
You can define the variable in the link template output and use it in the main template. There is a simple example here. Hope this will help you!

you could use a union() function to build the desired object and pass that as a value to the parameter.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"firstObject": {
"type": "object",
"defaultValue": {"one": "a", "two": "b", "three": "c1"}
},
"secondObject": {
"type": "object",
"defaultValue": {"three": "c2", "four": "d", "five": "e"}
}
},
"resources": [],
"outputs": {
"objectOutput": {
"type": "object",
"value": "[union(parameters('firstObject'), parameters('secondObject'))]"
}
}
}

Related

Azure mainTemplate.json - access output from templateLink

I have a JSON deployment template with something like:
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-10-01",
"name": "parameters('storageAccounts')[copyIndex()].name",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "'https://foo.blob.sa/StorageAccount/azuredeploy.json"
},
"parameters": {
...
}
},
azuredeploy.json creates the storage account then has something like:
"outputs": {
"storageAccountWebEndpoint": {
"type": "object",
"value": {
"tags": { ... },
"type": "string",
"value": "[reference(parameters('storageAccountName')).primaryEndpoints.web]"
}
},
Is it possible to leverage the output from the linked template to set a property for another resource in my deployment template?
If so, what would be the syntax?
(Assume I have dependsOn set correctly.)
You can use the function reference() to get the output in the link template:
"[reference('deploymentName').outputs.propertyName.value]"
But note that:
When getting an output property from a linked template, the property
name must not include a dash.
Get more details here.

Can output of an ARM template be used in another template as a reference which is not called as nested?

I'm trying to use different templates for creating a NSG and then for a spoke. I don't want to use nested template, instead I want the out of NSG template as resource ID and give reference to spoke template as a parameter. Can this be achieved or is it just the case for nested template also the parameters in the spoke template where NSG resource ID is needed is in a array as I have used copy function.
"outputs": {"resourceID": {
"type": "string",
"value": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('nsgName'))]"}}
This output is to be used here
"subnetsConfiguration": {
"value": [
{
"name": "app-subnet",
"addressPrefix": "10.112.0.0/20",
"networkSecurityGroupName": "set the resource ID as a reference here",
To get an output value from a linked template, retrieve the property value with syntax like: [reference('deploymentName').outputs.propertyName.value]
First, the linked template- helloworld.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [],
"outputs": {
"greetingMessage": {
"value": "Hello World",
"type" : "string"
}
}
}
The main template deploys the linked template and gets the returned value -
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-06-01",
"name": "linkedTemplate",
"properties": {
"mode": "incremental",
"templateLink": {
"uri": "[uri(deployment().properties.templateLink.uri, 'helloworld.json')]",
"contentVersion": "1.0.0.0"
}
}
}
],
"outputs": {
"messageFromLinkedTemplate": {
"type": "string",
"value": "[reference('linkedTemplate').outputs.greetingMessage.value]"
}
}
}
Please refer this documentation for more details.

How to wait for deploying a resource in another ResourceGroup in Arm template

We want to have two resource group, 1- shared-resource group for shared resources like app service plan, ...
2- another resource-group for other resources like web-app.
We are using nested link template which contains three level:
highest level is main deployment template file which contains everything.
next level is for calling all resource templates for each resource-group, e.g we have two template file in this level one for shared resource-group and two other resource-group, so for-example first file will call some link template to create app service plan, elastic pool and ....
second file will call link templates for deploying web-app, network and ....
last level is our generic resource templates, in this level for each resource we have a template file e.g for web-app we have a generic template which deploy web-app with some parameters.
Now the question is how we can use 'dependson' to create all shared-Resources first after that deploy all other resources?
*please note: shared resources are in different resource-group and other resources are in another resource-group so we have two resource-group which both and all included resources are deploying in main template file.
My main deployment template file is this:
// Include rg-CockpitShared and app specific resources - and share parameters
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-
01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"cockpitEnvironment": {
"type": "string"
}
},
"variables": {
"currentTemplateUri": "[deployment().properties.templateLink.uri]",
"rgCockpitSharedUrl": "[concat(uri(variables('currentTemplateUri'), 'rg-CockpitShared/rg-CockpitShared.json'))]",
"rgCockpitSharedParametersUrl": "[concat(uri(variables('currentTemplateUri'), concat('rg-CockpitShared/rg-CockpitShared.parameters.', parameters('cockpitEnvironment'), '.json')))]",
"rgClientCmdbTemplateUrl": "[concat(uri(variables('currentTemplateUri'), 'rg-ClientCmdb/rg-ClientCmdb.json'))]",
"rgClientCmdbParametersUrl": "[concat(uri(variables('currentTemplateUri'), concat('rg-ClientCmdb/rg-ClientCmdb.parameters.', parameters('cockpitEnvironment'), '.json')))]"
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "CockpitShared",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgCockpitSharedUrl')]",
"contentVersion": "1.0.0.0"
},
"parametersLink": {
"uri": "[variables('rgCockpitSharedParametersUrl')]",
"contentVersion": "1.0.0.0"
}
}
},
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "otherResources",
"tags": {
"devOwner": "aspBuild"
},
"dependsOn": [
"CockpitShared"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgClientCmdbTemplateUrl')]",
"contentVersion": "1.0.0.0"
},
"parametersLink": {
"uri": "[variables('rgClientCmdbParametersUrl')]",
"contentVersion": "1.0.0.0"
}
}
}
],
"outputs": {
"rgCockpitSharedUrl": {
"type": "string",
"value": "[variables('rgCockpitSharedUrl')]"
},
"rgCockpitSharedParametersUrl": {
"type": "string",
"value": "[variables('rgCockpitSharedParametersUrl')]"
}
}
}
So What I have done here is using dependsOn = "CockpitShared" and expected that first it deploys shared resources and after that start to deploy 'otherResources', but I got error that app-service-plan is not found which means it's not wait to shared resources be deployed and I don't understand why? :(
Finally I found a solution:
I used 'reference' instead of 'dependson'.
So in my shared-Resources arm template I return app service plan name as output and used it from my main deployment file so it wait for finishing shared resources deployment first and after that start to deploy web app ...
for more detail see below code:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"cockpitEnvironment": {
"type": "string"
},
"storageAccountUri": {
"type": "string"
}
},
"variables": {
"rgCockpitSharedUrl": "[concat(uri(parameters('storageAccountUri'), 'rg-CockpitShared/rg-CockpitShared.json'))]",
"rgClientCmdbTemplateUrl": "[concat(uri(parameters('storageAccountUri'), 'ClientCmdb/rg-ClientCmdb/rg-ClientCmdb.json'))]"
},
"functions": [
{
"namespace": "ClientCmdb",
"members": {
"SelectValueByEnv": {
"parameters": [
{
"name": "Environment",
"type": "string"
},
{
"name": "prodValue",
"type": "string"
},
{
"name": "devValue",
"type": "string"
},
{
"name": "testValue",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[if(equals(parameters('Environment'),'prod'),parameters('prodValue'),if(equals(parameters('Environment'),'dev'),parameters('devValue'),parameters('testValue')))]"
}
}
}
}
],
"resources": [
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "CockpitShared",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgCockpitSharedUrl')]"
},
"parameters": {
"rgName": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'rg-CockpitShared','rg-CockpitShared','rg-CockpitShared')]"
},
"location": {
"value": "[parameters('location')]"
},
"cockpitEnvironment": {
"value": "[parameters('cockpitEnvironment')]"
}
}
}
},
{
"type": "Microsoft.Resources/deployments",
"location": "[parameters('location')]",
"apiVersion": "2019-10-01",
"name": "ClientCmdb",
"tags": {
"devOwner": "aspBuild"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('rgClientCmdbTemplateUrl')]"
},
"parameters": {
"rgName": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'rg-ClientCmdb','rg-ClientCmdb','rg-ClientCmdb')]"
},
"location": {
"value": "[ClientCmdb.SelectValueByEnv(parameters('cockpitEnvironment'),'northeurope','northeurope','northeurope')]"
},
"cockpitEnvironment": {
"value": "[parameters('cockpitEnvironment')]"
},
"planName": {
// Here__________________________________________________________________________________
"value": "[reference('CockpitShared').outputs.planName.value]"
}
}
}
}
],
}
so basically I pass my app service plan name from shared resource template to main template and main template gets it by 'reference' and pass it to other resources template via parameter.
Regarding to do that I had to use inline params as I was using params in seperated files...
Hope it's useful for someone else :)

Can one get output from multiple (copy/copyIndex) ARM child Templates?

I have a parent template that takes an array (websites) and invokes a childTemplate (website) multiple times using the copyIndex() method to loop through values in the array passed as a parameter.
Each of the child templates returns -- in its outputs -- the MSI principalId, as well as outgoingIPAddresses.
Is there a way one can assemble the returned individual principalId values into an array that can be used by subsequent child template invoked by the parent template (in order to loop through the resulting array of principalIds and give them all the same rights to a KeyVault)?
Thank you.
Yes, this can be done, although, not as pretty\easy as you would like it to.
easiest way to do this, is assemble an array of values you need using output from the templates. you need each your template to take the output from the previous one and concat it with its own output and spit the result as an output. sample code:
{
"name": "reference0", << this one is needed so that the first real one has something to reference, as it cant reference itself
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "yourtemplate",
"contentVersion": "1.0.0.0"
}
},
"parameters": {
"state": {
"value": []
}
}
},
{
"name": "[concat('reference', copyIndex(1))]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"copy": {
"name": "loop",
"count": "[variables('types')[resourceGroup().name]]"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "yourtemplate",
"contentVersion": "1.0.0.0"
},
"parameters": {
"state": {
"value": "[reference(concat('loop', copyIndex())).outputs.state.value]"
}
}
}
},
and your state output should just be something like this:
"outputs": {
"state": {
"type": "array",
"value": "[concat(parameters('state'), array(your_actual_output))]"
}

Nested variable and resource with copy not working

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]"

Resources