Cross Subscription Deployments

Back in June Microsoft introduced the ability to have an ARM template deploy to more than one resource group, at Ignite this year they went a step further to allow cross subscription deployments. It is now possible to have a single deployment create resources in multiple resource groups, regions and subscriptions, which is really useful if you are trying to deploy environments that include separate DR locations, or deployments that impact both a client and a core set of infrastructure.

As with cross resource group deployments, the process makes use of nested templates. When you deploy an ARM template, you specify a resource group (and so a subscription by inference) that will be used for the deployment, like below:

New-AzureRmResourceGroupDeployment -ResourceGroupName "LambdaToys" -TemplateFile "D:\Azure\Templates\LambdaWeb.json" -TemplateParameterFile "D:\Azure\Templates\WebParms.json"

This does not change here, and this resource group (LambdaToys in this example) becomes your primary resource group. Deploying resources to this Resource Group in your ARM template is done as normal.

Where you want to deploy resources into a second subscription, these resources will be defined inside a nested template (either inline or in a separate JSON file) which is called from the main template. Let’s look at an example, in this simple deployment, I want to deploy 2 storage accounts, one in subscription1 and one in subscription2. Subscription 1 will be my primary.

The first thing we will do is set up our parameters:

"parameters": {
    "StorageAccountName1": {
        "type": "string"
    },
    "StorageAccountName2": {
        "type": "string"
    },
    "SecondSubID": {
        "type": "string"
    },
    "SecondRGName": {
        "type": "string"
    }
},

I’m passing in the name for my two storage accounts, and then the subscription ID and resource group name of subscription2. Note I do not need to pass in the details of subscription1, my primary subscription, as this will be collected from my deployment command.

Now I have my parameters, I will define my resources. First I will define the storage account in subscription1:

{
    "type": "Microsoft.Storage/storageAccounts",
    "name": "[parameters('StorageAccountName1')]",
    "apiVersion": "2015-06-15",
    "location": "West Europe",
    "properties": {
        "accountType": "Standard_LRS"
    }
}

There is absolutely nothing new here, this is how we have always deployed storage accounts in an ARM template. Because it is going in the primary subscription I don’t need to do anything special.

For the second storage account, we need to use a nested template. In this example, I am going to use an inline nested template because it is only deploying a storage account, but for more complex deployments you can, and should use a separate JSON file which you reference by URL.

{
    "apiVersion": "2017-05-10",
    "name": "nestedTemplate",
    "type": "Microsoft.Resources/deployments",
    "resourceGroup": "[parameters('SecondRGName')]",
    "subscriptionId": "[parameters('SecondSubID')]",
    "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",
                    "name": "[parameters('StorageAccountName2')]",
                    "apiVersion": "2015-06-15",
                    "location": "West Europe",
                    "properties": {
                        "accountType": "Standard_LRS"
                    }
                }
            ]
        },
        "parameters": {}
    }
}

As you can see, as part of the declaration of the nested template we are providing the name of the resource group and the ID of the subscription we want to deploy to. After that, it is just a normal nested template, and we define our resources as normal.

Remember, the ARM template will not create the resource groups for you, in either subscription so you will need to create these prior to deployment. Once those are created you can run the deployment with the usual New-AzureRmResourceGroupDeployment command, or your deployment tool of choice.

Here is the full template:

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "StorageAccountName1": {
            "type": "string"
        },
        "StorageAccountName2": {
            "type": "string"
        },
        "SecondSubID": {
            "type": "string"
        },
        "SecondRGName": {
            "type": "string"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "name": "[parameters('StorageAccountName1')]",
            "apiVersion": "2015-06-15",
            "location": "West Europe",
            "properties": {
                "accountType": "Standard_LRS"
            }
        },
        {
            "apiVersion": "2017-05-10",
            "name": "nestedTemplate",
            "type": "Microsoft.Resources/deployments",
            "resourceGroup": "[parameters('SecondRGName')]",
            "subscriptionId": "[parameters('SecondSubID')]",
            "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",
                            "name": "[parameters('StorageAccountName2')]",
                            "apiVersion": "2015-06-15",
                            "location": "West Europe",
                            "properties": {
                                "accountType": "Standard_LRS"
                            }
                        }
                    ]
                },
                "parameters": {}
            }
        }
    ]
}