ARM nested templates vs linked templates

Let’s say you wanna deploy an ARM template for an API Management service without using a storage account for deployment. In that case you can add all of your ARM template code to the orchestrator file. We can refer to that scenario as nested templates. The alternative is to create separate ARM template files for your version set, named values, policies, products and the API itself and use them as linked templates in your orchestrator file. In other words. You don’t assemble all template files in your orchestrator, you just refer to the linked template files via a so called templateLink.

Let’s look at an example. The first resource you will have to create, is a version set. An orchestrator with a linked template would look like this:

{
      "name": "versionSetTemplate",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2018-01-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[concat(parameters('LinkedTemplatesBaseUrl'), '/alfen-is-tst-apim-apiVersionSets.template.json', parameters('LinkedTemplatesUrlQueryString'))]",
          "contentVersion": "1.0.0.0"
        }
      }
    }

Whereas an orchestrator with a nested template would look like this:

{
      "name": "TransformerSubstationVersionSet",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2019-05-01",
      "dependsOn": [],
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              "name": "[concat(parameters('apiManagementName'), '/', parameters('versionSet'))]",
              "type": "Microsoft.ApiManagement/service/apiversionsets",
              "apiVersion": "2019-01-01",
              "properties": {
                "displayName": "[parameters('apiName')]",
                "versioningScheme": "Segment"
              }
            }
          ]
        }
      }
    }

ARM Nested Templates

As an improvement of the previous post, I decided to separate the creation of the service bus topic subscription from the creation of the servicebus topic itself. Here is where nested templates come into play. First I created an ARM template with the definition of both the servicebus subscription and the servicebus subscription rule. But, whatever I tried – dependsOn resourceId, dependsOn name, empty dependsOn – I could not get it to work. In the end I decided to create two separate ARM templates with a dependsOn in the orchestrator.

Orchestrator:

{
      "name": "sb-topdesk-pnb-werknemer-uit-sub",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2017-05-10",
      "dependsOn": [
      ],
      "properties": {
        ...
        },
        "templateLink": {
          "uri": "[concat(parameters('storageAccountCICD').url,'/APIConnections/sb-sub.json', parameters('storageAccountCICD').sasToken)]",
          "contentVersion": "1.0.0.0"
        }
      }
    },
    {
      "apiVersion": "2017-05-10",
      "name": "sb-topdesk-pnb-werknemer-uit-subfilter",
      "type": "Microsoft.Resources/deployments",
      "dependsOn": [
        "sb-topdesk-pnb-werknemer-uit-sub"
      ],

First you will see the subscription and then the subscription filter. The subscription has no depends on the service bus topic. The rule filter depends on the subscription as you might expect.

sb-sub.json:

..
"resources": [
		{
		    "apiVersion": "2018-05-01",
		    "name": "nestedTemplate",
		    "type": "Microsoft.Resources/deployments",
            	    "resourceGroup": "[parameters('SBResourceGroup')]",
		    "properties": {
			"mode": "Incremental",
			"template": {
				"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
				"contentVersion": "1.0.0.0",
				"resources": [
                			{
                    			"name": "[concat(parameters('ServiceBusNaam'), '/shared~werknemers/', Parameters('SBTopDeskPnbSubscriptionNaam'))]",
                    			"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions",
                    			"apiVersion": "2017-04-01",
                    			"location": "westeurope",
                    			"dependsOn": [],
                    			"properties": {
                        			"lockDuration": "PT1M",
                        			"requiresSession": false,
                        			"defaultMessageTimeToLive": "P10675199DT2H48M5.4775807S",
                        			"deadLetteringOnMessageExpiration": false,
                        			"deadLetteringOnFilterEvaluationExceptions": false,
                        			"maxDeliveryCount": 10,
                        			"status": "Active",
                        			"enableBatchedOperations": false,
                        			"autoDeleteOnIdle": "P10675199DT2H48M5.4775807S"
                    			}
				    }
				]

As you can see, it’s a nested template (type Microsoft.Resources/deployments) without a dependsOn relation. It’s a nested template because the resource depends on the previously created servicebus topic.

sb-subfilter.json:

..
"resources": [
		{
			"apiVersion": "2018-05-01",
			"name": "nestedTemplate",
			"type": "Microsoft.Resources/deployments",
            		"resourceGroup": "[parameters('SBResourceGroup')]",
			"properties": {
				"mode": "Incremental",
				"template": {
					"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
					"contentVersion": "1.0.0.0",
					"resources": [
                        			{
                            				"name": "[concat(parameters('ServiceBusNaam'), '/shared~werknemers/', Parameters('SBTopDeskPnbSubscriptionNaam'), '/', parameters('SBTopDeskPnbRuleNaam'))]",
                            				"type": "Microsoft.ServiceBus/namespaces/topics/subscriptions/rules",
                            				"apiVersion": "[variables('apiVersion')]",
                            				"location": "westeurope",
                            				"dependsOn": [],
                            				"properties": {
                                				"filterType": "SqlFilter",
                                				"sqlFilter": {
                                    					"sqlExpression": "Kostenplaats IN ('2370', '2670')",
                                    					"requiresPreprocessing": "false"
                                				}
                            				}
                        			}
                    			]