0
votes

I want to deploy resources for storage account, service bus, azure function and web app using ARM templates. Also I want to write storage and service bus connections strings to both function and web app application settings. So I created a main template which references 2 child templates: one for function and one for web app. Both child templates reference same linked templates for service bus and storage account. My composite template is validated successfully but when I'm trying to deploy I've got errors like this:

Unable to edit or replace deployment serviceBus: previous deployment from [datetime] is still active

It seems like Azure tries to deploy service bus and storage account twice. How can I tell ARM to use the same serviceBus deployment for both web app and function?

Current simplified schema:

      Main ARM template(azuredeploy.json)
     /                                 \
Web App(nestedtemplates/webApp.json)   Function App(nestedtemplates/functions/resourceAllocationFunction.json)
      \                               /
    Service Bus(nestedtemplates/serviceBus.json)

Templates:

azuredeploy.json

"resources": [    
    {
      "name": "webApp",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('webAppTemplateFolder'), '/', variables('webAppTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "mainStorageAccountType": { "value": "[parameters('mainStorageAccountType')]" }, 

          "location": { "value": "[parameters('location')]" },

          "_artifactsLocation": { "value": "[parameters('_artifactsLocation')]" },
          "_artifactsLocationSasToken": { "value": "[parameters('_artifactsLocationSasToken')]" }
        }
      }
    },
    {
      "name": "resourceAllocationFunction",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('resourceAllocationFunctionTemplateFolder'), '/', variables('resourceAllocationFunctionTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "mainStorageAccountType": { "value": "[parameters('mainStorageAccountType')]" },    


          "location": { "value": "[parameters('location')]" },

          "_artifactsLocation": { "value": "[parameters('_artifactsLocation')]" },
          "_artifactsLocationSasToken": { "value": "[parameters('_artifactsLocationSasToken')]" }
        }
      }
    }
  ],

nestedtemplates/webApp.json

"resources": [
    {
      "name": "[parameters('servicePlanName')]",
      "type": "Microsoft.Web/serverfarms",
      "location": "[resourceGroup().location]",
      "apiVersion": "2015-08-01",
      "sku": {
        "name": "[parameters('servicePlanSkuName')]"
      },
      "dependsOn": [],     
      "properties": {
        "name": "[parameters('servicePlanName')]",
        "numberOfWorkers": 1
      }
    },
    {
      "name": "[variables('webAppName')]",
      "type": "Microsoft.Web/sites",
      "location": "[parameters('location')]",
      "apiVersion": "2015-08-01",
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', parameters('servicePlanName'))]"
      ],      
      "properties": {
        "name": "[variables('webAppName')]",
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('servicePlanName'))]"        
      },
      "resources": [
        {
          "name": "appsettings",
          "type": "config",
          "apiVersion": "2015-08-01",
          "dependsOn": [
            "[resourceId('Microsoft.Web/sites', variables('webAppName'))]",
            "[resourceId('microsoft.insights/components/', variables('appInsightsName'))]"
          ],
          "properties": {

            "ServiceBus:ConnectionString": "[reference('serviceBus').outputs.connectionString.value]",
            "ServiceBus:QueueName": "[reference('serviceBus').outputs.queueName.value]",

            "StorageAccounts:WritableAccountName": "[reference('mainStorage').outputs.storageAccountName.value]",
            "StorageAccounts:ConnectionString": "[reference('mainStorage').outputs.storageAccountConnString.value]",           
          }
        }
      ]
    },
    {
      "name": "serviceBus",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [ ],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('serviceBusTemplateFolder'), '/', variables('serviceBusTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },       
        "parameters": {
          "location": { "value": "[parameters('location')]" }
        }
      }
    },
    {
      "name": "mainStorage",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [ ],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('mainStorageTemplateFolder'), '/', variables('mainStorageTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },       
        "parameters": {
          "location": { "value": "[parameters('location')]" },
          "mainStorageAccountType": { "value": "[parameters('mainStorageAccountType')]" }
        }
      }
    }
]

nestedtemplates/functions/resourceAllocationFunction.json

"resources": [
    {
      "name": "functionStorageAccount",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('functionStorageAccountTemplateFolder'), '/', variables('functionStorageAccountTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "location": { "value": "[parameters('location')]" }
        }       
      }
    },
    {
      "name": "consumptionPlan",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('consumptionPlanTemplateFolder'), '/', variables('consumptionPlanTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "location": { "value": "[parameters('location')]" }
        }       
      }
    },    
    {
      "apiVersion": "2016-08-01",
      "type": "Microsoft.Web/sites",
      "name": "[variables('functionAppName')]",
      "location": "[parameters('location')]",
      "kind": "functionapp",
      "dependsOn": [
        "[resourceId('microsoft.insights/components', variables('appInsightsName'))]"
      ],
      "properties": {
        "name": "[variables('functionAppName')]",
        "serverFarmId": "[reference('consumptionPlan').outputs.resourceID.value]",
        "siteConfig": {
          "appSettings": [
            {
              "name": "AzureWebJobsDashboard",
              "value": "[reference('functionStorageAccount').outputs.storageAccountConnString.value]"
            },
            {
              "name": "AzureWebJobsStorage",
              "value": "[reference('functionStorageAccount').outputs.storageAccountConnString.value]"
            },
            {
              "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
              "value": "[reference('functionStorageAccount').outputs.storageAccountConnString.value]"
            },
            {
              "name": "WEBSITE_CONTENTSHARE",
              "value": "[toLower(variables('functionAppName'))]"
            },
            {
              "name": "FUNCTIONS_WORKER_RUNTIME",
              "value": "dotnet"
            },
            {
              "name": "AccoutLogin",
              "value": "[reference('mainStorage').outputs.storageAccountName.value]"
            },
            {
              "name": "AccountConnString",
              "value": "[reference('mainStorage').outputs.storageAccountConnString.value]"
            },                      
            {
              "name": "ServiceBusConnectionString",
              "value": "[reference('serviceBus').outputs.connectionString.value]"
            },
            {
              "name": "QueueName",
              "value": "[reference('serviceBus').outputs.queueName.value]"
            }
          ]
        }
      }
    },
    {
      "name": "serviceBus",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('serviceBusTemplateFolder'), '/', variables('serviceBusTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "location": { "value": "[parameters('location')]" }
        }
      }
    },
    {
      "name": "mainStorage",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2016-09-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('_artifactsLocation'), '/', variables('mainStorageTemplateFolder'), '/', variables('mainStorageTemplateFileName'), parameters('_artifactsLocationSasToken'))]",
          "contentVersion": "1.0.0.0"
        },       
        "parameters": {
          "location": { "value": "[parameters('location')]" },
          "mainStorageAccountType": { "value": "[parameters('mainStorageAccountType')]" }
        }
      }
    }
  ],

nestedtemplates/mainStorage.json

"resources": [
    {
      "name": "[variables('mainStorageAccountName')]",
      "type": "Microsoft.Storage/storageAccounts",
      "location": "[parameters('location')]",
      "apiVersion": "2018-02-01",
      "sku": {
        "name": "[parameters('mainStorageAccountType')]"
      },
      "dependsOn": [],
      "kind": "StorageV2",
      "resources": [
        {
          "name": "[concat('default/', variables('containerName'))]",
          "type": "blobServices/containers",
          "apiVersion": "2018-03-01-preview",
          "dependsOn": [
            "[variables('mainStorageAccountName')]"
          ]
        }
      ]
    }
  ],
  "outputs": {
    "resourceID": {
      "type": "string",
      "value": "[resourceId('Microsoft.Storage/storageAccounts', variables('mainStorageAccountName'))]"
    },
    "storageAccountName": {
      "type": "string",
      "value": "[variables('mainStorageAccountName')]"
    },
    "storageAccountContainer": {
      "type": "string",
      "value": "[variables('containerName')]"
    },
    "storageAccountConnString": {
      "type": "string",
      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('mainStorageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('mainStorageAccountName')),'2015-05-01-preview').key1)]"
    }
  }

nestedtemplates/serviceBus.json

"resources": [
    {
      "type": "Microsoft.ServiceBus/namespaces",
      "name": "[variables('serviceBusNamespaceName')]",
      "apiVersion": "2017-04-01",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Standard"
      },
      "properties": {},
      "resources": [
        {
          "apiVersion": "2017-04-01",
          "name": "[parameters('serviceBusQueueName1')]",
          "type": "Queues",
          "dependsOn": [
            "[concat('Microsoft.ServiceBus/namespaces/', variables('serviceBusNamespaceName'))]"
          ],
          "properties": {
            "lockDuration": "PT5M",
            "maxSizeInMegabytes": "1024",
            "requiresDuplicateDetection": "false",
            "requiresSession": "false",
            "defaultMessageTimeToLive": "P10675199DT2H48M5.4775807S",
            "deadLetteringOnMessageExpiration": "false",
            "duplicateDetectionHistoryTimeWindow": "PT10M",
            "maxDeliveryCount": "10",
            "autoDeleteOnIdle": "P10675199DT2H48M5.4775807S",
            "enablePartitioning": "false",
            "enableExpress": "false"
          }
        }
      ]
    }
  ],
  "outputs": {
    "resourceID": {
      "type": "string",
      "value": "[resourceId('Microsoft.ServiceBus/namespaces', variables('serviceBusNamespaceName'))]"
    },
    "connectionString": {
      "type": "string",
      "value": "[listkeys(variables('authRuleResourceId'), '2017-04-01').primaryConnectionString]"
    },
    "sharedAccessPolicyPrimaryKey": {
      "type": "string",
      "value": "[listkeys(variables('authRuleResourceId'), '2017-04-01').primaryKey]"
    },
    "queueName": {
      "type": "string",
      "value": "[parameters('serviceBusQueueName1')]"
    }
  }
1

1 Answers

0
votes

ok, this makes very little sense. why do you deploy same thing two times if you only need it one time? answer is - you dont deploy it two times. so move it as a separate resource\separate template and deploy only one. just get its keys into web app\function app.

  1. move these deployment to the main template: mainStorage and serviceBus. remove outputs from them, outputs are not secure.
  2. replace "[reference('mainStorage').outputs.storageAccountConnString.value]" with appropriate value from the outputs: "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('mainStorageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('mainStorageAccountName')),'2015-05-01-preview').key1)]". repeat for all outputs.
  3. you also need dependsOn on the webapp\function app to wait until service bus\storage are actually created

ps. makes no sense to create a nested deployment for 1 resource. I'd rework your template into a flat template. it makes no sense to use nested deployment here.