Does the ARM template offers a way to define a JSON object inline, as a template function parameter?
Something that could look like this, where I am mixing references, Azure template functions and JSON object.
"value": "[concat(reference('ArrayMaker').outputs.fooBarArray.value],
[{ "cat": "Tom", "mouse" : "Jerry"}, { "cat":"Garfield", "mouse":"[reference('MouseTrap').outputs.mouseTrap.value]"} ] )]"
Using variables would seem to be a natural fit for this, but since the value is constructed from a reference, variables can't be used.
well, not natively. you can hack around with nested deployments, something like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"garfiled": {
"type": "string"
},
"catData": {
"type": "object",
"defaultValue": {
"cat": "Tom"
}
}
},
"variables": {
"cat": { <<< if you can\need to construct whole variable in the nested template
"cat": "Garfield",
"mouse": "[parameters('garfiled')]"
},
"t&j": { <<< if you need to pass in part of the variable to the nested template, you can also create another variable to create an object of a proper structure to union with existing object
"mouse": "Jerry"
}
},
"resources": [],
"outputs": {
"garfiled": {
"type": "object",
"value": "[variables('cat')]"
},
"t&j": {
"type": "object",
"value": "[union(variables('t&j'), parameters('catData'))]"
}
}
}
You would then use nested template to pass reference to this template and output the result.
Related
I have an ARM template which syncs secret value from source Keyvault into Destination one.
I also want to sync secret tags, but ARM reference that I use for 'sourceKV.secret.tags' retrieval does not work
[reference(resourceId('subscriptionId', 'resourceGroup', 'Microsoft.KeyVault/vaults/secrets', 'SourceKV', 'Secret'), '2021-04-01-preview', 'Full').tags.tagName]
any ideas what can be the issue, or what is the correct form to retrieve tags during ARM template deployment?
These work for me:
"outputs": {
"tags": {
"type": "string",
"value": "[reference('/subscriptions/xxxx/resourceGroups/yyyy/providers/Microsoft.KeyVault/vaults/zzzz/secrets/mysecret', '2022-07-01', 'Full').tags]"
},
"tagValue": {
"type": "string",
"value": "[reference('/subscriptions/xxxx/resourceGroups/yyyy/providers/Microsoft.KeyVault/vaults/zzzz/secrets/mysecret', '2022-07-01', 'Full').tags.hello]"
},
"tagValue2": {
"type": "string",
"value": "[reference(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.KeyVault/vaults/secrets', 'xxxx', 'mysecret'), '2021-04-01-preview', 'Full').tags.hello]"
}
}
Will result in:
"outputs": {
"tagValue": {
"type": "String",
"value": "world"
},
"tagValue2": {
"type": "String",
"value": "world"
},
"tags": {
"type": "Object",
"value": {
"hello": "world"
}
}
}
Also works with the API version you used. It is important that you use 'Full', otherwise you won't get the tags. Note that you can use this syntax anywhere in your template. I just used it in the outputs because it is good for testing.
As I found out it is not possible to use Reference function for setting tags property value for keyvault as valid usages state
reference func only works if it is used inside properties block or for outputs; but as tags are not part of properties instead of returning value reference fun returns just string "reference(resource...)"
I am looking for a way to convert an array (e.g. of strings) into one object, where the properties are generated from the array values.
Use case: I want to generate a tags object with links to resources, based on a list of resource names. I need to do this, to link App Service resources to an Application Insights resource.
The list of resources could be supplied using a parameter:
"parameters": {
"appServices": {
"type": "array",
"metadata": {
"description": "Names of app services to link this application insights resource to via hidden tags"
}
}
}
Sample input:
['appName1', 'appName2', 'appName3']
Sample output:
"tags":
{
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName1'))]": "Resource",
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName2'))]": "Resource",
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName3'))]": "Resource"
}
I know you can use copy to loop over arrays but that will create an array of objects and not a single object (which is required for tags), for example:
[
{
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName1'))]": "Resource"
},
{
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName2'))]": "Resource"
},
{
"[concat('hidden-link:', resourceId('Microsoft.Web/sites/', 'appName3'))]": "Resource"
}
]
It would be possible to use union to merge those objects again, but that function requires you to hardcode the objects you want to merge, so it does not work when you have an input with variable length.
What I am looking for is a way to do this in a dynamic way.
There is no direct option to convert array to object.
But here's a hack to achieve what you need. This will work for array of any length.
Steps:
append hidden-link text to service names
convert array to string
replace necessary symbols and make it a valid json string.
use json() to convert string to object
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appServices": {
"type": "array",
"metadata": {
"description": "Names of app services to link this application insights resource to via hidden tags"
},
"defaultValue": [ "appName1", "appName2", "appName3" ]
}
},
"functions": [],
"variables": {
"copy": [
{
"name": "as",
"count": "[length(parameters('appServices'))]",
"input": "[concat('hidden-link:', resourceId('Microsoft.Web/sites/', parameters('appServices')[copyIndex('as')]))]"
}
],
"0": "[string(variables('as'))]",
"1": "[replace(variables('0'), '[', '{')]",
"2": "[replace(variables('1'), '\",', '\":\"Resource\",')]",
"3": "[replace(variables('2'), '\"]', '\":\"Resource\"}')]"
},
"resources": [],
"outputs": {
"op1": {
"type": "object",
"value": "[json(variables('3'))]"
}
}
}
Now that lambdas have been added to bicep you can convert arrays to objects using reduce. Note that the array you reduce must consist of object items. If it doesn't you can convert it to an object using map.
// With array of objects
var names = [
{
name: 'foo'
id: 'foo-id'
}
{
name: 'bar'
id: 'bar-id'
}
]
var nameIds = reduce(names, {}, (cur, next) => union(cur, {
'${next.name}': next.id
}))
output test object = nameIds
// With array of strings
var names = [
'foo'
'bar'
]
var nameMaps = map(names, (name) => {name: name})
var nameIds = reduce(nameMaps, {}, (cur, next) => union(cur, {
'${next.name}': next.name
}))
output test object = nameIds
I'm not sure if this is the best approach to this problem.
Tags are supposed to be metadata about a specific object/service. Wouldn't it make more sense to apply a tag (say your system name, environment, etc..) and then run a query against azure on that tag?
This should achieve the same result pulling back all related resources.
I don't know if it is still relevant, but since 2021 it is possible to do with items() function
Is there any way to link nested template for parameters in azure ARM templates?
"variables": {
"templatelinkappinsight": "https://xxxxx.blob.net/appinsights.json"
},
For example: I have parameter section defined like following, i want to do nested template linking like variables section as mention above for parameters.
"parameters": {
"functionAppName": {
"value": "test"
},
"applicationInsightsName": {
"value": "test"
},
}
I suspect it's not possible to do what I'm looking for but it's worth a shot!
I have a pipeline for provisioning Azure log query alert rules. The individual alert rules are defined as ARM parameter files, and I use a shared ARM template file to deploy them.
Here's a stripped down version of my template file with most of the parameters omitted.
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"logQuery": {
"type": "string",
"minLength": 1,
"metadata": {
"description": "Query to execute against the AI resource"
}
}
},
"variables": {
"appInsightsResourceId": "[concat(resourceGroup().id,'/providers/','microsoft.insights/components/', parameters('appInsightsResourceName'))]",
"actionGroupId": "[concat(resourceGroup().id,'/providers/','microsoft.insights/actionGroups/', parameters('actionGroupName'))]",
"linkToAiResource" : "[concat('hidden-link:', variables('appInsightsResourceId'))]"
},
"resources":[{
"name":"[parameters('alertName')]",
"type":"Microsoft.Insights/scheduledQueryRules",
"location": "northeurope",
"apiVersion": "2018-04-16",
"tags": {
"[variables('linkToAiResource')]": "Resource"
},
"properties":{
"description": "[parameters('alertDescription')]",
"enabled": "[parameters('isEnabled')]",
"source": {
"query": "[parameters('logQuery')]",
"dataSourceId": "[variables('appInsightsResourceId')]",
"queryType":"[parameters('logQueryType')]"
},
"schedule":{
"frequencyInMinutes": "[parameters('alertSchedule').Frequency]",
"timeWindowInMinutes": "[parameters('alertSchedule').Time]"
},
"action":{
"odata.type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.Microsoft.AppInsights.Nexus.DataContracts.Resources.ScheduledQueryRules.AlertingAction",
"severity": "[parameters('alertSeverity')]",
"aznsAction":{
"actionGroup":"[array(variables('actionGroupId'))]"
},
"trigger":{
"thresholdOperator":"[parameters('alertTrigger').Operator]",
"threshold":"[parameters('alertTrigger').Threshold]"
}
}
}
}
]
}
You can see how I'm providing the App Insights query as a parameter, so my parameters file could look something like:
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"logQuery": {
"value": "requests | where resultCode >= 500"
}
}
}
However, these queries can be very long and hard to understand when viewing as an unbreakable JSON string. So I want to parametize this parameter (if you know what I mean) so that the key variables are defined and supplied separately. I was thinking about changing the parameters to something like this, introducing a new parameter holding an array of placeholder replacements for the parametized query...
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"logQueryVariables": [
{ "{minCode}": "500" }
],
"logQuery": {
"value": "requests | where resultCode >= {minCode}"
}
}
}
...then finding a way to iterate over the variables array and replace the placeholders in the logQuery parameter, I thought maybe I could use an ARM function or something. But I'm afraid to admit I'm stuck with this part. Is it possible to use the copy syntax to do something like this?
depends on what the end result should look like you can do this different ways, but I'd suggest against doing this in the template and suggest you do this outside of the template and feed in the result. If you really want to achieve exactly what you describe you can do that** with nested deployments. I dont think there is any other way of iterating through an array to build a string in ARM Templates.
This is the closest I could come. We do the input looping in powershell for secretObjects.
{
"type": "Microsoft.KeyVault/vaults/secrets",
"name": "[concat(variables('KeyVaultName'), '/',
variables('secretsObject').secrets[copyIndex()].secretName)]",
"apiVersion": "2018-02-14",
"dependsOn": [
"[concat('Microsoft.KeyVault/vaults/', variables('KeyVaultName'))]"
],
"copy": {
"name": "secretsCopy",
"count": "[length(variables('secretsObject').secrets)]"
},
"properties": {
"value": "[variables('secretsObject').secrets[copyIndex()].secretValue]"
}
}
When deploying an ARM template using resource iteration, I'd like to pass the resource properties as an object.
Doing this would allow for a different set of parameters to exist within each element the copy array. The reason for this is because some properties may need to be conditionally included or excluded depending on the values of others. For example, in the case of an API Management product, the documentation states the following with regard to the subscriptionsLimit property -
Can be present only if subscriptionRequired property is present and has a value of false.
However, when deploying the example template below the deployment hangs in Azure. Looking in to the related events, I can see that the action the deploy the resource keeps failing with an Internal Server Error (500), but there are no additional details.
If I refer to each parameter in the properties object using variables('productsJArray')[copyIndex()].whatever then the deployment succeeds. However, this is undesirable as it means that every properties object would have to contain identical parameters, which is not always permissible and may cause the deployment to fail.
Example template
Note that I've output variables('productsJArray')[copyIndex()] and it is a valid object. I've even copied the output in to the template and deployed it successfully.
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"apiManagementServiceName": {
"type": "string",
"metadata": {
"description": "The name of the API Management instance."
}
},
"productsJson": {
"type": "string",
"metadata": {
"description": "A JSON representation of the Products to add."
}
}
},
"variables": {
"productsJArray": "[json(parameters('productsJson'))]"
},
"resources": [
{
"condition": "[greater(length(variables('productsJArray')), 0)]",
"type": "Microsoft.ApiManagement/service/products",
"name": "[concat(parameters('apiManagementServiceName'), '/', variables('productsJArray')[copyIndex()].name)]",
"apiVersion": "2018-06-01-preview",
"properties": "[variables('productsJArray')[copyIndex()]]",
"copy": {
"name": "productscopy",
"count": "[if(greater(length(variables('productsJArray')), 0), length(variables('productsJArray')), 1)]"
}
}
]
}
Example parameters
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"apiManagementServiceName": {
"value": "my-api-management"
},
"productsJson": {
"value": "[{\"name\":\"my-product\",\"displayName\":\"My Product\",\"description\":\"My product is awesome.\",\"state\":\"published\",\"subscriptionRequired\":true,\"approvalRequired\":false}]"
}
}
}
Output of variable 'productsJArray[0]'
"outputs": {
"properties": {
"type": "Object",
"value": {
"approvalRequired": false,
"description": "My product is awesome.",
"displayName": "My Product",
"name": "my-product",
"state": "published",
"subscriptionRequired": true
}
}
}
The issue here was that I was passing including name parameter along with other parameters when setting resource properties. This is obviously wrong, but it would have been helpful if MS had handled the error in a more human friendly way (guess they can't think of everything).
I've updated my incoming productsJson parameter -
[{\"name\":\"cs-automation\",\"properties\":{\"displayName\":\"CS Automation Subscription\",\"state\":\"published\",\"description\":\"Allows access to the ConveyorBot v1 API.\",\"subscriptionRequired\":true,\"approvalRequired\":false}}]
And I'm now passing only the required 'properties' -
"resources": [
{
"type": "Microsoft.ApiManagement/service/products",
"name": "[concat(parameters('apiManagementServiceName'), '/', variables('productsJArray')[copyIndex()].name)]",
"apiVersion": "2018-06-01-preview",
"properties": "[variables('productsJArray')[copyIndex()].properties]"
}
]