Terraform alias — Solving Multi-Subscription Deployment Challenges

 

In cloud environments, especially in Azure, infrastructure is often spread across multiple subscriptions for security and organizational reasons.

Hub and Spoke Topology is a classic pattern where:

    • The Hub VNet (shared resources, security services, DNS zones etc.) lives in its own subscription.
    • The Spoke VNets (application workloads) live in different subscriptions, each managing their own state files.

While everything works smoothly for independent deployments, the real problem starts when you need cross-subscription interactions, like:

    • VNet Peering between Hub and Spoke (both sides need peering objects)
    • Private Endpoint + Private DNS Zones (Private Endpoint in spoke, DNS zone in hub)

By default, Terraform executes operations only against a single provider configuration (in our case, a single Azure subscription).

So, how can we create resources in two different subscriptions at the same time from within the spoke configuration?

Answer → Terraform alias provider.


📌 Scenario — VNet Peering between Hub and Spoke

    • Hub VNet → Deployed already → In Subscription A (critical, no change expected)
    • Spoke VNet → Deployed already → In Subscription B

Now from Spoke side (where we are working right now), we need:

    • Peering from Spoke → Hub → Simple, as spoke is local subscription
    • Peering from Hub → Spoke → Problematic, because Hub is in a different subscription

✅ Solution → Use Terraform Provider Alias for Hub Subscription


📦 Terraform Code Example

Step 1: Define Two Providers (One Default, One with Alias)

provider "azurerm" {
  features {}

  subscription_id = var.spoke_subscription_id
}

provider "azurerm" {
  alias           = "hub"
  features {}

  subscription_id = var.hub_subscription_id
}

✅ The default provider (without alias) will be used for Spoke.
✅ The aliased provider (“hub”) will be used for Hub Subscription.


Step 2: Create Peering from Spoke → Hub (Using Default Provider)

resource "azurerm_virtual_network_peering" "spoke_to_hub" {
  name                      = "SpokeToHub"
  resource_group_name       = var.spoke_vnet_rg
  virtual_network_name      = var.spoke_vnet_name
  remote_virtual_network_id = var.hub_vnet_id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = false
  use_remote_gateways       = false
}

This will create the peering on the Spoke side.


Step 3: Create Peering from Hub → Spoke (Using provider = azurerm.hub)

resource "azurerm_virtual_network_peering" "hub_to_spoke" {
  provider                  = azurerm.hub
  name                      = "HubToSpoke"
  resource_group_name       = var.hub_vnet_rg
  virtual_network_name      = var.hub_vnet_name
  remote_virtual_network_id = var.spoke_vnet_id
  allow_forwarded_traffic   = true
  allow_gateway_transit     = true
  use_remote_gateways       = true
}

✅ Both peering objects can now be created from Spoke Terraform plan/apply.


📘 Private DNS Zone Example (Hub) + Private Endpoint (Spoke)

resource "azurerm_private_dns_a_record" "private_endpoint_dns" {
  provider              = azurerm.hub
  name                  = "myservice"
  zone_name             = var.private_dns_zone_name
  resource_group_name   = var.hub_dns_rg
  ttl                   = 300
  records               = [var.private_endpoint_ip]
}

✅ Another real-world use case where alias comes handy.


📌 Summary

Resource Type Subscription Provider Usage
Spoke → Hub Peering Spoke Default provider
Hub → Spoke Peering Hub provider = azurerm.hub
Private Endpoint Spoke Default provider
Private DNS A Record Hub provider = azurerm.hub

📦 Using Terraform Alias in Azure DevOps Pipelines (Single Subscription Context)

In enterprise CI/CD pipelines like Azure DevOps Pipelines, Terraform plans and applies are typically targeted at a single subscription at a time.

So how do you use alias in this case?

Answer → By authenticating the alias provider separately using Service Principal details.

    • Create a separate SPN for Hub Subscription (Contributor/Network Contributor access).
    • Store its client_id, client_secret, tenant_id, and subscription_id in pipeline variables or secrets.
    • Use these for alias provider authentication in Terraform.
provider "azurerm" {
  alias           = "hub"
  features {}

  subscription_id = var.hub_subscription_id
  tenant_id       = var.hub_tenant_id
  client_id       = var.hub_client_id
  client_secret   = var.hub_client_secret
}

✅ Azure DevOps Pipeline Example

trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

variables:
  spoke_subscription_id: 'xxxxxxxx-xxxx'
  hub_subscription_id: 'yyyyyyyy-yyyy'
  hub_tenant_id: 'zzzzzzzz-zzzz'
  hub_client_id: 'aaaaaaaa-aaaa'
  hub_client_secret: $(hub_client_secret)

steps:

- task: TerraformInstaller@1
  inputs:
    terraformVersion: '1.5.7'

- task: AzureCLI@2
  displayName: 'Terraform Plan'
  inputs:
    azureSubscription: 'Azure-Spoke-ServiceConnection'
    scriptType: bash
    scriptLocation: inlineScript
    inlineScript: |
      terraform init
      export ARM_HUB_CLIENT_ID=$(hub_client_id)
      export ARM_HUB_CLIENT_SECRET=$(hub_client_secret)
      export ARM_HUB_TENANT_ID=$(hub_tenant_id)
      terraform plan -out=tfplan

- task: AzureCLI@2
  displayName: 'Terraform Apply'
  inputs:
    azureSubscription: 'Azure-Spoke-ServiceConnection'
    scriptType: bash
    scriptLocation: inlineScript
    inlineScript: |
      terraform apply tfplan

✅ Hub alias provider will authenticate using SPN.
✅ Spoke default provider will authenticate using Service Connection.


✅ When to Use Alias

    • Cross-subscription VNet Peering
    • Private DNS Zone management across subscriptions
    • Shared resources residing in different subscriptions
    • Hybrid cloud scenarios (Azure Global + Azure China, etc.)

✅ Conclusion

Terraform alias is not just a feature — it’s a life-saver when dealing with complex enterprise environments like Hub & Spoke topologies in Azure.

Without alias:

    • 🚨 Running multiple plans and applies becomes mandatory
    • 🚨 You risk making changes in critical Hub environments manually
    • 🚨 Configuration drift and human errors increase

With alias, you achieve:

    • ✅ Single plan/apply covering both Spoke and Hub resources
    • ✅ Avoid touching Hub’s Terraform state again and again
    • ✅ Seamless Azure DevOps Pipeline integration for automation

→ Use Terraform alias smartly and safely automate multi-subscription Azure deployments with ease.

Leave a comment