Multiple values for a property on resource in Azure ARM Template - json

According to https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iteration it should be possible to create multiple values on a resource using copy but I can't make it work. Here is my code...
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appServiceName": {
"type": "string",
"metadata": {
"description": "Name of app service to apply SSL to."
}
},
"certificateName": {
"type": "string",
"metadata": {
"description": "User friendly certificate resource name"
}
},
"appServicePlan": {
"type": "string",
"metadata": {
"description": "App Service Plan Name"
}
},
"keyVaultId": {
"type": "string",
"metadata": {
"description": "Existing Key Vault resource Id with an access policy to allow Microsoft.Web RP to read Key Vault secrets (Checkout README.md for more information)"
}
},
"hostname": {
"type": "array",
"metadata": {
"description": "Custom hostname for creating SSL binding. This hostname should already be assigned to the Web App"
}
}
},
"resources": [
{
"apiVersion": "2017-03-30",
"type": "Microsoft.Web/sites",
"name": "[parameters('appServiceName')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Web/certificates', parameters('certificateName'))]"
],
"properties": {
"copy": [
{
"name": "hostnames",
"count": "[length(parameters('hostname'))]",
"input": {
"name": "[copyIndex('hostnames')]",
"properties": {
"hostNameSslStates": [
{
"name": "[[copyIndex(hostname)]]",
"sslState": "SniEnabled",
"thumbprint": "[reference(resourceId('Microsoft.Web/certificates', parameters('certificateName'))).Thumbprint]",
"toUpdate": true
}
]
}
}
}
]
}
},
{
"type": "Microsoft.Web/certificates",
"name": "[parameters('certificateName')]",
"apiVersion": "2016-03-01",
"location": "[resourceGroup().location]",
"properties": {
"keyVaultId": "[parameters('keyVaultId')]",
"keyVaultSecretName": "[parameters('certificateName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms',parameters('appServicePlan'))]"
}
}
]
}
And it returns Error: Code=InvalidTemplate; Message=Deployment template l
anguage expression evaluation failed: 'Unable to parse language expression 'copyIndex(hostname)]': expecte
d token 'LeftParenthesis' and actual 'RightParenthesis'.'. Please see https://aka.ms/arm-template-expressi
ons for usage details.
Any ideas what am I doing wrong?
Thanks in advance!

the error gives is away:
change "[[copyIndex(hostname)]]", to "[copyIndex('hostname')]"
you even have it right in other places, why not here?
and you probably want to do this:
"[parameters('hostname')[copyIndex('hostname')]]"

Related

Loop for nested template and dynamically get keyvault secret name

Based on information from Microsoft site, it is possible get key vault secrets during the implementation. I would like to have similar solution but for 5 VMs and reuse templates in Loop to create 5 VMs with different password from key vault. The key vault already exists with the secrets name. The secrets name are like: Secrets0...Secrets4.
Does anyone has any idea of how to do this? Below my solution with Loop but doesn't works for me.
I appreciate for your support.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location where the resources will be deployed."
}
},
"vaultName": {
"type": "string",
"metadata": {
"description": "The name of the keyvault that contains the secret."
}
},
"secretName": {
"type": "string",
"metadata": {
"description": "The name of the secret."
}
},
"vaultResourceGroupName": {
"type": "string",
"metadata": {
"description": "The name of the resource group that contains the keyvault."
}
},
"vaultSubscription": {
"type": "string",
"defaultValue": "[subscription().subscriptionId]",
"metadata": {
"description": "The name of the subscription that contains the keyvault."
}
}
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2020-10-01",
"name": "[concat('DynamicSecret,copyIndex('VMsLoop'))]"
"copy": {
"name": "VMsLoop",
"count": 5,
"mode": "Serial",
"batchSize": 1
},
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adminLogin": {
"type": "string"
},
"adminPassword": {
"type": "securestring"
},
"location": {
"type": "string"
}
},
"variables": {
"sqlServerName": "[concat('sql-', uniqueString(resourceGroup().id, 'sql'))]"
},
"resources": [
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2018-06-01-preview",
"name": "[variables('sqlServerName')]",
"location": "[parameters('location')]",
"properties": {
"administratorLogin": "[parameters('adminLogin')]",
"administratorLoginPassword": "[parameters('adminPassword')]"
}
}
],
"outputs": {
"sqlFQDN": {
"type": "string",
"value": "[reference(variables('sqlServerName')).fullyQualifiedDomainName]"
}
}
},
"parameters": {
"location": {
"value": "[parameters('location')]"
},
"adminLogin": {
"value": "ghuser"
},
"adminPassword": {
"reference": {
"keyVault": {
"id": "[resourceId(parameters('vaultSubscription'), parameters('vaultResourceGroupName'), 'Microsoft.KeyVault/vaults', parameters('vaultName'))]"
},
"secretName": "[concat(parameters('secretName'),copyIndex('VMsLoop'))]"
}
}
}
}
}
],
"outputs": {
}
}
The template you have provided actually works (except for one tiny typo - missing closing quote after DynamicSecret at "[concat('DynamicSecret,copyIndex('VMsLoop'))]" ).
However, since the uniquestring function that is used to generate the sql server name is deterministic (i.e. generated using the passed parameters of resoure group name and 'sql' string - uniqueString(resourceGroup().id, 'sql'). So the loop is hitting the same server instance just changing the password to the next secret in the list.
You can either promote the sqlServerName to parameter and suffix it with the loop index, or keep it as a variable and expand the uniqueString function with the deployment (i.e. uniqueString(resourceGroup().id, deployment().name, 'sql') ).

What is the error within the ARM template? Please advise

Hello I am trying to deploy this template via azure devops and get this error:
'The template resource '' of type 'microsoft.insights/workbooks' at line '1' and column '1512' is not valid. The name property cannot be null or empty. Please see https://aka.ms/arm-template/#resources for usage details.'." AdditionalInfo=[{"info":{"lineNumber":1,"linePosition":1512,"path":"properties.template.resources[0]"},"type":"TemplateViolation"}]
Template:
{
"contentVersion": "1.0.0.0",
"parameters": {
"workbookDisplayName": {
"type": "string",
"defaultValue": "Azure Firewall Workbook",
"metadata": {
"description": "The friendly name for the workbook that is used in the Gallery or Saved List. This name must be unique within a resource group."
}
},
"workbookType": {
"type": "string",
"allowedValues": [
"workbook",
"sentinel"
],
"defaultValue": "workbook",
"metadata": {
"description": "The gallery that the workbook will been shown under. Supported values include workbook, tsg, etc. Usually, this is 'workbook'"
}
},
"DiagnosticsWorkspaceName": {
"type": "string",
"defaultValue": "WorkspaceName",
"metadata": {
"description": "Provide the workspace name for your Network Diagnostic logs"
}
},
"DiagnosticsWorkspaceSubscription": {
"type": "string",
"defaultValue": "WorkspaceSubscriptionID",
"metadata": {
"description": "Provide the workspace subscription GUID for your Network Diagnostic logs"
}
},
"DiagnosticsWorkspaceResourceGroup": {
"type": "string",
"defaultValue": "ResourceGroupName",
"metadata": {
"description": "Provide the workspace resourcegroupname for your Network Diagnostic logs"
}
},
"workbookId": {
"type": "string",
"defaultValue": "[newGuid()]",
"metadata": {
"description": "The unique guid for this workbook instance"
}
}
},
"variables": {
"workbookSourceId": "[concat('/subscriptions/',parameters('DiagnosticsWorkspaceSubscription'),'/resourcegroups/', parameters('DiagnosticsWorkspaceResourceGroup'), '/providers/Microsoft.OperationalInsights/workspaces/',parameters('DiagnosticsWorkspaceName'))]"
},
"resources": [
{
"name": "[parameters('workbookId')]",
"type": "microsoft.insights/workbooks",
"location": "[resourceGroup().location]",
"apiVersion": "2018-06-17-preview",
"dependsOn": [],
"kind": "shared",
"properties": {
"displayName": "[parameters('workbookDisplayName')]"}",
"version": "1.0",
"sourceId": "[variables('workbookSourceId')]",
"category": "[parameters('workbookType')]"
}
}
],
"outputs": {
"workbookId": {
"type": "string",
"value": "[resourceId( 'microsoft.insights/workbooks', parameters('workbookId'))]"
}
},
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#"
}
Looks like there are some syntactical errors in the above shared ARM template .
"properties": {
"displayName": "[parameters('workbookDisplayName')]"}",
"version": "1.0",
"sourceId": "[variables('workbookSourceId')]",
"category": "[parameters('workbookType')]"
}
As per the Azure documentation for the Microsoft.Insights/Workbooks sourceID,Category & Version are the parameters of the properties block.
When we have loaded the above shared Json template/ARM template in our VisualStudioCode,the intellisense has found few syntactical errors & also thrown error stating serializedData parameter is mandatory in the properities block of Microsoft.Insights/Workbooks.
Here is the ARM template post fixing those errors & tried deploying in our local environment which got successfully deployed without any issues.
{
"contentVersion": "1.0.0.0",
"parameters": {
"workbookDisplayName": {
"type": "string",
"defaultValue": "Azure Firewall Workbook",
"metadata": {
"description": "The friendly name for the workbook that is used in the Gallery or Saved List. This name must be unique within a resource group."
}
},
"workbookType": {
"type": "string",
"allowedValues": [
"workbook",
"sentinel"
],
"defaultValue": "workbook",
"metadata": {
"description": "The gallery that the workbook will been shown under. Supported values include workbook, tsg, etc. Usually, this is 'workbook'"
}
},
"DiagnosticsWorkspaceName": {
"type": "string",
"defaultValue": "WorkspaceName",
"metadata": {
"description": "Provide the workspace name for your Network Diagnostic logs"
}
},
"DiagnosticsWorkspaceSubscription": {
"type": "string",
"defaultValue": "WorkspaceSubscriptionID",
"metadata": {
"description": "Provide the workspace subscription GUID for your Network Diagnostic logs"
}
},
"DiagnosticsWorkspaceResourceGroup": {
"type": "string",
"defaultValue": "ResourceGroupName",
"metadata": {
"description": "Provide the workspace resourcegroupname for your Network Diagnostic logs"
}
},
"workbookId": {
"type": "string",
"defaultValue": "[newGuid()]",
"metadata": {
"description": "The unique guid for this workbook instance"
}
}
},
"variables": {
"workbookSourceId": "[concat('/subscriptions/',parameters('DiagnosticsWorkspaceSubscription'),'/resourcegroups/', parameters('DiagnosticsWorkspaceResourceGroup'), '/providers/Microsoft.OperationalInsights/workspaces/',parameters('DiagnosticsWorkspaceName'))]"
},
"resources": [
{
"name": "[parameters('workbookId')]",
"type": "microsoft.insights/workbooks",
"location": "[resourceGroup().location]",
"apiVersion": "2018-06-17-preview",
"dependsOn": [],
"kind": "shared",
"properties" : {
"displayName": "[parameters('workbookDisplayName')]",
"sourceId": "[variables('workbookSourceId')]",
"category": "[parameters('workbookType')]",
"serializedData" : "",
"version" : "1.0"
}
}
],
"outputs": {
"workbookId": {
"type": "string",
"value": "[resourceId( 'microsoft.insights/workbooks', parameters('workbookId'))]"
}
},
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#"
}
Note:
In the above shared template, we observed that the schema version in the deployment template you are using is 2015-01-01 its always suggested to use latest version 2019-04-01
Here is the output sample for our reference:

Azure ARM Template - Running DSC script without triggering extension install?

I am trying to deploy a Active Directory forest with two DCs. I've managed to deploy the DCs and install the ADDS features on both VMs. The "PDC" had a DSC script that runs and configures the forest, again that works great. The issue I have is trying to run a second DSC script on the second DC, this script runs the ADDS configuration to promote the VM to a DC and join it to the forest. I've created a nested JSON template that gets called by the main template. But I am hitting this error:
"Multiple VMExtensions per handler not supported for OS type 'Windows'. VMExtension 'PrepareBDC' with handler 'Microsoft.Powershell.DSC' already added or specified in input."
I've spent the last hour or so whizzing around the internet looking for answers and everyone seems to say the same thing...you can't install the same extension twice. Ok, I can see why that would make sense, my question is can I configure the nested template so it doesn't try and install the extension, just uses what's already installed on the VM?
Main template snippet:
{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(variables('dc2name'), '/PrepareDC2AD')]",
"apiVersion": "2018-06-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachines', variables('dc2name'))]"
],
"properties": {
"publisher": "Microsoft.Powershell",
"type": "DSC",
"typeHandlerVersion": "2.19",
"autoUpgradeMinorVersion": true,
"settings": {
"ModulesUrl": "[concat(parameters('Artifacts Location'), '/dsc/PrepareADBDC.zip', parameters('Artifacts Location SAS Token'))]",
"ConfigurationFunction": "PrepareADBDC.ps1\\PrepareADBDC",
"Properties": {
"DNSServer": "[variables('dc1ipaddress')]"
}
}
}
},
{
"name": "ConfiguringDC2",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2016-09-01",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/',variables('dc1name'),'/extensions/CreateADForest')]",
"[concat('Microsoft.Compute/virtualMachines/',variables('dc2name'),'/extensions/PrepareDC2AD')]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[concat(parameters('Artifacts Location'), '/nestedtemplates/configureADBDC.json', parameters('Artifacts Location SAS Token'))]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"adBDCVMName": {
"value": "[variables('dc2name')]"
},
"location": {
"value": "[resourceGroup().location]"
},
"adminUsername": {
"value": "[parameters('Administrator User')]"
},
"adminPassword": {
"value": "[parameters('Administrator Password')]"
},
"domainName": {
"value": "[parameters('Domain Name')]"
},
"adBDCConfigurationFunction": {
"value": "ConfigureADBDC.ps1\\ConfigureADBDC"
},
"adBDCConfigurationModulesURL": {
"value": "[concat(parameters('Artifacts Location'), '/dsc/ConfigureADBDC.zip', parameters('Artifacts Location SAS Token'))]"
}
}
}
},
The nested template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"adBDCVMName": {
"type": "string"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"adminUsername": {
"type": "string"
},
"adminPassword": {
"type": "securestring"
},
"domainName": {
"type": "string"
},
"adBDCConfigurationFunction": {
"type": "string"
},
"adBDCConfigurationModulesURL": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(parameters('adBDCVMName'),'/PrepareBDC')]",
"apiVersion": "2016-03-30",
"location": "[parameters('location')]",
"properties": {
"publisher": "Microsoft.Powershell",
"type": "DSC",
"typeHandlerVersion": "2.21",
"autoUpgradeMinorVersion": true,
"forceUpdateTag": "1.0",
"settings": {
"modulesURL": "[parameters('adBDCConfigurationModulesURL')]",
"wmfVersion": "4.0",
"configurationFunction": "[parameters('adBDCConfigurationFunction')]",
"properties": {
"domainName": "[parameters('domainName')]",
"adminCreds": {
"userName": "[parameters('adminUsername')]",
"password": "privateSettingsRef:adminPassword"
}
}
},
"protectedSettings": {
"items": {
"adminPassword": "[parameters('adminPassword')]"
}
}
}
}
]
}
this error means exactly what it says: you cannot have multiple copies of the same extension, what you need to do is apply the same extension to the vm, all the inputs have to be the same. you can have a look at this example which does exactly that. This particular template installs the extension for the second time to join bdc to the domain.
But, I don't like that approach. I use Powershell DSC to just wait for the domain to get created and join the bdc to the domain in one go. you would use this powershell dsc snippet:
xWaitForADDomain DscForestWait {
DomainName = $DomainName
DomainUserCredential = $DomainCreds
RetryCount = $RetryCount
RetryIntervalSec = $RetryIntervalSec
}
Here's a complete example

Deploying Web App for containers using ARM templates

I have been trying to automate deployment of my resources to resource group on Azure. Right now I am using ARM templates and so far I was able to create App Insights and App Service Plan using a template. This is how it looks like:
{
"apiVersion": "2015-05-01",
"name": "[variables('servicePlan')]",
"kind": "linux",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"tags": {
"displayName": "BTC Push Notification Settings HostingPlan"
},
"sku": {
"name": "[variables('skuNamePlan')]",
"capacity": "[variables('skuSizePlan')]"
},
"properties": {
"name": "[variables('servicePlan')]"
}
},
{
"apiVersion": "2015-05-01",
"name": "[variables('appInsights')]",
"type": "Microsoft.Insights/components",
"location": "southcentralus",
"tags": {
"[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('appInsights'))]": "Resource",
"displayName": "BTC Push Notification Settings App Insights"
},
"properties": {
"applicationId": "[variables('appInsights')]"
}
}
I am having hard time creating Web App for Containers and pointing it to my docker image using ARM template. I have done it manually and it worked, likewise I did it through azure-cli like this az webapp create --resource-group ExampleGroupAlpina --plan myAppServicePlan --name DockerContainer --deployment-container-image-name this-is-my-image/sample-docker and this worked as well. I would appreciate if anyone could suggest creating this Web App for Containers using ARM Template.
For me none of these other answers worked. With the assistance of Azure support and these other answers I came up with the following template, that succesfully creates an app service plan with an Linux App Service for Containers running a custom Docker image from Azure Container Repository:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment":{
"type": "string"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location (region) for all resources."
}
},
"appServiceSku": {
"type": "string",
"defaultValue": "B1",
"metadata": {
"description": "The SKU of App Service Plan "
}
},
"dockerImageName": {
"type": "string",
"defaultValue": "_MY_REGISTRY_USERNAME_.azurecr.io/_MY_NAMESPACE_/_MY_DOCKER_IMAGE_NAME_:_TAG_"
},
"dockerRegistryUrl": {
"type": "string",
"defaultValue": "https://_MY_REGISTRY_USERNAME_.azurecr.io"
},
"dockerRegistryUsername": {
"type": "string",
"defaultValue": "_MY_REGISTRY_USERNAME_"
},
"dockerRegistryPassword": {
"type": "string",
"defaultValue": "_MY_REGISTRY_PSW_"
},
"_artifactsLocation": {
"type": "string"
},
"_artifactsLocationSasToken": {
"type": "securestring"
}
},
"variables": {
"name": "projectname-",
"webAppPortalName": "[concat(variables('name'), parameters('environment'), '-webapp')]",
"appServicePlanName": "[concat(variables('name'), parameters('environment'),'-hosting')]",
"resources": [
{
"apiVersion": "2017-08-01",
"type": "Microsoft.Web/serverfarms",
"kind": "linux",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"comments": "This app service plan is used for the web app and slots.",
"properties": {
"reserved": true
},
"dependsOn": [],
"sku": {
"name": "[parameters('appServiceSku')]"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2016-08-01",
"name": "[variables('webAppPortalName')]",
"kind": "app,linux,container",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
],
"properties": {
"name": "[variables('webAppPortalName')]",
"siteConfig": {
"linuxFxVersion": "[concat('DOCKER|', parameters('dockerImageName'))]",
"alwaysOn": true,
"appSettings": [
{
"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
"value": "false"
},
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "[parameters('dockerRegistryUrl')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": "[parameters('dockerRegistryUsername')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": "[parameters('dockerRegistryPassword')]"
}
]
},
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
}
}
]
}
_artifactsLocation and _artifactsLocationSasToken do not need values, but somehow they need to be included. The main difference to other answers is the inclusion of the reserved attribute in the properties for the App Service Plan.
Hopefully this saves some of the headache this created for me!
The following ARM template worked for me.
It allows to specify the authentication details of a private Azure Container Registry.
Also make sure the docker image name follows this pattern: _MY_REGISTRY_USERNAME_-on.azurecr.io/_MY_NAMESPACE_/_MY_DOCKER_IMAGE_NAME_:latest
I run the az command like this:
az group deployment create \
--name "deployAzureApp" \
--resource-group <MY_RESOURCE_GROUP_NAME> \
--template-file <MY_ARM_JSON_TEMPLATE>.json --verbose --debug
Here's the Azure Resource Manager (ARM) JSON template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appName": {
"type": "String",
"defaultValue": "_MY_APP_NAME_"
},
"dockerImageName": {
"type": "String",
"defaultValue": "_MY_REGISTRY_USERNAME_-on.azurecr.io/_MY_NAMESPACE_/_MY_DOCKER_IMAGE_NAME_:latest"
},
"dockerRegistryUrl": {
"type": "String",
"defaultValue": "https://_MY_REGISTRY_USERNAME_-on.azurecr.io"
},
"dockerRegistryUsername": {
"type": "String",
"defaultValue": "_MY_REGISTRY_USERNAME_"
},
"dockerRegistryPassword": {
"type": "String",
"defaultValue": "_MY_REGISTRY_PSW_"
},
"servicePlanName": {
"type": "String",
"defaultValue": "_MY_SERVICE_PLAN_NAME_"
},
"appLocation": {
"type": "String",
"defaultValue": "_MY_REGION_"
}
},
"resources": [
{
"type": "Microsoft.Web/sites",
"apiVersion": "2016-08-01",
"name": "[parameters('appName')]",
"kind": "app,linux,container",
"location": "[parameters('appLocation')]",
"properties": {
"name": "[parameters('appName')]",
"siteConfig": {
"linuxFxVersion": "[concat('DOCKER|', parameters('dockerImageName'))]",
"alwaysOn": true,
"appSettings": [
{
"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
"value": "false"
},
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "[parameters('dockerRegistryUrl')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": "[parameters('dockerRegistryUsername')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": "[parameters('dockerRegistryPassword')]"
}
]
},
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('servicePlanName'))]"
}
}
]
}
About Azure Web App for Container, actually, there is just one point different with Azure Web App in the template. The point is the kind type.
Azure Web App:
"kind": "app"
Azure Web App for Container:
"kind": "app,linux,container",
So, you can create the Azure Web App for Container using template just setting up the kind with app,linux,container.
Update
I do the test and find out that the website kind is not the most important. The key is the property of the website:
"siteConfig": {
"linuxFxVersion": "DOCKER|nginx"
},
And the template will like below and it does a good job.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"webAppName": {
"type": "string",
"metadata": {
"description": "Base name of the resource such as web app name and app service plan "
},
"minLength": 2
},
"sku": {
"type": "string",
"defaultValue": "S1",
"metadata": {
"description": "The SKU of App Service Plan "
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
}
},
"variables": {
"webAppPortalName": "[concat(parameters('webAppName'), '-webapp')]",
"appServicePlanName": "[concat('AppServicePlan-', parameters('webAppName'))]"
},
"resources": [
{
"apiVersion": "2017-08-01",
"type": "Microsoft.Web/serverfarms",
"kind": "linux",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"comments": "This app service plan is used for the web app and slots.",
"properties": {},
"dependsOn": [],
"sku": {
"name": "[parameters('sku')]"
}
},
{
"apiVersion": "2016-08-01",
"type": "Microsoft.Web/sites",
"name": "[variables('webAppPortalName')]",
"location": "[parameters('location')]",
"comments": "This is the web app, also the default 'nameless' slot.",
"properties": {
"name": "[parameters('webAppName')]",
"siteConfig": {
"appCommandLine": "",
"linuxFxVersion": "DOCKER|nginx"
},
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
]
}
]
}
For what I observed, the linuxFxVersion problem depends on the App Service Plan where the Web App is hosted. What helped me was to set the reserved property on the properties object of App Service Plan resource to true.
What Microsoft doumentation says about the reserved setting:
If Linux app service plan true, false otherwise.
It says nothing about its default value, but from what I observed its false.
This is how my App Service Plan resource ARM template looks like.
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2019-08-01",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('appServicePlanSkuName')]",
"tier": "[parameters('appServicePlanSkuTier')]",
"size": "[parameters('appServicePlanSkuSize')]",
"family": "[parameters('appServicePlanSkuFamily')]",
"capacity": "[parameters('appServicePlanSkuCapacity')]"
},
"kind": "linux",
"properties": {
"name": "[variables('appServicePlanName')]",
"reserved": true
}
}
The above template doesn't work now and will show the following logs-
[error]BadRequest: {
"Code": "BadRequest",
"Message": "The parameter LinuxFxVersion has an invalid value.",
"Target": null,
"Details": [
{
"Message": "The parameter LinuxFxVersion has an invalid value."
},
{
"Code": "BadRequest"
},
{
"ErrorEntity": {
"ExtendedCode": "01007",
"MessageTemplate": "The parameter {0} has an invalid value.",
"Parameters": [
"LinuxFxVersion"
],
"Code": "BadRequest",
"Message": "The parameter LinuxFxVersion has an invalid value."
}
}
],
"Innererror": null
}
To fix this, We can make it 2 steps deployment process as mentioned in the following blog
Updated Solution

How to create multiple Service Bus topics using ARM template?

I've looked at the https://github.com/Azure/azure-quickstart-templates/tree/master/101-servicebus-topic example. Now I'm trying to figure out how to create several topics when running the deploy script using nested resource looping (https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-multiple/#looping-on-a-nested-resource).
It would be nice to just define an array in the value for serviceBusTopicName and then somehow creating topics by looping through it, instead of doing it manually.
I am getting the following error when trying to deploy.
Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource '[parameters('serviceBusTopicName')[copyIndex()]]' at line '72' column '14' is not valid.
Copying nested resources is not supported. Please see https://aka.ms/arm-copy/#looping-on-a-nested-resource for usage details.'.
This is the template.json that I have tried to get to work.
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "array",
"metadata": {
"description": "Name of the Topic"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/Namespaces",
"location": "[variables('location')]",
"kind": "Messaging",
"sku": {
"name": "StandardSku",
"tier": "Standard"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusTopicName')]",
"type": "Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"properties": {
"path": "[parameters('serviceBusTopicName')]"
},
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicName'))]"
}
}
]
},
parameters.json
{"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"value": "ServiceBus"
},
"serviceBusTopicName": {
"value": [
"Person",
"Stadium",
"Team"
]
},
"serviceBusApiVersion": {
"value": "2015-08-01"
}
}
As #Lain said, copying nested resource isn't supported as of now Check Here. For deploying multiple resource you have to move it to root. I just finished my POC for deploying multiple topics (Just topics assuming Namespace already exists) here is the code. I am passing topic names as a comma separated string:
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "string",
"metadata": {
"description": "Comma seperated Topic Names"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
}
}
},
"variables": {
"location": "[resourceGroup().location]",
"sbVersion": "[parameters('serviceBusApiVersion')]",
"TopicNames": "[split(parameters('serviceBusTopicName'), ',')]"
},
"resources": [{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', variables('TopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/Namespaces/Topics",
"copy": {
"name": "TopicNameCopy",
"count": "[length(variables('TopicNames'))]"
},
"properties": {
"path": "[variables('TopicNames')[copyIndex()]]"
}
}]
}
The link in the error message goes through this pretty well, but you can't have loops in nested resources, you need to push the resource up to the top level, and then link the resources together using names. This template will do what you seem to be trying to do:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus Namespace"
}
},
"serviceBusTopicNames": {
"type": "array",
"metadata": {
"description": "Name of the Service Bus Topic"
}
}
},
"variables": {
"sbVersion": "2015-08-01"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/namespaces",
"location": "[resourceGroup().location]",
"properties": {
}
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('serviceBusTopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/namespaces/Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicNames'))]"
},
"properties": {
"path": "[parameters('serviceBusTopicNames')[copyIndex()]]"
},
"resources": [
]
}
]
}