Tenant deployments with ARM templates

As your organization matures, you may need to define and assign policies or Azure role-based access control (Azure RBAC) across your Azure AD tenant. With tenant level templates, you can declaratively apply policies and assign roles at a global level.

Tip

We recommend Bicep because it offers the same capabilities as ARM templates and the syntax is easier to use. To learn more, see tenant deployments.

Supported resources

Not all resource types can be deployed to the tenant level. This section lists which resource types are supported.

For Azure role-based access control (Azure RBAC), use:

  • roleAssignments

For nested templates that deploy to management groups, subscriptions, or resource groups, use:

  • deployments

For creating management groups, use:

  • managementGroups

For creating subscriptions, use:

  • aliases

For managing costs, use:

  • billingProfiles
  • billingRoleAssignments
  • instructions
  • invoiceSections
  • policies

For configuring the portal, use:

  • tenantConfigurations

Built-in policy definitions are tenant-level resources, but you can't deploy custom policy definitions at the tenant. For an example of assigning a built-in policy definition to a resource, see tenantResourceId example.

Schema

The schema you use for tenant deployments is different than the schema for resource group deployments.

For templates, use:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  ...
}

The schema for a parameter file is the same for all deployment scopes. For parameter files, use:

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  ...
}

Required access

The principal deploying the template must have permissions to create resources at the tenant scope. The principal must have permission to execute the deployment actions (Microsoft.Resources/deployments/*) and to create the resources defined in the template. For example, to create a management group, the principal must have Contributor permission at the tenant scope. To create role assignments, the principal must have Owner permission.

The Global Administrator for the Microsoft Entra ID doesn't automatically have permission to assign roles. To enable template deployments at the tenant scope, the Global Administrator must do the following steps:

  1. Elevate account access so the Global Administrator can assign roles. For more information, see Elevate access to manage all Azure subscriptions and management groups.

  2. Assign Owner or Contributor to the principal that needs to deploy the templates.

    New-AzRoleAssignment -SignInName "[userId]" -Scope "/" -RoleDefinitionName "Owner"
    
    az role assignment create --assignee "[userId]" --scope "/" --role "Owner"
    

The principal now has the required permissions to deploy the template.

Deployment commands

The commands for tenant deployments are different than the commands for resource group deployments.

For Azure CLI, use az deployment tenant create:

az deployment tenant create \
  --name demoTenantDeployment \
  --location ChinaNorth \
  --template-uri "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/tenant-deployments/new-mg/azuredeploy.json"

For more detailed information about deployment commands and options for deploying ARM templates, see:

Deployment location and name

For tenant level deployments, you must provide a location for the deployment. The location of the deployment is separate from the location of the resources you deploy. The deployment location specifies where to store deployment data. Subscription and management group deployments also require a location. For resource group deployments, the location of the resource group is used to store the deployment data.

You can provide a name for the deployment, or use the default deployment name. The default name is the name of the template file. For example, deploying a template named azuredeploy.json creates a default deployment name of azuredeploy.

For each deployment name, the location is immutable. You can't create a deployment in one location when there's an existing deployment with the same name in a different location. For example, if you create a tenant deployment with the name deployment1 in chinaeast, you can't later create another deployment with the name deployment1 but a location of chinanorth. If you get the error code InvalidDeploymentLocation, either use a different name or the same location as the previous deployment for that name.

Deployment scopes

When deploying to a tenant, you can deploy resources to:

  • the tenant
  • management groups within the tenant
  • subscriptions
  • resource groups

The only prohibited scope transitions occur from Resource Group to Management Group, or from Subscription to Management Group.

An extension resource can be scoped to a target that is different than the deployment target.

The user deploying the template must have access to the specified scope.

This section shows how to specify different scopes. You can combine these different scopes in a single template.

Scope to tenant

Resources defined within the resources section of the template are applied to the tenant.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    tenant-resources
  ],
  "outputs": {}
}

Scope to management group

To target a management group within the tenant, add a nested deployment and specify the scope property.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "mgName": {
      "type": "string"
    }
  },
  "variables": {
    "mgId": "[concat('Microsoft.Management/managementGroups/', parameters('mgName'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2022-09-01",
      "name": "nestedMG",
      "scope": "[variables('mgId')]",
      "location": "chinaeast",
      "properties": {
        "mode": "Incremental",
        "template": {
          management-group-resources
        }
      }
    }
  ],
  "outputs": {}
}

Scope to subscription

You can also target subscriptions within the tenant. The user deploying the template must have access to the specified scope.

To target a subscription within the tenant, use a nested deployment and the subscriptionId property.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2021-04-01",
      "name": "nestedSub",
      "location": "chinanorth2",
      "subscriptionId": "00000000-0000-0000-0000-000000000000",
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              subscription-resources
            }
          ]
        }
      }
    }
  ]
}

Scope to resource group

You can also target resource groups within the tenant. The user deploying the template must have access to the specified scope.

To target a resource group within the tenant, use a nested deployment. Set the subscriptionId and resourceGroup properties. Don't set a location for the nested deployment because it's deployed in the location of the resource group.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2021-04-01",
      "name": "nestedRGDeploy",
      "subscriptionId": "00000000-0000-0000-0000-000000000000",
      "resourceGroup": "demoResourceGroup",
      "properties": {
        "mode": "Incremental",
        "template": {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [
            {
              resource-group-resources
            }
          ]
        }
      }
    }
  ]
}

Create management group

The following template creates a management group.

{
    "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "mgName": {
        "type": "string",
        "defaultValue": "[concat('mg-', uniqueString(newGuid()))]"
      }
    },
    "resources": [
      {
        "type": "Microsoft.Management/managementGroups",
        "apiVersion": "2020-02-01",
        "name": "[parameters('mgName')]",
        "properties": {
        }
      }
    ]
  }
  

If your account doesn't have permission to deploy to the tenant, you can still create management groups by deploying to another scope. For more information, see Management group.

Assign role

The following template assigns a role at the tenant scope.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.5.6.12127",
      "templateHash": "17107802581699825924"
    }
  },
  "parameters": {
    "principalId": {
      "type": "string",
      "metadata": {
        "description": "principalId if the user that will be given contributor access to the tenant"
      }
    },
    "roleDefinitionId": {
      "type": "string",
      "defaultValue": "8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
      "metadata": {
        "description": "roleDefinition for the assignment - default is owner"
      }
    }
  },
  "variables": {
    "roleAssignmentName": "[guid('/', parameters('principalId'), parameters('roleDefinitionId'))]"
  },
  "resources": [
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2020-03-01-preview",
      "name": "[variables('roleAssignmentName')]",
      "properties": {
        "roleDefinitionId": "[tenantResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]",
        "principalId": "[parameters('principalId')]"
      }
    }
  ]
}

Next steps