ARM Templates - Deploy to multiple Resource Groups with Nested Templates

Deployment

Azure Resource Manager (ARM) templates are JSON files that allow you to define the infrastructure and configuration of your project. This means that your Azure resources can be deployed repeatedly and reliably, with the templates living with your code under version control. I recently needed to create a template that provisioned resources across two different resource groups and hit a few “gotchas”, which I will document below.

The Scenario

When you are provisioning resources for your application in Azure, they usually reside in the same resource group. But there are times when it makes sense to deploy across resource groups. For our scenario, we want to deploy a single App Service Plan and have multiple App Services, each in their own resource group, use the same plan.

  • resource-group-1: Contains the App Service Plan.
  • resource-group-2: Contains an App Service (and other resources related to this individual application).

We want to define an ARM Template to provision the App Service Plan in resource-group-1 and, once this has been completed successfully, provision an App Service in resource-group-2 to run within this new App Service Plan.

The Problem

The deployment of resources such as our App Service Plan and App Service are scoped at the resource group level. This means that a single ARM template can deploy to only one resource group. To deploy to multiple resource groups we need to make use of nested templates.

The Solution

The solution is actually quite straightforward, but it took some trial and error and referring back to the Microsoft Documentation here:Deploy resources cross subscription & resource group — Azure Resource Manager | Microsoft Docs]. Hopefully by writing this up I can save someone else some time.

We have a parent template and a single nested template defined in a single JSON file, deploy.json. We will deploy this template from the Azure CLI with a command like:

az group deployment create \
—name NewDeployment \
—resource-group resource-group-1 \
—template-file deploy.json \
—parameters parameters.json

You can see from the resource-group argument that we will initially target resource-group-1, so that the parent template can deploy the App Service Plan. The nested template will deploy the App Service to resource-group-2 and therefore we require a parameter to allow the name of resource-group-2 to be specified.

We accept 3 parameters: rg_2_name, appService_name and appServicePlan_name. The values for these parameters are defined in the parameters.json file, but could just as easily be passed in from the command line when deploying the template.

Our parent template contains 2 resources. First is the Microsoft.Web/serverfarms resource type for defining the App Service Plan. The second is our nested template, as a Microsoft.Resources/deployments resource type. Our nested template contains resource types for the App Service (Microsoft.Web/sites), its config (Microsoft.Web/sites/config) and bindings (Microsoft.Web/sites/hostNameBindings). There is nothing special about our use of the Microsoft.Web resource types. Our interest here lies in the use of the Microsoft.Resources/deployments type as our nested template.

The full template is available in my GitHub repository, but let’s focus on the definition and deployment of the nested template. The image below shows the entire Microsoft.Resources/deployments resource type definition of our nested template.

See https://github.com/alastairchristian/CrossRGExample for the full template.See https://github.com/alastairchristian/CrossRGExample for the full template.

  • Line 45: we specify the resource group that the nested template will be deployed to. We use the value of the parameter containing the name of resource-group-2.

  • Lines 46–48: The deployment of our nested template is dependent on the deployment of the App Service Plan (from the parent template) being successful. This makes sense, you can’t deploy an App Service without an App Service Plan.

  • Lines 50–52: We set the expressionEvaluationOptions scope to “inner”. This means that the expressions within our nested template (lines 62–196) will be evaluated within its own scope and won’t refer back to the parent template. We need this set to be able to pass the App Service Plan’s resourceId into the nested template for use as the ServerFarmId of the App Service.

  • Lines 65–72: Our nested template defines its own parameters for the name of the App Service and the ServerFarmId of the App Service Plan.

  • Lines 54–61: The parent template provides values for the nested template’s parameters.

The actual nested template, lines 62–196, is just like any other ARM Template for deploying to a single resource group. For more complex scenarios you will likely make use of linked rather than nested templates, but the underlying concepts here remain the same.