Jeff Brown

Cloud and DevOps Engineer specializing in Microsoft 365, Azure, and PowerShell. Twitter | LinkedIn

Did you know Terraform can deploy to multiple Azure subscriptions? Terraform can deploy to multiple subscriptions by defining multiple providers blocks. You then use aliases to identity each subscription within your configuration.

This tutorial will teach you how to define these aliases and to write a Terraform configuration for deploying resources to multiple Azure subscriptions in a single tenant. To follow along with this tutorial, you will need:

Authentication Considerations

If you are working on multiple Azure subscriptions, you will need permissions to each subscription for Terraform to perform the deployment. These permissions can come from a user account, service principal, or managed identity. The account needs sufficient rights to create and manage resources in those subscriptions, such as the Contributor role. You can use a single account for all subscriptions or multiple accounts for each subscription.

After creating each account and assigned permissions, you will need to authenticate to each subscription. You can authenticate using multiple methods, such as Azure CLI credentials, defining credentials within the provider block, or using environment variables.

To learn more about different authentication options, take a look at Performing Azure Authentication Inside Terraform.

Defining Multiple Azure Providers

To get started with the Terraform configuration, include the azurerm provider in your terraform definition block. You can put version requirements within the azurerm definition block if you’d like. This section of the configuration does not change for multiple Azure subscription deployments.

terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
    }
  }
}

Next, create a provider block that defines properties and features for the azurerm provider. However, unlike previous single subscription deployments, you define an alias for this provider. You later use the alias to target an Azure subscription in the resource definition.

In the example below, the two aliases are app-sub for the application subscription and prod-sub for the production management subscription. Also, note that each provider block uses different service principal client_id and client_secret values. You very well can use the same service principal for each subscription, but this tutorial uses separate accounts to show using two accounts for each subscription is possible.

Note: Best practices say you should not store client secrets directly within a configuration like this. It is only done here to demonstrate using different accounts for each subscription.

provider "azurerm" {
  alias           = "app-sub"
  subscription_id = "fabf54e7-fc37-4c80-9d84-bd626f3c2823"
  tenant_id       = "3767d0e1-2c4b-461c-867e-7668ab0d7b06"
  client_id       = "9f55cc89-d82a-4f16-bd7f-09fd99ce9216"
  client_secret   = "AXVTYRAadfvASDDFvb4234EWR$3"
  features {}
}

provider "azurerm" {
  alias           = "prod-sub"
  subscription_id = "7458e6f6-36b2-4d75-b6a5-c143ee9547e2"
  tenant_id       = "3767d0e1-2c4b-461c-867e-7668ab0d7b06"
  client_id       = "a0d7fbe0-dca2-4848-b6ac-ad15e2c31840"
  client_secret   = "BAFHTR3235FEHsdfb%#$W%weF#@a"
  features {}
}

Terraform Deployments to Multiple Azure Subscriptions

With the two provider blocks defined with aliases, you can now deploy resources to each subscription using each alias. You continue defining the resources as you normally would, but you add a new property called provider. This property references the azurerm provider block using the syntax <provider name>.<alias>.

Here is an example of deploying a resource group and a key vault to the prod-sub provider. Note the highlighted lines reference the azurerm.prod-sub alias.

resource "azurerm_resource_group" "rg-prod" {
  provider = azurerm.prod-sub
  name     = "mgmt-prod-rg"
  location = "WestUS2"
}

resource "azurerm_key_vault" "kv-prod" {
  provider            = azurerm.prod-sub
  name                = "kv-jbt-mgmt-prod"
  resource_group_name = azurerm_resource_group.rg-prod.name
  location            = azurerm_resource_group.rg-prod.location
  sku_name            = "standard"
  tenant_id           = "3767d0e1-2c4b-461c-867e-7668ab0d7b06"
}

Here is the rest of the configuration deploying another resource group with a virtual network using the azurerm.app-sub alias.

resource "azurerm_resource_group" "rg-app" {
  provider = azurerm.app-sub
  name     = "app1-rg"
  location = "EastUS"
}

resource "azurerm_virtual_network" "vnet-app" {
  provider            = azurerm.app-sub
  name                = "vnet-app-eastus-1"
  resource_group_name = azurerm_resource_group.rg-app.name
  location            = azurerm_resource_group.rg-app.location
  address_space       = ["10.10.0.0/16"]
}

Terraform Deployments to Multiple Azure Tenants

To deploy to multiple Azure tenants, follow the same process as above, except using a different tenant_id value when defining the azurerm provider. In this scenario, you will definitely need multiple service principals or managed identities to deploy to each respective tenant.

Summary

Deploying to multiple Azure subscriptions from Terraform is as easy as defining multiple providers with aliases. The biggest obstacle is ensuring whatever account(s) you are using for authentication have permissions to each subscription.

Enjoyed this article? Check out more Terraform articles here!

This Post Has 3 Comments

  1. ArieH

    Do this for learning purpose maybe but not for live systems as its not best practices.
    Azure subscription details falls under secrets and you should always prefer running your scripts not manually from your station but rather through a cicd platform allowing to store the secrets in a safe way and refer to them when you execute the pipelines via environment variables or other tokenization tools.

    1. Jeff Brown

      Those are excellent points, and I do mention that putting secret information directly in the provider block like this is not a good idea. Storing information as environment/pipeline variables or from external system (like Azure Key Vault) is the way to go.

Start a discussion!