Çoklu Bulut Durumu

Büyük şirketlerin %92'si birden fazla bulut sağlayıcısı kullanıyor (Flexera State of the Cloud) 2025). Bunun nedenleri çeşitlidir: Satıcıya bağlılığın azaltılması, maliyet optimizasyonu (her iş yükü için en ucuz sağlayıcıyı kullanın), uyumluluk gereksinimleri (AB bölgelerindeki veriler yalnızca Azure'da, AI/ML'de GCP'de, kurumsal iş yükü AWS'de), ve heterojen altyapı getiren satın almalar.

Terraform, çoklu bulutu yönetmek için ideal bir araçtır: yerel olarak destekler Aynı HCL sözdizimine sahip tüm büyük bulutların sağlayıcısı. Sorun değil teknik ve mimari: modüllerin nasıl yapılandırılacağı yeniden kullanımı en üst düzeye çıkarın e karmaşıklığı en aza indirgemek her bulutun benzer kavramlar için farklı API'leri olduğunda.

Ne Öğreneceksiniz

  • Çoklu sağlayıcı yapılandırması: takma ad, çalışma alanı başına sağlayıcı, modül başına sağlayıcı
  • Soyutlama katmanı: bilgi işlem, ağ oluşturma ve veritabanları için tek tip arayüz modülü
  • Bulutlar (VPC/VNet, Örnek/VM) arasındaki anlamsal farklılıkları yönetmeye yönelik desen
  • Çoklu bulut ekipleri için depo yapısı: monorepo ve polyrepo
  • Çoklu bulut gizli yönetimi: Tek gerçek bilgi kaynağı olarak Vault
  • Maliyet optimizasyonu: spot AWS örnekleri, öncelikli GCP, spot Azure

Takma Adla Çoklu Sağlayıcı Yapılandırması

Terraform, aynı sağlayıcının (veya farklı sağlayıcıların) birden fazla örneğini kullanmanıza olanak tanır aracılığıyla aynı formda takma ad. Bu şunun için faydalıdır: Kaynakları aynı bulutun birden fazla bölgesine veya hesabına dağıtmanın yanı sıra farklı sağlayıcıları yapılandırın.

# providers.tf - Configurazione centralizzata di tutti i provider

terraform {
  required_version = "~> 1.7"

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

# AWS: provider principale (EU) e secondario (US) con alias
provider "aws" {
  region = "eu-west-1"  # Provider default
}

provider "aws" {
  alias  = "us_east"
  region = "us-east-1"  # Alias per risorse in US
}

provider "aws" {
  alias   = "disaster_recovery"
  region  = "eu-central-1"  # Alias per DR
}

# Azure: richiede features{} minimo
provider "azurerm" {
  features {}
  subscription_id = var.azure_subscription_id
  # Autenticazione tramite Service Principal o Managed Identity
}

# GCP: configurazione base
provider "google" {
  project = var.gcp_project_id
  region  = "europe-west1"
}

# Vault: per gestione centralizzata dei segreti multi-cloud
provider "vault" {
  address = "https://vault.mycompany.com"
  # Token da variabile d'ambiente VAULT_TOKEN o via AppRole
}

Soyutlama Katmanı: Temel Tasarım Deseni

Çoklu bulutun temel zorluğu, AWS'nin hizmetini EC2 olarak adlandırmasıdır. Azure buna Sanal Makine ve GCP Bilgi İşlem Motoru adını veriyor ancak kavramsal olarak bunlar aynı şey. 'soyutlama katmanı arayüzlü formlar oluşturma bu farklılıkları gizleyen üniformalar.

# Struttura del repository con abstraction layer
terraform-multicloud/
  modules/
    # Layer 1: Moduli cloud-specific (implementazione)
    aws/
      compute/      # EC2, Auto Scaling Groups
      networking/   # VPC, Subnets, Security Groups
      database/     # RDS, Aurora
    azure/
      compute/      # Virtual Machine Scale Sets
      networking/   # VNet, NSG, Subnets
      database/     # Azure Database for PostgreSQL
    gcp/
      compute/      # Instance Groups, MIGs
      networking/   # VPC, Firewall Rules
      database/     # Cloud SQL

    # Layer 2: Moduli di interfaccia (abstraction)
    compute/        # Interfaccia uniforme, delega a aws/ o azure/ o gcp/
    networking/
    database/

    # Layer 3: Composite modules (pattern applicativi)
    three-tier-app/ # Frontend + Backend + Database su cloud specificato
    kubernetes-cluster/

  environments/
    dev/
      main.tf       # Usa composite modules
      providers.tf
    production-aws/
    production-azure/

Soyutlama Katmanını Uygulama

# modules/compute/variables.tf
# Interfaccia uniforme per il modulo compute (cloud-agnostic)

variable "cloud" {
  type        = string
  description = "Cloud provider: aws, azure, gcp"
  validation {
    condition     = contains(["aws", "azure", "gcp"], var.cloud)
    error_message = "cloud deve essere aws, azure o gcp"
  }
}

variable "name" {
  type        = string
  description = "Nome del gruppo di compute (snake_case)"
}

variable "environment" {
  type        = string
  description = "Ambiente: dev, staging, production"
}

variable "instance_type" {
  type        = string
  description = "Tipo istanza nel formato normalizzato: small, medium, large, xlarge"
  validation {
    condition     = contains(["small", "medium", "large", "xlarge"], var.instance_type)
    error_message = "instance_type deve essere small|medium|large|xlarge"
  }
}

variable "min_size" {
  type    = number
  default = 1
}

variable "max_size" {
  type    = number
  default = 10
}

variable "subnet_ids" {
  type        = list(string)
  description = "Lista di subnet/subnetwork IDs dove deployare le istanze"
}

variable "ami_or_image_id" {
  type        = string
  description = "AMI ID (AWS), Image ID (Azure/GCP)"
}

variable "user_data" {
  type        = string
  description = "Script di inizializzazione (cloud-init compatible)"
  default     = ""
}

variable "tags" {
  type    = map(string)
  default = {}
}
# modules/compute/main.tf
# Abstraction layer: delega all'implementazione cloud-specifica

locals {
  # Mapping instance_type -> tipo istanza per ogni cloud
  instance_type_map = {
    aws = {
      small  = "t3.small"
      medium = "t3.medium"
      large  = "t3.large"
      xlarge = "t3.xlarge"
    }
    azure = {
      small  = "Standard_B2s"
      medium = "Standard_B4ms"
      large  = "Standard_D4s_v3"
      xlarge = "Standard_D8s_v3"
    }
    gcp = {
      small  = "e2-small"
      medium = "e2-medium"
      large  = "e2-standard-4"
      xlarge = "e2-standard-8"
    }
  }

  resolved_instance_type = local.instance_type_map[var.cloud][var.instance_type]
}

# Delegazione condizionale all'implementazione cloud-specifica
module "aws_compute" {
  source = "../aws/compute"
  count  = var.cloud == "aws" ? 1 : 0

  name           = var.name
  environment    = var.environment
  instance_type  = local.resolved_instance_type
  min_size       = var.min_size
  max_size       = var.max_size
  subnet_ids     = var.subnet_ids
  ami_id         = var.ami_or_image_id
  user_data      = var.user_data
  tags           = var.tags
}

module "azure_compute" {
  source = "../azure/compute"
  count  = var.cloud == "azure" ? 1 : 0

  name          = var.name
  environment   = var.environment
  vm_size       = local.resolved_instance_type
  min_instances = var.min_size
  max_instances = var.max_size
  subnet_ids    = var.subnet_ids
  source_image  = var.ami_or_image_id
  custom_data   = var.user_data
  tags          = var.tags
}

module "gcp_compute" {
  source = "../gcp/compute"
  count  = var.cloud == "gcp" ? 1 : 0

  name           = var.name
  environment    = var.environment
  machine_type   = local.resolved_instance_type
  min_replicas   = var.min_size
  max_replicas   = var.max_size
  subnetwork_ids = var.subnet_ids
  source_image   = var.ami_or_image_id
  metadata       = var.user_data != "" ? { "user-data" = var.user_data } : {}
  labels         = var.tags
}
# modules/compute/outputs.tf
# Output uniformi indipendentemente dal cloud

output "instance_group_id" {
  value = var.cloud == "aws" ? module.aws_compute[0].autoscaling_group_id :
          var.cloud == "azure" ? module.azure_compute[0].scale_set_id :
          module.gcp_compute[0].instance_group_id
  description = "ID del gruppo di compute (ASG ID, Scale Set ID, MIG ID)"
}

output "load_balancer_dns" {
  value = var.cloud == "aws" ? module.aws_compute[0].alb_dns_name :
          var.cloud == "azure" ? module.azure_compute[0].load_balancer_fqdn :
          module.gcp_compute[0].load_balancer_ip
  description = "DNS o IP del load balancer frontale"
}

Çoklu Bulut Veritabanı Modülü

# modules/database/main.tf
# Astrazione per PostgreSQL su AWS (RDS), Azure (Flexible Server) e GCP (Cloud SQL)

variable "cloud" {
  type = string
}

variable "engine_version" {
  type    = string
  default = "15"  # PostgreSQL major version
}

variable "size" {
  type    = string
  default = "small"  # small, medium, large
}

variable "storage_gb" {
  type    = number
  default = 50
}

variable "backup_retention_days" {
  type    = number
  default = 7
}

variable "multi_az" {
  type        = bool
  default     = false
  description = "Alta disponibilità: Multi-AZ (AWS), Zone-Redundant (Azure), HA (GCP)"
}

locals {
  db_size_map = {
    aws = {
      small  = "db.t3.medium"
      medium = "db.t3.large"
      large  = "db.r6g.xlarge"
    }
    azure = {
      small  = "Standard_D2ds_v4"
      medium = "Standard_D4ds_v4"
      large  = "Standard_D8ds_v4"
    }
    gcp = {
      small  = "db-custom-2-7680"
      medium = "db-custom-4-15360"
      large  = "db-custom-8-30720"
    }
  }
}

# AWS: RDS PostgreSQL
resource "aws_db_instance" "main" {
  count = var.cloud == "aws" ? 1 : 0

  engine            = "postgres"
  engine_version    = var.engine_version
  instance_class    = local.db_size_map.aws[var.size]
  allocated_storage = var.storage_gb
  storage_encrypted = true          # Sempre: CKV_AWS_17
  deletion_protection = true        # Sempre in non-dev

  backup_retention_period = var.backup_retention_days
  multi_az                = var.multi_az

  # Performance Insights
  performance_insights_enabled = true
  performance_insights_retention_period = 7

  tags = {
    ManagedBy   = "terraform"
    Cloud       = "aws"
  }
}

# Azure: PostgreSQL Flexible Server
resource "azurerm_postgresql_flexible_server" "main" {
  count = var.cloud == "azure" ? 1 : 0

  name                = var.name
  resource_group_name = var.resource_group_name
  location            = var.location

  sku_name   = local.db_size_map.azure[var.size]
  version    = var.engine_version

  storage_mb = var.storage_gb * 1024

  backup_retention_days        = var.backup_retention_days
  geo_redundant_backup_enabled = var.multi_az

  high_availability {
    mode = var.multi_az ? "ZoneRedundant" : "Disabled"
  }
}

# GCP: Cloud SQL PostgreSQL
resource "google_sql_database_instance" "main" {
  count = var.cloud == "gcp" ? 1 : 0

  name             = var.name
  database_version = "POSTGRES_${var.engine_version}"

  settings {
    tier = local.db_size_map.gcp[var.size]

    disk_size = var.storage_gb
    disk_autoresize = true

    backup_configuration {
      enabled            = true
      point_in_time_recovery_enabled = true
      transaction_log_retention_days = var.backup_retention_days
    }

    availability_type = var.multi_az ? "REGIONAL" : "ZONAL"

    insights_config {
      query_insights_enabled = true
    }
  }

  deletion_protection = true
}

Vault ile Çoklu Bulut Gizli Yönetimi

# modules/secrets/main.tf
# HashiCorp Vault come source of truth unica per segreti multi-cloud

variable "cloud" {
  type = string
}

variable "environment" {
  type = string
}

variable "application" {
  type = string
}

# Leggi i segreti da Vault
data "vault_generic_secret" "app_secrets" {
  path = "secret/${var.environment}/${var.application}"
}

# Distribuisci i segreti al cloud appropriato

# AWS: crea Secrets Manager entry dal segreto Vault
resource "aws_secretsmanager_secret" "app" {
  count = var.cloud == "aws" ? 1 : 0
  name  = "${var.environment}/${var.application}"

  tags = {
    ManagedBy = "terraform"
    Source    = "vault"
  }
}

resource "aws_secretsmanager_secret_version" "app" {
  count         = var.cloud == "aws" ? 1 : 0
  secret_id     = aws_secretsmanager_secret.app[0].id
  secret_string = jsonencode(data.vault_generic_secret.app_secrets.data)
}

# Azure: crea Key Vault secrets dal segreto Vault
resource "azurerm_key_vault_secret" "app" {
  for_each = var.cloud == "azure" ? data.vault_generic_secret.app_secrets.data : {}

  name         = replace(each.key, "_", "-")  # Azure Key Vault: no underscore
  value        = each.value
  key_vault_id = var.azure_key_vault_id
}

# GCP: crea Secret Manager entries
resource "google_secret_manager_secret" "app" {
  for_each  = var.cloud == "gcp" ? data.vault_generic_secret.app_secrets.data : {}
  secret_id = "${var.environment}-${var.application}-${each.key}"

  replication {
    auto {}
  }
}

resource "google_secret_manager_secret_version" "app" {
  for_each = var.cloud == "gcp" ? data.vault_generic_secret.app_secrets.data : {}

  secret      = google_secret_manager_secret.app[each.key].id
  secret_data = each.value
}

Bir Uygulamanın Çoklu Bulut Dağıtımı

# environments/production-multicloud/main.tf
# Deploy della stessa applicazione su AWS (primary) e Azure (DR)

locals {
  app_name    = "catalog-api"
  environment = "production"
  common_tags = {
    Application = local.app_name
    Environment = local.environment
    ManagedBy   = "terraform"
    CostCenter  = "product-team"
  }
}

# Networking AWS (Primary)
module "aws_networking" {
  source = "../../modules/aws/networking"

  name        = "${local.app_name}-${local.environment}"
  cidr_block  = "10.0.0.0/16"
  az_count    = 3
  tags        = local.common_tags
}

# Networking Azure (DR)
module "azure_networking" {
  source = "../../modules/azure/networking"

  name                = "${local.app_name}-${local.environment}"
  resource_group_name = azurerm_resource_group.dr.name
  location            = "West Europe"
  address_space       = ["10.1.0.0/16"]
  tags                = local.common_tags
}

# Compute AWS (Primary) - Usa il modulo uniforme
module "compute_primary" {
  source = "../../modules/compute"

  cloud         = "aws"
  name          = "${local.app_name}-primary"
  environment   = local.environment
  instance_type = "large"
  min_size      = 3
  max_size      = 20
  subnet_ids    = module.aws_networking.private_subnet_ids
  ami_or_image_id = data.aws_ami.app.id
  tags          = local.common_tags
}

# Compute Azure (DR) - Stessa interfaccia, cloud diverso
module "compute_dr" {
  source = "../../modules/compute"

  cloud         = "azure"
  name          = "${local.app_name}-dr"
  environment   = local.environment
  instance_type = "large"
  min_size      = 1  # DR: capacità ridotta finché non necessaria
  max_size      = 20
  subnet_ids    = module.azure_networking.subnet_ids
  ami_or_image_id = var.azure_vm_image_id
  tags          = local.common_tags
}

# Database AWS (Primary)
module "database_primary" {
  source = "../../modules/database"

  cloud                 = "aws"
  name                  = "${local.app_name}-primary"
  size                  = "large"
  storage_gb            = 200
  backup_retention_days = 30
  multi_az              = true  # HA in production
}

# Database Azure (DR)
module "database_dr" {
  source = "../../modules/database"

  cloud                 = "azure"
  name                  = "${local.app_name}-dr"
  resource_group_name   = azurerm_resource_group.dr.name
  location              = "West Europe"
  size                  = "medium"
  storage_gb            = 200
  backup_retention_days = 7
  multi_az              = false  # DR: single zone per costi
}

# Output per entrambi gli ambienti
output "primary_endpoint" {
  value = module.compute_primary.load_balancer_dns
}

output "dr_endpoint" {
  value = module.compute_dr.load_balancer_dns
}

Maliyet Optimizasyonu Çoklu Bulut: Spot/Öncelikli

# modules/compute-spot/main.tf
# Modulo unificato per spot/preemptible instances (70-90% risparmio vs on-demand)

variable "cloud" {
  type = string
}

variable "spot_percentage" {
  type        = number
  default     = 70
  description = "Percentuale di istanze spot (0-100). Il resto è on-demand."
}

# AWS: Mixed Instance Policy con Spot
resource "aws_autoscaling_group" "mixed" {
  count = var.cloud == "aws" ? 1 : 0

  mixed_instances_policy {
    instances_distribution {
      on_demand_base_capacity                  = 2  # Minimo garantito on-demand
      on_demand_percentage_above_base_capacity = 100 - var.spot_percentage
      spot_allocation_strategy                 = "price-capacity-optimized"
    }

    launch_template {
      launch_template_specification {
        launch_template_id = aws_launch_template.app[0].id
        version            = "$Latest"
      }

      # Tipi istanza diversi per aumentare disponibilità spot
      override {
        instance_type = "t3.large"
      }
      override {
        instance_type = "t3a.large"
      }
      override {
        instance_type = "m5.large"
      }
    }
  }

  min_size = var.min_size
  max_size = var.max_size
}

# GCP: Preemptible instances nel MIG
resource "google_compute_instance_template" "preemptible" {
  count = var.cloud == "gcp" ? 1 : 0

  scheduling {
    preemptible        = var.spot_percentage > 0
    automatic_restart  = false  # Obbligatorio per preemptible
    on_host_maintenance = "TERMINATE"
  }
}

# Azure: Spot VMs con eviction policy
resource "azurerm_orchestrated_virtual_machine_scale_set" "spot" {
  count = var.cloud == "azure" ? 1 : 0

  priority        = "Spot"
  eviction_policy = "Deallocate"  # o "Delete" per risparmio storage
  max_bid_price   = -1  # -1 = paga fino al prezzo on-demand
}

Kaçınılması Gereken Çoklu Bulut Anti-Desen

Çoklu Bulut IaC Mimarisinde Yaygın Hatalar

  • Soyutlama çok zorlandı: Tüm hizmetlerin eşdeğerleri yoktur doğrudan bulutların arasında. AWS SQS, Azure Service Bus ve GCP Pub/Sub benzerdir ancak aynı değildir. Çok genel bir form, buluta özgü önemli özellikleri gizler.
  • Tüm bulutlar için tek bir durum dosyası: Bulut başına ayrı durum dosyası ve çevreye göre. Her şeyi tek bir durum dosyasına koymak yolsuzluk riskini artırır ve işlemleri yavaşlatır.
  • Aşırı hoşgörülü sağlayıcılar: verme AdministratorAccess Terraform sağlayıcısına. Her ortama özel IAM'in en az ayrıcalıklı rollerini kullanın.
  • .tfs'de sabit kodlanmış kimlik bilgileri: Her zaman ortam değişkenlerini kullanın, OIDC veya Kasa. Terraform dosyalarında asla AWS anahtarları kullanmayın.

Sonuçlar ve Sonraki Adımlar

Terraform'daki soyutlama katmanı modeli altyapı oluşturmanıza olanak tanır bakımı yapılabilir çoklu bulut: sağlayıcılar arasındaki farklar modüller halinde özetlenmiştir buluta özgü olduğundan tüketiciler tek tip bir arayüz kullanır ve buluta geçiş yapar bu bir değişkende yapılan değişikliktir, altyapının yeniden yazılması değil.

Anahtar, doğru soyutlama düzeyini bulmaktır: çok fazla gizli farklılık Formu kullanılamaz hale getirirken, çok fazla ayrıntıyı ortaya çıkaran bir form buluta özgü, tekdüzelik avantajını kaybeder.

Serideki Sonraki Yazılar

  • Madde 8: Terraform için GitOps — Flux TF Kontrol Cihazı, Spacelift ve Drift Detection: Terraform'u sürekli mutabakatla GitOps paradigmasına getiriyor depo ortamından.
  • Madde 9: Terraform vs Pulumi vs OpenTofu - Karşılaştırma Final 2026: Bağlama göre hangi IaC aracının ne zaman seçileceği.