Did you know Terraform can deploy to multiple Azure subscriptions? Terraform can deploy to multiple subscriptions by defining multiple provider blocks. You then use aliases to identify 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:
- An Azure subscription with sufficient permissions at the tenant level, like a Global Administrator
- Terraform open source version
- Here is a guide to Install and Configure Terraform on Windows
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 must 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 need multiple service principals or managed identities to deploy to each respective tenant.
Summary
Deploying 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!
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.
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.
Pingback: Azure Top 5 for January 17, 2022 | Jeff Brown Tech
Do you have a work around for module? many people says for module configuraton azurerm has a bug and it doesn’st support right now.
https://www.terraform.io/language/providers/configuration#multiple-provider-instances
Can you provide an example of what you mean for a module? Or where you have read about this bug?
same code managing with tf modules, typically it shouldn’t have the duplicate data.
I’m still not quite sure what you are asking, but you can configure the AzureRM provider with an alias, then reference this alias when creating a resource or calling a module.
Reference: https://www.terraform.io/language/providers/configuration#alias-multiple-provider-configurations
Code example:
module "aws_vpc" {
source = "./aws_vpc"
providers = {
aws = aws.west
}
}