Azure mainTemplate.json - access output from templateLink - azure

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.

Related

using dependsOn property in ARM Template

I am deploying VNET before deploying other resources. It does deploy the first VNET template, but gives an error deploying others, as it says subnet is is provisioning state i.e. the resource is updating.
I am using nested templates and tried using dependsOn property in the ARM, although is not working. Is is possible to use it at the resource level?
"resources": [
{
"apiVersion": "2017-05-10",
"name": "vNet_ResourceUnit",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('vnetTemplateUrl')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"hyphenBasedPrefix": {
"value": "[variables('hyphenBasedPrefix')]"
},
"baseTemplateUrl": {
"value": "[parameters('baseTemplateUrl')]"
},
"vnetObject": {
"value": "[variables('vnet')]"
}
}
}
},
{
"apiversion": "2017-05-10",
"name": "keyVault_resourceunit",
"type": "microsoft.resources/deployments",
"resourcegroup": "[resourcegroup().name]",
"dependsOn": [
------
],
"properties": {
"mode": "incremental",
"templatelink": {
"uri": "[variables('keyVaultTemplateUrl')]",
"contentversion": "1.0.0.0"
},
"parameters": {
"hyphenbasedprefix": {
"value": "[variables('hyphenbasedprefix')]"
},
"basetemplateurl": {
"value": "[parameters('basetemplateurl')]"
},
"keyvaultobject": {
"value": "[variables('keyvault')]"
},
"vnetObject": {
"value": "[variables('vnet')]"
}
}
}
}
]
How can i use the dependsOn property here at resource level? I did try at the last template using :
"[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]"
But its not working.
How can i use it in the 'keyVault_resourceunit' itself?
Instead of using a resource id in the dependency, try using the name of the resource object i.e. the value "vNet_ResourceUnit" from line 3 of the code in your question.
"dependsOn": [
"vNet_ResourceUnit"
]
The effect that has is to make the arm process wait until that resource deployment (named "vNet_ResourceUnit") has completely finished, before starting your keyVault_resourceunit deployment.
you need to wait for the deployment to finish, not the resources inside the deployment (because they are in a different deployment, template doesnt know anything about them).
"[resourceId('Microsoft.Resources/deployments', 'vNet_ResourceUnit')]"

Configure FirewallRules for all PossibleOutboundIpAddresses in ARM Template

I would like to create firewall rules so that only my Azure Web App can connect to my database. If possible, I'd like to do this in my ARM template. Here's what I have tried so far:
{
"variables": {
"defaultResourceName": "[resourceGroup().name]",
},
"resources": [
{
"type": "Microsoft.Web/sites/firewallRules",
"name": "[concat('AllowAzureIpAddress', copyIndex()",
"apiVersion": "2015-05-01-preview",
"properties": {
"startIpAddress": "[reference('Microsoft.Web/sites', variables('defaultResourceName')).possibleOutboundIpAddresses[copyIndex()]]",
"endIpAddress": "[reference('Microsoft.Web/sites', variables('defaultResourceName')).possibleOutboundIpAddresses[copyIndex()]]"
},
"dependsOn": [
"[resourceId('Microsoft.Sql/servers/', toLower(variables('defaultResourceName')))]"
],
"copy": {
"name": "firewallRuleCopy",
"count": "[length(reference('Microsoft.Web/sites', variables('defaultResourceName')).possibleOutboundIpAddresses)]"
}
},
]
}
The main problem is getting the PossibleOutboundIpAddresses. I'm not sure if they're available to me here, and I'm getting an error when I try to validate my ARM Template that says The template function 'reference' is not expected at this location. Please see https://aka.ms/arm-template-expressions for usage details..
Has anyone done this that has any advice for how to go about getting those OutboundIpAddresses (preferably in a list so that copy can use them)?
your problem comes not from using reference function in a wrong fashion, but from the fact that you cant use reference function in copy property (copy is evaluated at "compile time" whereas reference at runtime, so it cannot evaluate length of the copy). your possible work around is: nested deployment. here's what I've been using:
{
"name": "firewallRules",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2015-01-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "https://paste.ee/d/Hkebg/0",
"contentVersion": "1.0.0.0"
},
"parameters": {
"prefix": {
"value": "[variables('prefix')]"
},
"iterator": {
"value": "[split(reference(concat(parameters('prefix'), '-', parameters('webAppNames').name), '2016-03-01', 'Full').properties.possibleOutboundIpAddresses, ',')]"
}
}
}
},

Azure availability zone parameter syntax

I'm trying to parameterise a VM deployment that uses availability zones. However, I keep receiving this error on deployment:
'The provided value for the template parameter 'availabilityZoneParameter' at line '1' and column '5118' is not valid.'
or:
"Deployment template parse failed: 'Error converting value \"[ '1' ]\" to type 'System.String[]'. Path ''.'."
The parameter file syntax is currently:
"availabilityZoneParameter": {
"value": "[ '1' ]"
}
I am then porting it in as a parameter and turning it into a variable, before exporting it to other linked templates as well as using it in the initial build template.
Parameter in deploy file syntax:
"availabilityZoneParameter": {
"type": "string"
}
Variable in original deploy file syntax:
"availabilityZone": "[parameters('availabilityZoneParameter')]"
Disk creation syntax in original deploy file:
{
"name": "[variables('diskName')]",
"type": "Microsoft.Compute/disks",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"zones": [ "[variables('availabilityZone')]" ],
"sku": {
"name": "Standard_LRS"
},
"properties": {
"creationData": {
"createOption": "Empty"
},
"diskSizeGB": 1023
}
},
VM parameter in original deploy template, which feeds into linked template:
"name": "PAN-VM",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"dependsOn": [
"[concat('Microsoft.Compute/disks/', variables('diskName'))]",
"Microsoft.Resources/deployments/SettingUpVirtualNetwork",
"Microsoft.Resources/deployments/SettingUpPublicIP",
"Microsoft.Resources/deployments/SetupNetworkInterfaces"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(variables('virtualMachineTemplate'), parameters('artifactsLocationSasToken'))]",
"contentVersion": "1.0.0.5"
},
"parameters": {
"avZone": {
"value": "[variables('availabilityZone')]"
VM template parameter:
"avZone": {
"type": "string"
VM template variable:
"variables": {
"apiVersion": "2018-04-01",
"availabilityZone": "[parameters('avZone')]"
VM template resource (calling parameter):
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"zones": "[variables('availabilityZone')]",
"plan": {
"name": "[parameters('imageSku')]",
"product": "[parameters('imageOffer')]",
"publisher": "[parameters('imagePublisher')]"
},
"properties":
For context - there are several files at play here. An initial azureparameters file, an azuredeploy file, and then at least two linked templates which also rely on the availability zone value.
Any advice on the correct syntax?
According to the example I've found online, it should be like this:
"availabilityZoneParameter": {
"value": [ "1" ]
}
also, it should be array:
"availabilityZoneParameter": {
"type": "array"
}
As it excepts an array, not a string that looks like an array:
https://github.com/Azure/azure-quickstart-templates/blob/master/101-vm-simple-zones/azuredeploy.json#L176
Should the parameter just be ?
"availabilityZoneParameter": {
"value": "1"
}
Final syntax, for those coming to this board seeking the same answer:
Note that the value is an array and not a string, as pointed out by contributor 4c74356b41 in this thread.
In original azureparameter file:
},
"availabilityZone": {
"value": [ "3" ]
}
In azuredeploy file:
},
"availabilityZone": {
"type": "array"
}
To call the availability zone parameter in nested template (example using storage disk resource):
"name": "[variables('diskName')]",
"type": "Microsoft.Compute/disks",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"zones": "[parameters('availabilityZone')]",
"sku": {
If using a linked template, when expressing the linked template parameters, I used this syntax:
"avZone": {
"value": "[parameters('availabilityZone')]"
Importing the parameter in the linked template:
},
"avZone": {
"type": "array"
}
And then in the resources within the linked template, I called the parameter in the same way as the azuredeploy template:
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[parameters('location')]",
"zones": "[parameters('avZone')]",
As you can see, I decided not to turn it into a variable as this was unnecessary in my case.

Inline object as a parameter value to a linked template deployment

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'))]"
}
}
}

Can't create and reference a keyvault secret in the same ARM template deployment

as part of a deployment I'm doing I want to deploy a Key vault into a resource group, create some secrets and then deploy a SQL server into another resource group using one of these secrets as the admin password. The following is a snippet of the resources I am using to do this:
{
"type": "Microsoft.KeyVault/vaults",
"name": "[variables('KeyVaultName')]",
"apiVersion": "2015-06-01",
"location": "[resourceGroup().location]",
"properties": {
"enabledForTemplateDeployment": "true",
"accessPolicies": "[variables('KeyVaultAccessPolicies')]",
"tenantId": "[parameters('TenantId')]",
"sku": {
"name": "Standard",
"family": "A"
}
}
},
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[concat(variables('KeyVaultName'), '/secretname')]",
"apiVersion": "2015-06-01",
"properties": {
"value": "<a randomised value>"
},
"dependsOn": [
"[concat('Microsoft.KeyVault/vaults/', variables('KeyVaultName'))]"
]
},
{
"apiVersion": "2017-05-10",
"name": "deploy",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "<another resource group>",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "<my linked template>",
"contentVersion": "1.0.0.0"
},
"parameters": {
"SQLServerAdminPasswordSecretName": {
"value": "secretname"
}
}
},
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('KeyVaultName'), 'secretname')]"
]
}
My linked template is also has a deployment which looks a bit like the following:
{
"apiVersion": "2017-05-10",
"name": "sql-server-deployment",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "<another linked tempalate>",
"contentVersion": "1.0.0.0"
},
"parameters": {
"SQLServerName": {
"value": "[variables('SQLServerName')]"
},
"SQLServerAdminPassword": {
"reference": {
"keyVault": {
"id": "[resourceId(parameters('ParentResourceGroupName'), 'Microsoft.KeyVault/vaults', parameters('ParentKeyVaultName'))]"
},
"secretName": "[parameters('SQLServerAdminPasswordSecretName')]"
}
}
}
}
}
This reference is currently killing my entire deployment saying:
"The specified KeyVault '/subscriptions/[subscription
id]/resourceGroups/[parent resource
group]/providers/Microsoft.KeyVault/vaults/[my keyvault]' could not be
found."
If I remove the Keyvault reference in my linked template the error goes away.
What is confusing to me is that the linked template deployment depends on the key vault secret, which in turn depends on the key vault, yet nothing is being deployed because the key vault reference is trying to be resolved before anything else.
Am I missing something obvious or is this a flaw in ARM templates?
Thanks in advance
Parameter references are evaluated at "compile" time (i.e. validation), so the vault does need to exist when the template is validated. You might be able to pull this off by nesting a few levels deep and using output references from the nested deployment. You'd need to do something like this:
Nest the vault setup and output the vaultId
nested the SQL Server deployment into a "level 1" nesting
in the parameters for the above bullet, reference the output in step 1
nest another deployment with the parameter reference constructed from the vaultId in the "level 1" nesting
That said, I'm interested in why you'd want to do this... normally the vault reference is used to keep a secret out of plain text/json. Since you're passing the secret value into the deployment at the top level, you already have the value. There's no reason to reference it from the vault since you already have it...

Resources