ARM Template Deployment Scopes

It used to be that ARM templates could only be deployed at a single scope, the resource group. All the resources you defined inside a template would be created inside the resource group you used as part of your deployment command. Over time we've seen the ability to deploy at different scopes added, first off was deployment at the subscription scope, and now we see the introduction of the management group and tenant scopes. With these new scopes, we now can deploy at all the levels we might need in an ARM template. Let's take a look at what these scopes are and how they work.

Note, the new PowerShell commands used below for the management group and tenant scope have only been recently released. Make sure you update your cmdlets to pick these up using the update-modules az command.

Resource Group Scope

The original scope for templates, any resources deployed at this scope will be deployed into the Resource Group defined. The vast majority of resources you may want to deploy are going to be deployed at this scope.

Creating a deployment at the resource group scope can be done using the New-AzResourceGroupDeployment PowerShell command or the az group deployment CLI command.

PowerShell

New-AzResourceGroupDeployment -ResourceGroupName "LambdaToysRG" -TemplateFile "D:\templates\mainTemplate.json" -TemplateParameterFile "D:\templates\mainTemplate.params.json"

CLI

az group deployment create -g "LambdaToysRG" --template-file @mainTemplate.json --parameters @mainTemplate.params.json

Subscription Scope

Next, we can deploy resources at the subscription scope. This is for resources that exist outside of a resource group, or are applied directly to subscriptions such as:

  • Resource Groups
  • Policy definitions and assignments
  • RBAC Permissions on Subscriptions
  • RBAC Custom Roles

Deployments at the Subscription scope use the New-AzDeployment PowerShell command, or the az deployment create CLI command. To bring this in line with the naming of the new Management Group and Tenant level commands this also has an alias of New-AzSubscriptionDeployment in PowerShell. The New-AzDeployment will eventually be deprecated so I would reccomend you start using New-AzSubscriptionDeployment in your scripts.

PowerShell

New-AzDeployment -Location "West Europe" -TemplateFile "D:\templates\mainTemplate.json" -TemplateParameterFile "D:\templates\mainTemplate.params.json"

CLI

az deployment create --location WestEurope --template-file @mainTemplate.json --parameters @mainTemplate.params.json

You'll notice that neither of these commands specifies the subscription name or ID, they are using the currently selected subscription in the PowerShell/CLI context.

Management Group Scope

Deployments at the management group level have recently been added which allow you to define resources at this level. These include

  • Policy definitions and assignments
  • RBAC Permissions on Management Groups
  • RBAC Custom Roles

These deployments use the New-AzManagementGroupDeployment PowerShell command. Currently, there is not a CLI command to undertake these deployments.

PowerShell

New-AzManagementGroupDeployment -ManagementGroupId "lambdaToysDevelopmentMgmt" -Location "West Europe" -TemplateFile "D:\templates\mainTemplate.json" -TemplateParameterFile "D:\templates\mainTemplate.params.json"

Here we do define the management group ID to deploy to.

Tenant Scope

The final scope is the tenant level scope, which allows you to deploy at the top-level tenant level. The amount of resources that can be deployed at the tenant scope seems to be somewhat limited at the moment; all I could see where:

  • RBAC Custom Roles
  • Policy Definitions

These deployments use the New-AzTenantDeployment PowerShell command. Currently, there is not a CLI command to do the same.

PowerShell

New-AzTenantDeployment -Location "West Europe" -TemplateFile "D:\templates\mainTemplate.json" -TemplateParameterFile "D:\templates\mainTemplate.params.json"

Combining Scopes

Often, you will be looking to deploy resources across scopes, rather than at a single one of the scopes above. In this example, we want to do the following:

  • Apply a policy at the management group scope
  • Create a resource group at the subscription scope
  • Create a storage account in the created resource group, at the resource group scope

We could just put our deployments in three separate files and call the 3 different PowerShell commands, but this is inefficient and fragments our templates. Instead, we can combine these into a single deployment, but this requires us to work with nested templates if you have not used nested templates before I recommend checking out my article on this.

All of the scopes listed above support another resource, the deployment resource. This means we can use nested templates to create a deployment at another scope. In our example, we will use the New-AzManagementGroupDeployment command to run our deployment, as this is the highest scope we need to create resources at. Inside our template, we will then create a subscription level deployment, inside which we create the resource group. Finally, nested inside the subscription level deployment, we will create a resource group level deployment to create our storage account.

This level of nesting can be a bit confusing. In the example below, I am using inline templates to make the example easier to see; however, I would recommend you don't do this. Instead, use template links to call out to separate template files containing your actual logic for each scope.

Here's the template that will do what we need, you can find a copy on GitHub here as well.

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "subscriptionID": {
            "type": "string",
            "metadata": {
                "description": "description"
            }
        }
    },
    "variables": {
    },
    "resources": [
        {
            "type": "Microsoft.Authorization/policyDefinitions",
            "apiVersion": "2018-05-01",
            "name": "locationpolicy",
            "location": "West Europe",
            "properties": {
                "policyType": "Custom",
                "parameters": {
                },
                "policyRule": {
                    "if": {
                        "field": "location",
                        "equals": "westus"
                    },
                    "then": {
                        "effect": "deny"
                    }
                }
            }
        },
        {
            "name": "nestedDeployment1",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2018-05-01",
            "location": "West Europe",
            "subscriptionId": "[parameters('subscriptionID')]",
            "properties": {
                "mode": "incremental",
                "template": {
                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                    "contentVersion": "1.0.0.0",
                    "parameters": {
                    },
                    "variables": {
                    },
                    "resources": [
                        {
                            "type": "Microsoft.Resources/resourceGroups",
                            "apiVersion": "2018-05-01",
                            "location": "West Europe",
                            "name": "TestRG1",
                            "properties": {
                            }
                        },
                        {
                            "type": "Microsoft.Resources/deployments",
                            "apiVersion": "2018-05-01",
                            "name": "storageDeployment",
                            "resourceGroup": "TestRG1",
                
                            "properties": {
                                "mode": "Incremental",
                                "template": {
                                    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
                                    "contentVersion": "1.0.0.0",
                                    "parameters": {
                                    },
                                    "variables": {
                                    },
                                    "resources": [
                                        {
                                            "type": "Microsoft.Storage/storageAccounts",
                                            "apiVersion": "2017-10-01",
                                            "name": "testrgstorage001",
                                            "location": "West Europe",
                                            "kind": "StorageV2",
                                            "sku": {
                                                "name": "Standard_LRS"
                                            }
                                        }
                                    ],
                                    "outputs": {
                                    }
                                }
                            }
                        }
                    ]
                }
            }
        }
    ],
    "outputs": {
    },
    "functions": [
    ]
}

We will then run the following command using PowerShell:

New-AzManagementGroupDeployment -ManagementGroupId "lambdaToysDevelopmentMgmt" -Location "West Europe" -TemplateFile "D:\templates\demo.json" -TemplateParameterFile "D:\templates\demo.params.json"

Once this runs, it will run through our different scopes and created the required resources.

Examples

You can find examples of deployments at each of the scopes on GitHub: