Terraform State Dosyası: Uzak Backend Yapılandırması

Terraform ile ciddi bir proje yönetiyorsanız, eninde sonunda state dosyası meselesiyle yüzleşmek zorunda kalırsınız. Yerel state dosyasıyla başlamak gayet mantıklıdır, ama ekip büyüdükçe, pipeline’lar devreye girdikçe ve altyapı karmaşıklaştıkça bu yaklaşım sizi ciddi sorunların içine çeker. Uzak backend yapılandırması tam da bu noktada devreye giriyor.

Terraform State Dosyası Nedir ve Neden Önemlidir

Terraform, yönettiği altyapının mevcut durumunu terraform.tfstate adlı bir JSON dosyasında saklar. Bu dosya olmadan Terraform hangi kaynakların zaten oluşturulduğunu, hangilerinin değiştirilmesi gerektiğini ya da hangilerinin silinmesi gerektiğini bilemez. Özetle bu dosya, gerçek dünya altyapısı ile Terraform konfigürasyonunuz arasındaki köprüdür.

Varsayılan olarak bu dosya yerel dizininizde oluşturulur. Tek kişilik küçük projelerde sorun çıkarmaz, ama şu senaryoları düşünün:

  • İki ekip üyesi aynı anda terraform apply çalıştırıyor
  • CI/CD pipeline’ı state dosyasına erişemiyor
  • Birisi dizüstü bilgisayarını formatladı ve state dosyası gitti
  • Farklı ortamlar (dev, staging, prod) için state yönetimi kaotik hale geldi

İşte tüm bu sorunların çözümü uzak backend yapılandırmasıdır.

Uzak Backend Seçenekleri

Terraform birden fazla uzak backend destekler. En yaygın kullanılanlar şunlardır:

  • S3 + DynamoDB: AWS kullanan ekipler için standart çözüm
  • Azure Blob Storage: Azure tabanlı altyapılarda tercih edilir
  • Google Cloud Storage: GCP projelerinde kullanılır
  • Terraform Cloud / HCP Terraform: HashiCorp’un yönetilen servisi
  • GitLab Managed State: GitLab CI kullananlar için pratik seçenek
  • HTTP Backend: Özel state sunucuları için

Bu yazıda en yaygın kullanılan S3 + DynamoDB kombinasyonunu detaylı ele alacağız, ardından Azure ve GitLab seçeneklerine de değineceğiz.

S3 + DynamoDB ile Backend Kurulumu

Altyapı Ön Hazırlığı

İlk adım, state dosyasını tutacak S3 bucket ve kilitleme için DynamoDB tablosunu oluşturmaktır. Burada tavuk-yumurta paradoksuyla karşılaşırsınız: Terraform altyapısını Terraform ile mi yöneteceksiniz? Başlangıçta bu kaynakları manuel olarak veya ayrı bir bootstrap scripti ile oluşturmanızı öneririm.

# Bootstrap S3 bucket oluşturma
aws s3api create-bucket 
  --bucket sirket-terraform-state 
  --region eu-central-1 
  --create-bucket-configuration LocationConstraint=eu-central-1

# Versiyonlamayı etkinleştir
aws s3api put-bucket-versioning 
  --bucket sirket-terraform-state 
  --versioning-configuration Status=Enabled

# Sunucu taraflı şifrelemeyi etkinleştir
aws s3api put-bucket-encryption 
  --bucket sirket-terraform-state 
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms"
      }
    }]
  }'

# Public erişimi engelle
aws s3api put-public-access-block 
  --bucket sirket-terraform-state 
  --public-access-block-configuration 
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

# DynamoDB tablosu oluştur (state kilitleme için)
aws dynamodb create-table 
  --table-name terraform-state-lock 
  --attribute-definitions AttributeName=LockID,AttributeType=S 
  --key-schema AttributeName=LockID,KeyType=HASH 
  --billing-mode PAY_PER_REQUEST 
  --region eu-central-1

Backend Konfigürasyonu

Şimdi Terraform projenizde backend yapılandırmasını tanımlayın. Genellikle backend.tf adında ayrı bir dosya oluşturmak iyi bir pratiktir:

# backend.tf
terraform {
  backend "s3" {
    bucket         = "sirket-terraform-state"
    key            = "production/vpc/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
    
    # KMS ile şifreleme (opsiyonel ama önerilir)
    kms_key_id = "arn:aws:kms:eu-central-1:123456789:key/mrk-abc123"
  }
  
  required_version = ">= 1.5.0"
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

State key yapısına dikkat edin: production/vpc/terraform.tfstate formatı çok ortamlı yönetim için kritiktir. Aynı bucket içinde farklı prefix’lerle onlarca farklı state dosyası tutabilirsiniz.

Backend Başlatma

# Mevcut projede backend'i başlat
terraform init

# Yerel state'i uzak backend'e taşı
terraform init -migrate-state

# Backend konfigürasyonunu zorla yeniden başlat
terraform init -reconfigure

-migrate-state parametresi, mevcut terraform.tfstate dosyanızı S3’e taşır. Bu işlemi yaparken Terraform sizden onay ister, dikkatli olun.

Ortam Bazlı State Yönetimi

Gerçek dünya projelerinde genellikle birden fazla ortam (dev, staging, production) yönetirsiniz. Bunun için iki yaygın yaklaşım vardır.

Yaklaşım 1: Dizin Yapısı ile Ayrım

# Proje yapısı
infrastructure/
├── modules/
│   ├── vpc/
│   ├── eks/
│   └── rds/
├── environments/
│   ├── dev/
│   │   ├── backend.tf
│   │   ├── main.tf
│   │   └── variables.tf
│   ├── staging/
│   │   ├── backend.tf
│   │   ├── main.tf
│   │   └── variables.tf
│   └── production/
│       ├── backend.tf
│       ├── main.tf
│       └── variables.tf

Her ortamın kendi backend.tf dosyası vardır:

# environments/dev/backend.tf
terraform {
  backend "s3" {
    bucket         = "sirket-terraform-state"
    key            = "dev/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

# environments/production/backend.tf
terraform {
  backend "s3" {
    bucket         = "sirket-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

Yaklaşım 2: Workspace ile Ayrım

Terraform Workspace’ler farklı state dosyaları kullanmanızı sağlar:

# Workspace oluştur ve geç
terraform workspace new dev
terraform workspace new staging
terraform workspace new production

# Mevcut workspace'i görüntüle
terraform workspace show

# Workspace listesi
terraform workspace list

# Workspace'e geç
terraform workspace select production

Workspace kullandığınızda S3’teki key yapısı otomatik olarak env:/production/terraform.tfstate formatına dönüşür. Konfigürasyonda bunu kullanabilirsiniz:

# Workspace destekli backend konfigürasyonu
terraform {
  backend "s3" {
    bucket         = "sirket-terraform-state"
    key            = "global/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

# main.tf içinde workspace bazlı değişken kullanımı
locals {
  environment = terraform.workspace
  
  instance_types = {
    dev        = "t3.micro"
    staging    = "t3.medium"
    production = "m5.large"
  }
  
  instance_type = local.instance_types[local.environment]
}

Önemli not: Workspace yaklaşımı basit görünse de büyük projelerde karmaşıklığa yol açabilir. Dizin bazlı ayrım genellikle daha yönetilebilirdir.

Azure Blob Storage Backend

AWS dışında Azure kullananlar için:

# Azure backend konfigürasyonu
terraform {
  backend "azurerm" {
    resource_group_name  = "terraform-state-rg"
    storage_account_name = "sirketterraformstate"
    container_name       = "tfstate"
    key                  = "production.terraform.tfstate"
    
    # Managed Identity kullanımı (önerilir)
    use_azuread_auth = true
  }
}
# Azure kaynakları oluşturma
az group create --name terraform-state-rg --location westeurope

az storage account create 
  --name sirketterraformstate 
  --resource-group terraform-state-rg 
  --location westeurope 
  --sku Standard_LRS 
  --encryption-services blob 
  --min-tls-version TLS1_2

az storage container create 
  --name tfstate 
  --account-name sirketterraformstate

# Versiyonlamayı etkinleştir
az storage blob service-properties update 
  --account-name sirketterraformstate 
  --enable-versioning true

CI/CD Pipeline Entegrasyonu

Uzak backend’in en büyük avantajlarından biri CI/CD sistemleriyle sorunsuz çalışmasıdır. GitHub Actions örneği:

# .github/workflows/terraform.yml
name: Terraform Apply

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  AWS_REGION: eu-central-1
  TF_VERSION: 1.6.0

jobs:
  terraform:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      pull-requests: write
    
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      
      - name: AWS OIDC Auth
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/github-terraform-role
          aws-region: ${{ env.AWS_REGION }}
      
      - name: Terraform Setup
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}
      
      - name: Terraform Init
        run: terraform init
        working-directory: ./environments/production
      
      - name: Terraform Plan
        id: plan
        run: terraform plan -out=tfplan -no-color
        working-directory: ./environments/production
      
      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve tfplan
        working-directory: ./environments/production

Pipeline içinde kimlik bilgilerini ortam değişkeni olarak geçirmek yerine OIDC kullanmak çok daha güvenlidir. Yukarıdaki örnekte GitHub Actions’ın OIDC token’ı ile AWS’de doğrudan oturum açılıyor.

State Kilitleme Mekanizması

DynamoDB tablosu, state dosyasına eşzamanlı erişimi engeller. Birisi terraform apply çalıştırdığında DynamoDB’ye bir kayıt yazılır ve diğerleri bu kilit kalktığına kadar bekler. Yanlış giden bir uygulama sonrası kilid takılı kalabilir, bu durumda:

# Mevcut kilidi görüntüle
aws dynamodb scan 
  --table-name terraform-state-lock 
  --region eu-central-1

# Kilidi zorla kaldır (dikkatli kullanın!)
terraform force-unlock LOCK_ID

# Örnek
terraform force-unlock "abc123-def456-..."

force-unlock komutunu kullanmadan önce gerçekten hiçbir Terraform işleminin çalışmadığından emin olun. Çalışan bir işlemi kesintiye uğratmak state bozulmasına yol açabilir.

State Dosyası Güvenliği

State dosyası son derece hassas veriler içerebilir. Şifreler, API anahtarları, sertifikalar… Bunları korumak için alınması gereken önlemler:

# S3 bucket policy - sadece yetkili roller erişebilir
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyPublicAccess",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::sirket-terraform-state",
        "arn:aws:s3:::sirket-terraform-state/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    },
    {
      "Sid": "AllowTerraformRole",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::123456789:role/terraform-ci-role",
          "arn:aws:iam::123456789:role/terraform-admin-role"
        ]
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::sirket-terraform-state",
        "arn:aws:s3:::sirket-terraform-state/*"
      ]
    }
  ]
}

Partial Backend Konfigürasyonu

Hassas değerleri kod deposuna koymamak için partial backend konfigürasyonu kullanabilirsiniz:

# backend.tf - sadece sabit değerler
terraform {
  backend "s3" {
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}
# init sırasında geri kalanı geç
terraform init 
  -backend-config="bucket=sirket-terraform-state" 
  -backend-config="key=production/terraform.tfstate" 
  -backend-config="kms_key_id=arn:aws:kms:eu-central-1:123456789:key/mrk-abc123"

# Ya da ayrı bir dosyadan
terraform init -backend-config=backend-prod.hcl
# backend-prod.hcl (git'e eklemeyin veya şifreli saklayın)
bucket     = "sirket-terraform-state"
key        = "production/terraform.tfstate"
kms_key_id = "arn:aws:kms:eu-central-1:123456789:key/mrk-abc123"

Bu yaklaşım özellikle farklı ekiplerin farklı bucket’lara yazması gereken büyük organizasyonlarda çok işe yarar.

State Dosyası Kurtarma ve Yedekleme

S3 versiyonlama aktifken eski state sürümlerine dönmek mümkündür:

# Bucket'taki tüm state versiyonlarını listele
aws s3api list-object-versions 
  --bucket sirket-terraform-state 
  --prefix "production/terraform.tfstate" 
  --query 'Versions[*].{VersionId:VersionId,LastModified:LastModified}' 
  --output table

# Belirli bir versiyona dön
aws s3api get-object 
  --bucket sirket-terraform-state 
  --key "production/terraform.tfstate" 
  --version-id "abc123XYZ" 
  terraform.tfstate.backup

# State'i manuel olarak geri yükle (çok dikkatli olun!)
terraform state push terraform.tfstate.backup

State bozulması ciddi bir felaket senaryosudur. Düzenli aralıklarla state dosyasını yedeklemek ve bu yedeklerden geri yüklemeyi test etmek iyi bir alışkanlıktır.

Remote State Datasource Kullanımı

Farklı Terraform projeleri arasında state paylaşımı için terraform_remote_state datasource’unu kullanabilirsiniz. Örneğin VPC projenizin output’larını EKS projenizde okumak isteyebilirsiniz:

# VPC projesinin outputs.tf
output "vpc_id" {
  value = aws_vpc.main.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}
# EKS projesinde VPC state'ini oku
data "terraform_remote_state" "vpc" {
  backend = "s3"
  
  config = {
    bucket = "sirket-terraform-state"
    key    = "production/vpc/terraform.tfstate"
    region = "eu-central-1"
  }
}

# Değerleri kullan
resource "aws_eks_cluster" "main" {
  name = "production-cluster"
  
  vpc_config {
    subnet_ids = data.terraform_remote_state.vpc.outputs.private_subnet_ids
  }
}

Bu yöntem projeler arası bağımlılıkları net bir şekilde ortaya koyar ve sıkı coupling’den kaçınmanızı sağlar.

Sık Karşılaşılan Sorunlar ve Çözümleri

State lock kalmış durumda:

# Çalışan terraform process'i kontrol et
ps aux | grep terraform

# Lock'u görüntüle
terraform show

# Güvenli olduğundan emin olunca unlock
terraform force-unlock <lock-id>

Backend erişim hatası:

# AWS kimlik bilgilerini doğrula
aws sts get-caller-identity

# S3 erişimini test et
aws s3 ls s3://sirket-terraform-state/

# DynamoDB erişimini test et
aws dynamodb list-tables --region eu-central-1

State drift tespiti:

# Altyapı durumunu state ile karşılaştır
terraform plan -detailed-exitcode

# Sadece belirli bir kaynağı yenile
terraform apply -refresh-only -target=aws_instance.web

GitLab Managed State

GitLab kullanıyorsanız harici bir backend kurmadan GitLab’ın kendi state yönetimini kullanabilirsiniz:

# GitLab backend konfigürasyonu
terraform {
  backend "http" {
    address        = "https://gitlab.com/api/v4/projects/12345/terraform/state/production"
    lock_address   = "https://gitlab.com/api/v4/projects/12345/terraform/state/production/lock"
    unlock_address = "https://gitlab.com/api/v4/projects/12345/terraform/state/production/lock"
    username       = "gitlab-ci-token"
    password       = "glpat-xxxxxxxxxxxx"
    lock_method    = "POST"
    unlock_method  = "DELETE"
    retry_wait_min = 5
  }
}

GitLab CI/CD pipeline’ında bu değerleri otomatik olarak ortam değişkenlerinden alabilirsiniz. GitLab bu konuda oldukça iyi bir entegrasyon sunuyor.

Sonuç

Terraform uzak backend yapılandırması, production kalitesinde altyapı yönetiminin vazgeçilmez bir parçasıdır. Yerel state ile başlamak anlaşılabilir, ama bunu bir borç olarak görün ve mümkün olan en kısa sürede uzak backend’e geçin.

Özetle yapmanız gerekenler şunlar: S3 bucket’ınızda versiyonlamayı ve şifrelemeyi mutlaka açın. DynamoDB ile state kilitlemeyi atlamamanın. Farklı ortamlar için farklı state key’leri kullanın. Hassas backend konfigürasyonlarını kod deposuna koymayın, partial config yaklaşımını benimseyin. CI/CD sistemlerinde long-lived credential kullanmak yerine OIDC veya managed identity tercih edin.

State dosyası küçük bir JSON dosyası gibi görünse de onun etrafındaki tüm yönetim süreci, bir altyapı ekibinin olgunluğunu ölçen en iyi göstergelerden biridir. Bu yazıdaki pratikleri uygularsanız hem gece 2’deki “state bozuldu” paniklerinden kurtulursunuz, hem de takım arkadaşlarınızla çok daha verimli çalışırsınız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir