Multi-Cloud Terraform: AWS, Azure & GCP
Multi-cloud Terraform uses multiple providers in a single configuration to manage infrastructure across AWS, Azure, and GCP from one unified codebase and workflow.
What You'll Learn
In this tutorial, you will learn how to configure Terraform for multi-cloud deployments across AWS, Azure, and GCP, share data between providers, and design cloud-agnostic modules.
Why It Matters
Most organizations use multiple cloud providers for redundancy, cost optimization, or compliance. Managing each cloud with separate tools increases overhead. Terraform provides a single language and workflow across all three major clouds.
Real-World Use
DodaTech runs compute workloads on AWS, Active Directory and identity on Azure, and BigQuery analytics on GCP. Durga Antivirus Pro uses a multi-cloud Terraform configuration that provisions AWS EC2 instances, Azure SQL databases, and GCP Cloud Storage buckets from one repository.
Multi-Provider Configuration
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
provider "azurerm" {
features {}
}
provider "google" {
project = "my-gcp-project"
region = "us-central1"
}
Expected output: <a href="/devops/terraform/">terraform</a> init downloads all three provider plugins. Each provider authenticates using its own mechanism (AWS env vars, Azure CLI, GCP application default credentials).
Cross-Cloud Resource Creation
Create resources in each cloud from the same configuration:
# AWS compute
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "multi-cloud-web"
}
}
# Azure database
resource "azurerm_resource_group" "main" {
name = "dodatech-rg"
location = "East US"
}
resource "azurerm_mssql_server" "db" {
name = "dodatech-sqlserver"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
version = "12.0"
administrator_login = "adminuser"
administrator_login_password = random_password.db.result
}
# GCP storage
resource "google_storage_bucket" "data" {
name = "dodatech-multi-cloud-data"
location = "US"
force_destroy = false
}
Expected output: One apply creates an AWS EC2 instance, an Azure resource group with SQL Server, and a GCP storage bucket. Each resource uses its respective provider's authentication.
Sharing Data Across Clouds
Use Terraform outputs and data sources to share information between clouds:
# Output the public IP for use in Azure NSG rules
output "aws_web_ip" {
value = aws_instance.web.public_ip
}
# Output the Azure SQL connection string
output "azure_sql_connection" {
value = azurerm_mssql_server.db.fully_qualified_domain_name
sensitive = true
}
# Output the GCP bucket URL
output "gcp_bucket_url" {
value = "gs://${google_storage_bucket.data.name}"
}
Expected output: After apply, all cross-cloud resource identifiers are available. Other tools and configurations can consume these outputs via remote state data sources.
Cloud-Agnostic Modules
Design modules that abstract provider differences:
# modules/compute/main.tf
variable "cloud" {
description = "Cloud provider: aws, azure, or gcp"
type = string
}
variable "name" {
description = "Instance name"
type = string
}
variable "size" {
description = "Instance size or SKU"
type = string
}
resource "aws_instance" "compute" {
count = var.cloud == "aws" ? 1 : 0
ami = var.ami_id
instance_type = var.size
tags = {
Name = var.name
}
}
resource "azurerm_linux_virtual_machine" "compute" {
count = var.cloud == "azure" ? 1 : 0
name = var.name
resource_group_name = var.resource_group_name
location = var.location
size = var.size
admin_username = "adminuser"
network_interface_ids = [var.nic_id]
admin_password = random_password.vm.result
disable_password_authentication = false
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
}
Expected output: The module creates a compute instance on the selected cloud provider. Same interface, different provider implementations internally.
Remote State Across Clouds
Share outputs between configurations in different clouds:
# Configuration A: AWS infrastructure
terraform {
backend "s3" {
bucket = "dodatech-state-aws"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
# Configuration B: Consume AWS outputs
data "terraform_remote_state" "network" {
backend = "s3"
config = {
bucket = "dodatech-state-aws"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
resource "azurerm_resource_group" "main" {
name = "dodatech-rg"
location = "East US"
}
resource "azurerm_network_security_group" "web" {
name = "web-nsg"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
security_rule {
name = "AllowHTTPFromAWS"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "80"
source_address_prefixes = data.terraform_remote_state.network.outputs.vpc_cidr
destination_address_prefix = "*"
}
}
Expected output: The Azure configuration reads the AWS VPC CIDR from remote state and creates a security group that allows traffic from the AWS network.
Common Mistakes
1. Inconsistent Tagging Across Clouds
Each cloud has different tagging conventions. Define a standard tag set and enforce it across all providers.
2. Duplicate Secret Management
Managing secrets separately per provider increases complexity. Use Vault or Terraform Cloud variables for all secrets.
3. Ignoring Provider-Specific Quotas
Each cloud has different service limits. Multi-cloud configurations must handle provider-specific constraints.
4. Mixed Credential Sources
Using environment variables for AWS, CLI for Azure, and service account for GCP makes debugging authentication issues difficult.
5. Not Testing Cross-Cloud Dependencies
Resources in one cloud that depend on resources in another cloud require careful ordering and may fail if the remote provider is unavailable.
Practice Questions
1. How do you configure Terraform to manage multiple cloud providers? Declare multiple providers in required_providers and configure each with its own provider block, typically using alias for multiple configurations.
2. How can you share data between AWS and Azure configurations? Use Terraform_remote_state data sources to read outputs from one cloud's state file in another cloud's configuration.
3. What is a cloud-agnostic module? A module that uses count or for_each with conditional logic to create resources on different providers based on a cloud selection variable.
4. Why might you use multiple cloud providers? For redundancy, cost optimization, compliance requirements, or using best-in-class services from each provider.
5. Challenge: Create a Terraform configuration that provisions an AWS EC2 instance, an Azure SQL database, and a GCP storage bucket. Output relevant connection details from each.
Mini Project: Multi-Cloud Application Stack
Create a Terraform configuration with three modules: AWS for compute (EC2), Azure for database (Azure SQL), and GCP for storage (Cloud Storage). Use variable maps per cloud provider for region, size, and tags. Output connection strings for each component.
Related Concepts
What's Next
Build multi-cloud Terraform configurations, then apply Production Best Practices to ensure reliability, scalability, and team collaboration.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro