Azure ARM nested templates targeting multiple scopes

In my last post, I talked about ARM template deployment scopes. All the templates in that post targeted a single scope. Instead, you can deploy resources at multiple scopes using the same ARM template. This is possible by nesting templates and setting the scope of the nested template. The resource type of the nested template will be “Microsoft.Resources/deployments”. The deployments resource type is supported across all scopes.

I’ve combined the templates that I used for creating a management group and creating a policy definition into a single template. Setting the scope and location of the deployment resource is mandatory. As I’m creating the management group in the same deployment, the dependsOn property has been set to the management group.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "mgName": {
      "type": "string",
      "defaultValue": "mgnew01"
    }
  },
  "variables": {
    "mgScope": "[concat('Microsoft.Management/managementGroups/', parameters('mgName'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Management/managementGroups",
      "apiVersion": "2020-02-01",
      "name": "[parameters('mgName')]",
      "properties": {
      }
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2020-06-01",
      "name": "nestedMGDeployment",
      "scope": "[variables('mgScope')]",
      "location": "Australia East",
      "dependsOn": [ "[variables('mgScope')]" ],
      "properties": {
        "mode": "Incremental",
        "expressionEvaluationOptions": {
          "scope": "inner"
        },

        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {
            "policyName": {
              "type": "string",
              "defaultValue": "Restrict Locations"
            },
            "policyDisplayName": {
              "type": "string",
              "defaultValue": "Restrict Locations"
            },
            "policyDescription": {
              "type": "string",
              "defaultValue": "This policy restrict the locations you can specify when deploying resources. Excludes resource groups, Microsoft.AzureActiveDirectory/b2cDirectories, and resources that use the 'global' region."
            }
          },
          "resources": [
            {
              "type": "Microsoft.Authorization/policyDefinitions",
              "name": "[parameters('policyName')]",
              "apiVersion": "2019-09-01",
              "properties": {
                "displayName": "[parameters('policyDisplayName')]",
                "policyType": "Custom",
                "description": "[parameters('policyDescription')]",
                "metadata": {
                  "category": "General"
                },
                "mode": "All",
                "parameters": {
                  "allowedLocations": {
                    "type": "Array",
                    "metadata": {
                      "displayName": "Allowed locations",
                      "description": "The list of locations that can be specified when deploying resources.",
                      "strongType": "location"
                    }
                  }
                },
                "policyRule": {
                  "if": {
                    "allOf": [
                      {
                        "field": "location",
                        "notIn": "[[parameters('allowedLocations')]"
                      },
                      {
                        "field": "location",
                        "notEquals": "global"
                      },
                      {
                        "field": "type",
                        "notEquals": "Microsoft.AzureActiveDirectory/b2cDirectories"
                      }
                    ]
                  },
                  "then": {
                    "effect": "Deny"
                  }
                }
              }
            }
          ]
        }
      }
    }
  ]
}

Note that within expressionEvaluationOptions, the “scope” is set to “inner”.

You can even target different resource groups under the same subscription or across different subscriptions. The following template creates a new resource group and deploys a storage account to an existing resource group under the same subscription. Note, the resourceGroup of the deployment resource has been set to the existing resource group.

{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "rgName": {
      "type": "string"
    },
    "rgLocation": {
      "type": "string"
    },
    "secondResourceGroup": {
      "type": "string"
    }
  },
  "variables": {
    "storageName": "[concat('store', uniqueString(subscription().id, parameters('secondResourceGroup')))]"
  },
  "resources": [
    {
      "type": "Microsoft.Resources/resourceGroups",
      "apiVersion": "2020-10-01",
      "name": "[parameters('rgName')]",
      "location": "[parameters('rgLocation')]",
      "properties": {}
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2020-06-01",
      "name": "nestedDeployment",
      "resourceGroup": "[parameters('secondResourceGroup')]",
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "parameters": {},
          "variables": {},
          "resources": [
            {
              "type": "Microsoft.Storage/storageAccounts",
              "apiVersion": "2019-06-01",
              "name": "[variables('storageName')]",
              "location": "[parameters('rgLocation')]",
              "sku": {
                "name": "Standard_LRS"
              },
              "kind": "StorageV2"
            }
          ],
          "outputs": {}
        }
      }
    }
  ]
}

The parameter file for the above template:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "rgName": {
            "value": "nestedTemplateDeployGroup"
        },
        "rgLocation": {
            "value": "Australia East"
        },
        "secondResourceGroup": {
            "value": "existingResourceGroup"
        }
    }
}

As you can see, you can combine templates in so many different ways. It makes sense to combine templates that deploy resources that will change at the same time.

Leave a comment