Nested resources for nested resources - azure

I am building an ARM template that creates multiple storage accounts and each one of them will contain multiple containers “blobs” but apparently still not supported.
Is there any other way to do this beside specify each of them separately?
example of what I am trying to achieve:
StorageAcct_1: must contain 10 blobs
StorageAcct_2 : must contain 6 blobs
I am not able to achieve that without duplicating my storage account and container templates.

You can do it - there are multiple ways (nesting, inline, variable loops), it really depends on what you want the code to look like and what your input format is... but a simple n*m loop could use this:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"variables": {
"numberOfAccounts": 2,
"blobsPerAccount": 3,
"saprefix": "[uniqueString(resourceGroup().id)]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}', variables('saprefix'), copyIndex())]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"copy": {
"name": "storageAccountLoop",
"count": "[variables('numberOfAccounts')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}/default', variables('saprefix'), copyIndex())]",
"copy": {
"name": "blobServiceLoop",
"count": "[variables('numberOfAccounts')]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', format('{0}{1}', variables('saprefix'), copyIndex()))]"
]
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-08-01",
"name": "[format('{0}{1}/{2}/{3}{4}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts')), 'default', 'container', mod(copyIndex(), variables('blobsPerAccount')))]",
"copy": {
"name": "containerLoop",
"count": "[mul(variables('numberOfAccounts'), variables('blobsPerAccount'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', format('{0}{1}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts'))))]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', format('{0}{1}', variables('saprefix'), mod(copyIndex(), variables('numberOfAccounts'))), 'default')]"
]
}
]
}
That help?

Related

Arm templates copy loop conditionally check index

Is there option copy iteration check conditionally in ARM templates? Example if copy index is zero set another value?
My ARM Code:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"storageAccountName": {
"type": "string"
},
"mediaServicesAccountName": {
"type": "string"
}
},
"functions": [],
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-01-01",
"name": "[concat('storage', copyIndex(), uniqueString(resourceGroup().id))]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"copy": {
"name": "storagecopy",
"count": 3
}
},
{
"type": "Microsoft.Media/mediaservices",
"apiVersion": "2020-05-01",
"name": "[parameters('mediaServicesAccountName')]",
"location": "[parameters('location')]",
"properties": {
"storageAccounts": [
{
"type": "Primary", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex(), uniqueString(resourceGroup().id)))]"
}
]
},
"identity": {
"type": "SystemAssigned"
},
"dependsOn": ["storagecopy"]
}
],
"metadata": {
"_generator": {
"name": "bicep",
"version": "0.3.126.58533",
"templateHash": "2006367938138350540"
}
}
}
In above code I am creating 3 storage accounts and after that I am creating azure media service, I need to map storage accounts to azure media service dynamically. Under properties, I need to use copy loop and set Primary if index is zero else Secondary for defined number of storages.
Below Block implementation is required for copy loop condition:
"storageAccounts": [
{
"type": "Primary", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex(), uniqueString(resourceGroup().id)))]"
}
]
You need to make a separate loop over storageAccounts property:
{
"type": "Microsoft.Media/mediaservices",
"apiVersion": "2020-05-01",
...
"properties": {
"copy": [
{
"name": "storageAccounts",
"count": "3",
"input": {
"type": "[if(equals(copyIndex('storageAccounts'), 0), 'Primary', 'Secondary']", # Primary if copyIndex is zero otherwise Secondary
"id": "[resourceId('Microsoft.Storage/storageAccounts', concat('storage', copyIndex('storageAccounts'), uniqueString(resourceGroup().id)))]"
}
}
]
}
}
See more information here: Property iteration in ARM templates

Parameter with nested array - is iteration possible?

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.

Azure RM Template. Deploy copy VM with unique secret from Key Vault

I would like to be able to create VMs amount of which I specify via parameters (achieved by copy) with different secret for each VM (ex. secret1 for VM1, secret2 for VM2, etc.) Here is a basic example of copy VM template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"numberOfVMs": {
"type": "int",
"defaultValue": 1,
"minvalue": 1
},
"vmAdminUserName": {
"type": "string",
"minLength": 1
},
"vmAdminPassword": {
"type": "securestring"
}
},
"variables": {
"storageAccountName": "[concat('stor567', uniqueString(resourceGroup().id))]",
"storageAccountType": "Standard_LRS",
"vmWindowsOSVersion": "2016-Datacenter",
"vnetPrefix": "10.0.0.0/16",
"vnetSubnet1Name": "Subnet-1",
"vnetSubnet1Prefix": "10.0.0.0/24",
"nicVnetID": "[resourceId('Microsoft.Network/virtualNetworks', 'vnet')]",
"nicSubnetRef": "[concat(variables('nicVnetID'), '/subnets/', variables('vnetSubnet1Name'))]",
"vmImagePublisher": "MicrosoftWindowsServer",
"vmImageOffer": "WindowsServer",
"vmVmSize": "Standard_DS1_v2",
"vmVnetID": "[resourceId('Microsoft.Network/virtualNetworks', 'vnet')]",
"vmSubnetRef": "[concat(variables('vmVnetID'), '/subnets/', variables('vnetSubnet1Name'))]",
"vmStorageAccountContainerName": "vhds"
},
"resources": [
{
"name": "[variables('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "2015-06-15",
"dependsOn": [ ],
"properties": {
"accountType": "[variables('storageAccountType')]"
}
},
{
"name": "vnet",
"type": "Microsoft.Network/virtualNetworks",
"location": "[resourceGroup().location]",
"apiVersion": "2016-03-30",
"dependsOn": [ ],
"tags": {
"displayName": "vnet"
},
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('vnetPrefix')]"
]
},
"subnets": [
{
"name": "[variables('vnetSubnet1Name')]",
"properties": {
"addressPrefix": "[variables('vnetSubnet1Prefix')]"
}
}
]
}
},
{
"name": "[concat('NIC',copyindex())]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[resourceGroup().location]",
"copy": {
"name": "nicLoop",
"count": "[parameters('numberOfVMs')]"
},
"apiVersion": "2016-03-30",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', 'vnet')]"
],
"tags": {
"displayName": "nic"
},
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('nicSubnetRef')]"
}
}
}
]
}
},
{
"name": "[concat('VM',copyindex())]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[resourceGroup().location]",
"copy": {
"name": "virtualMachineLoop",
"count": "[parameters('numberOfVMs')]"
},
"apiVersion": "2015-06-15",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
"nicLoop"
],
"tags": {
"displayName": "vm"
},
"properties": {
"hardwareProfile": {
"vmSize": "[variables('vmVmSize')]"
},
"osProfile": {
"computerName": "[concat('VM',copyindex())]",
"adminUsername": "[parameters('vmAdminUsername')]",
"adminPassword": "[parameters('vmAdminPassword')]"
},
"storageProfile": {
"imageReference": {
"publisher": "[variables('vmImagePublisher')]",
"offer": "[variables('vmImageOffer')]",
"sku": "[variables('vmWindowsOSVersion')]",
"version": "latest"
},
"osDisk": {
"name": "vmOSDisk",
"vhd": {
"uri": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2016-01-01').primaryEndpoints.blob, variables('vmStorageAccountContainerName'), '/', 'VM',copyIndex(),'-','OSdisk.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', concat('NIC',copyindex()))]"
}
]
}
}
}],
"outputs": {}
}
However, I'm struggling to integrate using of password as unique secrets from Key Vault in that template. If I use example from official documentation Reference a secret with static id VMs with secret1 for each VM will be created. And I can’t wrap Reference a secret with dynamic id into nested template because that would deploy my copied VMs again and again for the each number of VMs I would like to deploy. Please help me understand, how this challenge can be solved?
Links: Parent and Nested.
I'm not sure if that's what you meant (because i still think that i struggle to understand your problem).
These templates allow to deploy variable amount of vm's and use different keyvault keys as passwords for those. Example:
2 Windows VM's with one secret and 3 Ubuntu VM's with another
1 Windows VM with one secret and 4 Ubuntu VM's with another
You can easily extend that to other images, like centos.
As you can probably see after looking at the templates I'm using arrays and copyindex() to feed proper values where they belong.
Tell me if that's not what you are after. Be careful when using those, github raw links use some form of caching, so deploying from github might not work for you with errors, in that case just use the links I've provided (NOT RAW) to copy to local machine and upload to some service like pastebin, and deploy from there.

Create Azure blob/fileshare container through ARM template

I am looking a way to create a container in Azure blob & file-share storage through ARM template.
At present I have ARM template to provision the storage accounts, but I want to create containers also in ARM.
{
"name": "[parameters('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"location": "[resourceGroup().location]",
"apiVersion": "[variables('storageApiVersion')]",
"sku": {
"name": "[variables('storageAccountType')]"
},
"dependsOn": [ ],
"tags": {
"Environment": "[parameters('Environment')]",
"Project": "[parameters('ProjectName')]",
"Contact": "[parameters('ContactName')]"
},
"kind": "Storage",
"properties": {
"encryption": {
"keySource": "Microsoft.Storage",
"services": {
"blob": {
"enabled": true
}
}
}
}
}
It is possible. Azure Management REST Api now has endpoints for Blob Containers: https://learn.microsoft.com/en-us/rest/api/storagerp/blobcontainers/create.
Since ARM Templates map to REST requests, we can create the following Template, containing a Blob Container as a nested resource below the Storage Account. Of course, you can also describe the Blob container in the toplevel resource array, following the usual rules.
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {
"accountName": "accountname",
"containerName": "containername"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[variables('accountName')]",
"apiVersion": "2018-02-01",
"location": "westeurope",
"kind": "BlobStorage",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"tags": {},
"dependsOn": [],
"properties": {
"accessTier": "Cool"
},
"resources": [
{
"type": "blobServices/containers",
"apiVersion": "2018-03-01-preview",
"name": "[concat('default/', variables('containerName'))]",
"dependsOn": [
"[variables('accountName')]"
],
"properties": {
"publicAccess": "None"
}
}
]
}
]
}
No, you cant do that, consult this feedback item.
you can now create containers. https://stackoverflow.com/a/51608344/6067741

Sharing web site config across deployment slots using an ARM template

I'm provisioning several webapps in an ARM template, and I'm finding I'm having to have a lot of duplicated code to maintain a single config across multiple deployment slots. Both dependencies and properties have to be duplicated and maintained separately. I looked into using a variable, but a lot of my config is dependent on other resources and cannot be evaluated at the time variables are evaluated.
Ideally I'd like all slots to reference the same 'Microsoft.Web/sites/config' object, but I can't see a way to do that. My current deployment script looks like this (although this has been massively simplified, I have significantly more properties in reality)
{
"name": "[variables('siteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"apiVersion": "2015-08-01",
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', variables('serverfarm'))]",
"[resourceid('Microsoft.EventHub/namespaces', variables('fullEventHubNameSpace'))]"
],
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('siteName'))]": "Resource"
},
"properties": {
"name": "[variables('siteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('serverfarm'))]",
"siteConfig": {
"AlwaysOn": true
}
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('siteName'))]",
"[concat('Microsoft.Insights/components/', variables('appInsightsSiteName'))]",
"[concat('Microsoft.Web/sites/', variables('otherSiteName'))]",
"[concat('Microsoft.DocumentDb/databaseAccounts/',variables('databaseAccountName'))]",
"[resourceid('Microsoft.EventHub/namespaces', variables('fullEventHubNameSpace'))]"
],
"properties": {
"APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', variables('appInsightsName'))).InstrumentationKey]",
"KEYVAULT_PATH": "[parameters('keyVaultPath')]",
"KEYVAULT_SECRET": "[parameters('keyVaultSecret')]",
"OTHER_SITE": "[reference(concat('Microsoft.Web/sites/', variables('otherSiteName'))).hostnames[0]]",
"DB_KEY": "[listKeys(resourceId(concat('Microsoft.DocumentDb/databaseAccounts'),variables('databaseAccountName')),'2015-04-08').primaryMasterKey]",
}
},
{
"apiVersion": "2015-08-01",
"name": "Staging",
"type": "slots",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', variables('siteName'))]"
],
"properties": {
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', variables('siteName'))]",
"[concat('Microsoft.Insights/components/', variables('appInsightsName'))]",
"[concat('Microsoft.DocumentDb/databaseAccounts/',variables('databaseAccountName'))]",
"[concat('Microsoft.Web/sites/', variables('otherSiteName'))]",
"[resourceid('Microsoft.EventHub/namespaces', variables('fullEventHubNameSpace'))]",
"Staging"
],
"properties": {
"APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', variables('appInsightsName'))).InstrumentationKey]",
"KEYVAULT_PATH": "[parameters('keyVaultPath')]",
"KEYVAULT_SECRET": "[parameters('keyVaultSecret')]",
"OTHER_SITE": "[reference(concat('Microsoft.Web/sites/', variables('otherSiteName'))).hostnames[0]]",
"DB_KEY": "[listKeys(resourceId(concat('Microsoft.DocumentDb/databaseAccounts'),variables('databaseAccountName')),'2015-04-08').primaryMasterKey]",
}
}
]
}
]
},
Is there any way to make this template more maintainable?
maybe you can take use of copy in your Template.
Move the section with your slot to root level in your template and add:
"copy": {
"name": "slotcopy",
"count": "[length(parameters('webSiteSlots'))]"
},
The name propperty shlould look like this:
"name": "[concat(parameters('webSiteName'), '/', parameters('webSiteSlots')[copyIndex()].name)]",
With this you say that this resource will be a child of the WebSite resource. More information about this here.
now you can add a parameter with a complex object array to your template:
"webSiteSlots": {
"type": "array",
"minLength": 0,
"defaultValue": []
}
in your parameters file you can now set a collection of slots you want to have with some additional values:
"webSiteSlots": {
"value": [
{
"name": "Staging",
"environment": "Production"
}
]
}
to use these given values you can do something like this:
"properties": {
"ASPNETCORE_ENVIRONMENT": "[parameters('webSiteSlots')[copyIndex()].environment]"
}
This should reduce the dublicated code a fair bit.
Greetings,
KirK

Resources