By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
(For engineers who need to deploy across AWS, Azure, GCP, or hybrid clouds—without the theory fluff.)
You’re a cloud engineer at a company that just acquired a startup running on Azure. Your existing infrastructure is on AWS. Leadership wants a unified monitoring dashboard (Grafana on GCP) and a single CI/CD pipeline (GitHub Actions) that deploys to all three clouds. Oh, and compliance requires that no single cloud provider holds all your data.
Multi-provider Terraform lets you: - Avoid vendor lock-in (e.g., migrate from AWS to Azure without rewriting everything).- Leverage best-of-breed services (e.g., AWS Lambda + Azure Cognitive Services + GCP BigQuery).- Enforce consistent security policies (e.g., IAM roles, networking, logging) across clouds.- Failover between clouds (e.g., if AWS us-east-1 goes down, traffic shifts to Azure).
What breaks if you ignore this?- You’ll end up with snowflake deployments (e.g., AWS uses Terraform, Azure uses ARM templates, GCP uses gcloud CLI).- Security drift: One cloud’s IAM policies might allow public S3 buckets while another’s doesn’t.- Cost overruns: You might accidentally spin up duplicate resources (e.g., a NAT gateway in AWS and Azure) because you didn’t centralize control.
Real-world scenario:You inherit a Terraform codebase that deploys a multi-region AWS VPC and a GCP Cloud SQL database. Your task: 1. Add Azure Blob Storage for backups.2. Ensure all three clouds use the same Terraform state file (stored in AWS S3).3. Make sure secrets (API keys, DB passwords) are never hardcoded in the repo.
provider
version = "~> 4.0"
hcl provider "aws" { region = "us-east-1" version = "~> 4.0" }
alias
us-east-1
eu-west-1
hcl provider "aws" { alias = "east" region = "us-east-1" } provider "aws" { alias = "west" region = "us-west-2" }
terraform
backend
hcl terraform { backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" } required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } } }
data
hcl data "aws_vpc" "main" { id = "vpc-123456" }
module
hcl module "aws_vpc" { source = "./modules/vpc" cloud = "aws" } module "azure_vnet" { source = "./modules/vpc" cloud = "azure" }
remote_state
depends_on
hcl data "terraform_remote_state" "aws_network" { backend = "s3" config = { bucket = "my-aws-state" key = "network/terraform.tfstate" region = "us-east-1" } }
dynamic
security_group
network_security_group
hcl dynamic "ingress" { for_each = var.cloud == "aws" ? [1] : [] content { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } }
null_resource
local-exec
hcl resource "null_resource" "configure_vpn" { provisioner "local-exec" { command = "python3 configure_vpn.py ${aws_vpn_gateway.main.id} ${azurerm_virtual_network_gateway.main.id}" } }
Scenario: Deploy a web app with: - Frontend: AWS S3 + CloudFront (static website).- Backend: Azure App Service (Node.js API).- Database: GCP Cloud SQL (PostgreSQL).- Monitoring: Grafana (hosted on GCP).
✅ AWS account with IAM admin permissions.✅ Azure account with Owner role.✅ GCP account with Editor role.✅ Terraform v1.3+ installed.✅ aws, az, and gcloud CLI tools installed.
Owner
Editor
aws
az
gcloud
Create main.tf:
main.tf
terraform { backend "s3" { bucket = "my-terraform-state-bucket" # Replace with your bucket key = "multi-cloud-app/terraform.tfstate" region = "us-east-1" } required_providers { aws = { source = "hashicorp/aws" version = "~> 4.0" } azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } google = { source = "hashicorp/google" version = "~> 4.0" } } } provider "aws" { region = "us-east-1" } provider "azurerm" { features {} } provider "google" { project = "my-gcp-project" # Replace with your GCP project region = "us-central1" }
Verify:
terraform init
✅ Should output:
Initializing the backend...Successfully configured the backend "s3"!
Add to main.tf:
resource "aws_s3_bucket" "frontend" { bucket = "my-multi-cloud-app-frontend" # Must be globally unique acl = "public-read" website { index_document = "index.html" } } resource "aws_cloudfront_distribution" "frontend" { origin { domain_name = aws_s3_bucket.frontend.bucket_regional_domain_name origin_id = "S3-${aws_s3_bucket.frontend.id}" } enabled = true default_root_object = "index.html" default_cache_behavior { allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] target_origin_id = "S3-${aws_s3_bucket.frontend.id}" forwarded_values { query_string = false cookies { forward = "none" } } viewer_protocol_policy = "redirect-to-https" } restrictions { geo_restriction { restriction_type = "none" } } viewer_certificate { cloudfront_default_certificate = true } }
Apply:
terraform apply -target=aws_s3_bucket.frontend -target=aws_cloudfront_distribution.frontend
✅ Verify: - Upload index.html to the S3 bucket.- Visit the CloudFront URL (output by Terraform).
index.html
resource "azurerm_resource_group" "backend" { name = "my-multi-cloud-app-rg" location = "East US" } resource "azurerm_app_service_plan" "backend" { name = "my-app-service-plan" location = azurerm_resource_group.backend.location resource_group_name = azurerm_resource_group.backend.name kind = "Linux" reserved = true sku { tier = "Basic" size = "B1" } } resource "azurerm_app_service" "backend" { name = "my-multi-cloud-app-backend" location = azurerm_resource_group.backend.location resource_group_name = azurerm_resource_group.backend.name app_service_plan_id = azurerm_app_service_plan.backend.id site_config { linux_fx_version = "NODE|14-lts" } }
terraform apply -target=azurerm_resource_group.backend -target=azurerm_app_service_plan.backend -target=azurerm_app_service.backend
✅ Verify: - Check the Azure Portal → App Services → my-multi-cloud-app-backend.- Deploy a Node.js app to test.
my-multi-cloud-app-backend
resource "google_sql_database_instance" "db" { name = "my-multi-cloud-db" database_version = "POSTGRES_13" region = "us-central1" settings { tier = "db-f1-micro" } deletion_protection = false # ⚠️ Disable for demo (enable in prod!) } resource "google_sql_database" "db" { name = "app_db" instance = google_sql_database_instance.db.name } resource "google_sql_user" "db_user" { name = "app_user" instance = google_sql_database_instance.db.name password = "SuperSecret123!" # ⚠️ Use Terraform secrets in prod! }
terraform apply -target=google_sql_database_instance.db -target=google_sql_database.db -target=google_sql_user.db_user
✅ Verify: - Check GCP Console → SQL → my-multi-cloud-db.- Connect via psql: bash gcloud sql connect my-multi-cloud-db --user=app_user
my-multi-cloud-db
psql
bash gcloud sql connect my-multi-cloud-db --user=app_user
Add to outputs.tf:
outputs.tf
output "frontend_url" { value = aws_cloudfront_distribution.frontend.domain_name } output "backend_url" { value = azurerm_app_service.backend.default_site_hostname } output "db_connection_name" { value = google_sql_database_instance.db.connection_name }
Apply all:
terraform apply
✅ Verify: - The frontend (CloudFront) should call the backend (Azure App Service).- The backend should connect to the database (GCP Cloud SQL).
sops
vault
hcl data "aws_secretsmanager_secret_version" "db_password" { secret_id = "prod/db_password" }
t3.micro
t3.large
Environment=prod
Owner=team-devops
modules/vpc
modules/database
Typical question patterns:1. "Which Terraform feature lets you deploy to AWS and Azure in the same config?" - ✅ Answer: provider blocks with alias. - ❌ Trap: "Use module" (modules are for reusability, not multi-cloud).
data "terraform_remote_state"
❌ Trap: "Use aws_vpc data source in Azure" (won’t work).
aws_vpc
"What’s the risk of not pinning provider versions?"
Key trap distinctions:- provider vs. module: Providers define where to deploy; modules define what to deploy.- alias vs. default provider: alias is for multiple instances of the same provider (e.g., AWS us-east-1 and eu-west-1).- backend vs. remote_state: backend stores your state; remote_state reads another state file.
default
Task: Deploy a multi-region AWS setup with: 1. A VPC in us-east-1 (default provider).2. A VPC in eu-west-1 (aliased provider).3. A VPC peering connection between them.
Solution:
provider "aws" { alias = "europe" region = "eu-west-1" } resource "aws_vpc" "us" { cidr_block = "10.0.0.0/16" } resource "aws_vpc" "eu" { provider = aws.europe cidr_block = "10.1.0.0/16" } resource "aws_vpc_peering_connection" "peer" { vpc_id = aws_vpc.us.id peer_vpc_id = aws_vpc.eu.id peer_region = "eu-west-1" auto_accept = false # Must be manually accepted in the other region }
Why it works: - alias lets you define a second AWS provider for eu-west-1.- peer_region tells AWS to create the peering connection across regions.
peer_region
provider "aws" { alias = "east" }
provider = aws.east
terraform state list
terraform import aws_instance.my_vm i-123456
backend "s3"
dynamic "ingress"
172.31.0.0/16
azurerm_resource_group
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.