Architecture

Multi-Cloud Terraform Architecture: AWS, Azure, and GCP in One Configuration

By CloudFormation Team ยท 2025-06-05 ยท 14 min read

Why Multi-Cloud?

Organizations adopt multi-cloud strategies for several compelling reasons: avoiding vendor lock-in, optimising for best-of-breed services (e.g. GCP BigQuery for analytics, AWS for compute, Azure for enterprise identity), regulatory requirements mandating geographic data distribution, and resilience against regional outages.

Terraform is uniquely suited to multi-cloud because it uses a single declarative language (HCL) for all providers. One team, one workflow, many clouds.

Configuring Multiple Providers

Terraform lets you define multiple providers in the same configuration. Each has its own required_providers entry and authentication:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.90"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = var.aws_region
}

provider "azurerm" {
  features {}
  subscription_id = var.azure_subscription_id
}

provider "google" {
  project = var.gcp_project_id
  region  = var.gcp_region
}

Provider Aliases for Multi-Region

Use alias to manage resources across multiple regions within the same provider:

provider "aws" {
  region = "us-east-1"
  alias  = "us_east"
}

provider "aws" {
  region = "eu-west-1"
  alias  = "eu_west"
}

resource "aws_instance" "us" {
  provider      = aws.us_east
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.medium"
}

resource "aws_instance" "eu" {
  provider      = aws.eu_west
  ami           = "ami-01720b5f421cf0179"
  instance_type = "t3.medium"
}

Cross-Cloud Networking Pattern

A common multi-cloud pattern is connecting AWS and Azure via VPN or ExpressRoute. Here's how to set up the Terraform resources for both sides:

# AWS side โ€” Virtual Private Gateway
resource "aws_vpn_gateway" "main" {
  vpc_id = aws_vpc.main.id
  tags   = { Name = "multi-cloud-vpgw" }
}

resource "aws_customer_gateway" "azure" {
  bgp_asn    = 65000
  ip_address = azurerm_public_ip.vpn.ip_address
  type       = "ipsec.1"
}

resource "aws_vpn_connection" "to_azure" {
  vpn_gateway_id      = aws_vpn_gateway.main.id
  customer_gateway_id = aws_customer_gateway.azure.id
  type                = "ipsec.1"
  static_routes_only  = false
}

# Azure side โ€” VPN Gateway
resource "azurerm_virtual_network_gateway" "main" {
  name                = "azure-vpn-gateway"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  type                = "Vpn"
  vpn_type            = "RouteBased"
  sku                 = "VpnGw1"

  ip_configuration {
    public_ip_address_id = azurerm_public_ip.vpn.id
    subnet_id            = azurerm_subnet.gateway.id
  }
}

Shared Remote State for Multi-Cloud

When infrastructure spans multiple clouds, you need a way for Terraform configurations to reference each other. Use remote state data sources:

# In your networking module (stored in S3)
terraform {
  backend "s3" {
    bucket = "company-terraform-state"
    key    = "networking/terraform.tfstate"
    region = "us-east-1"
  }
}

# In your application module โ€” read networking outputs
data "terraform_remote_state" "networking" {
  backend = "s3"
  config = {
    bucket = "company-terraform-state"
    key    = "networking/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_instance" "app" {
  subnet_id = data.terraform_remote_state.networking.outputs.private_subnet_id
}

Multi-Cloud Directory Structure

Organise your multi-cloud Terraform project by cloud and environment:

infrastructure/
โ”œโ”€โ”€ modules/
โ”‚   โ”œโ”€โ”€ networking/          # Reusable networking module
โ”‚   โ”œโ”€โ”€ compute/             # Reusable compute module
โ”‚   โ””โ”€โ”€ database/            # Reusable database module
โ”œโ”€โ”€ aws/
โ”‚   โ”œโ”€โ”€ dev/
โ”‚   โ”‚   โ”œโ”€โ”€ main.tf
โ”‚   โ”‚   โ””โ”€โ”€ terraform.tfvars
โ”‚   โ””โ”€โ”€ prod/
โ”‚       โ”œโ”€โ”€ main.tf
โ”‚       โ””โ”€โ”€ terraform.tfvars
โ”œโ”€โ”€ azure/
โ”‚   โ”œโ”€โ”€ dev/
โ”‚   โ””โ”€โ”€ prod/
โ””โ”€โ”€ gcp/
    โ”œโ”€โ”€ dev/
    โ””โ”€โ”€ prod/

Authentication Best Practices

CloudDevelopmentCI/CD (Production)
AWSaws configureOIDC with GitHub Actions / IAM Role
Azureaz loginService Principal + Federated Identity
GCPgcloud auth application-default loginWorkload Identity Federation
Never put cloud credentials in Terraform variables or .tfvars files. Use environment variables, instance profiles, or managed identity instead.

Common Multi-Cloud Patterns

Active-Active Multi-Cloud

Traffic is distributed across two clouds simultaneously. Requires a global load balancer (e.g. Cloudflare or AWS Global Accelerator) and data replication between clouds. Highest availability, highest complexity.

Cloud-of-Choice by Workload

Different workloads run on the best-suited cloud. Example: ML training on GCP Vertex AI, production web on AWS, enterprise identity on Azure AD. Data flows between clouds via API or managed replication.

Active-Passive Failover

Primary workload runs on one cloud; failover region is on another. Lower cost than active-active, provides cloud-level resilience for regulatory or business continuity requirements.

Designing Multi-Cloud Infrastructure Visually

CloudFormation's drag-and-drop canvas supports all 9 cloud providers simultaneously on the same canvas. Draw connections between AWS Transit Gateway and Azure ExpressRoute, then click Generate to get a complete multi-provider Terraform configuration with all provider blocks, cross-references, and variables correctly wired.

Try CloudFormation Free

Design infrastructure visually and generate production-ready Terraform in seconds โ€” 693 resources across 9 cloud providers.

๐Ÿš€ Open the Designer โ†’