Terraform Workspace ile Çoklu Ortam Yönetimi
Çok ortamlı altyapı yönetimi, her sysadmin’in er ya da geç karşılaştığı bir kabus haline gelir. Development, staging, production ortamları için ayrı ayrı Terraform kodları mı yazacaksın? Her birini ayrı dizinlerde mi tutacaksın? Yoksa tek bir kod tabanından tüm ortamları mı yöneteceksin? İşte tam bu noktada Terraform Workspace’ler devreye giriyor ve hayatını ciddi ölçüde kolaylaştırıyor.
Terraform Workspace Nedir?
Terraform’da workspace, aynı konfigürasyon dosyalarını kullanarak birden fazla bağımsız state dosyası yönetmenizi sağlayan bir mekanizmadır. Basitçe söylemek gerekirse, tek bir .tf dosyası seti yazıyorsun ama bu kodu dev, staging, prod gibi farklı ortamlar için ayrı ayrı çalıştırabiliyorsun. Her workspace’in kendi state dosyası var, birbirlerinden tamamen izole çalışıyorlar.
Terraform’u ilk kurduğunda otomatik olarak default adında bir workspace ile başlarsın. Bu workspace’i fark etmeden sürekli kullanıyorsundur. Ama işler büyüdükçe, “dev’de deneyeyim, sonra prod’a alayım” diye düşündüğünde workspace’lerin gerçek gücünü anlıyorsun.
State Dosyalarının İzolasyonu
Her workspace için Terraform ayrı bir state dosyası tutar. Local backend kullanıyorsan terraform.tfstate.d/ dizini altında her workspace için ayrı bir klasör oluşturulur:
terraform.tfstate.d/
├── dev/
│ └── terraform.tfstate
├── staging/
│ └── terraform.tfstate
└── prod/
└── terraform.tfstate
Remote backend kullandığında (S3, Terraform Cloud vb.) her workspace için ayrı bir state path oluşturulur. Bu izolasyon sayesinde dev ortamında yaptığın değişiklik, prod’un state’ini hiçbir şekilde etkilemez.
Temel Workspace Komutları
Workspace yönetimi için kullanacağın komutlar oldukça basit. Hepsini ezberlemenize gerek yok ama sık kullandıklarınızı mutlaka bilmelisiniz:
# Mevcut workspace listesini gör
terraform workspace list
# Yeni workspace oluştur
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Workspace'ler arası geçiş yap
terraform workspace select dev
# Aktif workspace'i göster
terraform workspace show
# Workspace sil (önce kaynakları destroy etmen gerekebilir)
terraform workspace delete staging
terraform workspace list komutunu çalıştırdığında aktif workspace’in başında * işareti görürsün:
$ terraform workspace list
default
* dev
staging
prod
Bu kadar basit. Komutların açıklamaları:
- list: Tüm workspace’leri listeler, aktif olanı
*ile gösterir - new: Yeni bir workspace oluşturur ve otomatik olarak o workspace’e geçer
- select: Belirtilen workspace’e geçiş yapar
- show: Sadece aktif workspace adını basar
- delete: Workspace’i siler, içinde kaynak varsa önce
destroyetmen gerekir
Gerçek Dünya Senaryosu: AWS’de Multi-Ortam Altyapısı
Diyelim ki bir e-ticaret şirketinde çalışıyorsun ve AWS üzerinde üç ortam yönetmen gerekiyor: dev, staging, prod. Her ortamın farklı instance boyutları, farklı replica sayıları ve farklı domain isimleri olacak. Bunu workspace ile nasıl yaparsın?
Önce proje yapısını oluşturalım:
mkdir -p terraform-multienv/{modules/ec2,environments}
cd terraform-multienv
touch main.tf variables.tf outputs.tf terraform.tfvars
Variables ve Locals ile Ortam Bazlı Konfigürasyon
Workspace’lerin gerçek gücü, terraform.workspace değişkenini kullanarak dinamik konfigürasyon yapabilmenden geliyor. İşte bu yüzden workspace’leri seviyorum:
# variables.tf
variable "region" {
description = "AWS region"
type = string
default = "eu-west-1"
}
variable "project_name" {
description = "Proje adi"
type = string
default = "ecommerce"
}
# main.tf - Ortam bazli konfigürasyonlar
locals {
env = terraform.workspace
# Her ortam icin farkli instance boyutlari
instance_sizes = {
dev = "t3.micro"
staging = "t3.medium"
prod = "t3.large"
}
# Her ortam icin farkli instance adetleri
instance_counts = {
dev = 1
staging = 2
prod = 4
}
# RDS instance boyutlari
db_instance_classes = {
dev = "db.t3.micro"
staging = "db.t3.medium"
prod = "db.r5.large"
}
# Ortama göre tag'ler
common_tags = {
Environment = local.env
Project = var.project_name
ManagedBy = "Terraform"
Workspace = terraform.workspace
}
# Seçili degerleri locals'a ata
current_instance_size = local.instance_sizes[local.env]
current_instance_count = local.instance_counts[local.env]
current_db_class = local.db_instance_classes[local.env]
}
Bu yapıyla dev workspace’indeyken t3.micro instance ayağa kalkacak, prod‘a geçince t3.large kullanılacak. Aynı kod, farklı sonuçlar.
EC2 ve RDS Kaynakları Tanımlama
# ec2.tf
resource "aws_instance" "web" {
count = local.current_instance_count
ami = data.aws_ami.amazon_linux.id
instance_type = local.current_instance_size
tags = merge(local.common_tags, {
Name = "${var.project_name}-web-${local.env}-${count.index + 1}"
Role = "WebServer"
})
lifecycle {
# Prod ortaminda kazara silme engeli
prevent_destroy = local.env == "prod" ? true : false
}
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
}
# rds.tf
resource "aws_db_instance" "main" {
identifier = "${var.project_name}-db-${local.env}"
engine = "postgres"
engine_version = "14.9"
instance_class = local.current_db_class
allocated_storage = local.env == "prod" ? 100 : 20
db_name = "${var.project_name}_${local.env}"
username = "dbadmin"
password = var.db_password
# Sadece prod'da multi-az
multi_az = local.env == "prod" ? true : false
# Sadece prod'da silme koruması
deletion_protection = local.env == "prod" ? true : false
# Dev'de backup tutma, prod'da 7 gun
backup_retention_period = local.env == "prod" ? 7 : 0
tags = merge(local.common_tags, {
Name = "${var.project_name}-db-${local.env}"
})
}
S3 Backend ile Remote State Yönetimi
Production ortamında local state kullanmak intihar sayılır. S3 backend ile remote state kuruyoruz:
# backend.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "sirket-terraform-state"
key = "ecommerce/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
S3 backend kullanırken workspace’ler otomatik olarak şu yolları kullanır:
defaultworkspace:ecommerce/terraform.tfstatedevworkspace:env:/dev/ecommerce/terraform.tfstateprodworkspace:env:/prod/ecommerce/terraform.tfstate
Bu yapıyı oluşturduktan sonra workspace başlatma süreci:
# Backend'i initialize et
terraform init
# Workspace'leri oluştur
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod
# Dev ortamina gecis yap ve plan gör
terraform workspace select dev
terraform plan
# Onaylarsan uygula
terraform apply
CI/CD Pipeline’a Entegrasyon
GitLab CI veya GitHub Actions ile workspace’leri otomatize etmek, DevOps süreçlerinin tam merkezine workspace’leri oturtur. İşte basit bir GitLab CI örneği:
# .gitlab-ci.yml
variables:
TF_VERSION: "1.6.0"
AWS_DEFAULT_REGION: "eu-west-1"
stages:
- validate
- plan
- apply
.terraform_base:
image: hashicorp/terraform:${TF_VERSION}
before_script:
- terraform init -input=false
- terraform workspace select ${TF_WORKSPACE} || terraform workspace new ${TF_WORKSPACE}
terraform:validate:
extends: .terraform_base
stage: validate
variables:
TF_WORKSPACE: "dev"
script:
- terraform validate
- terraform fmt -check
only:
- merge_requests
terraform:plan:dev:
extends: .terraform_base
stage: plan
variables:
TF_WORKSPACE: "dev"
script:
- terraform plan -out=tfplan-dev -input=false
artifacts:
paths:
- tfplan-dev
expire_in: 1 hour
only:
- develop
terraform:apply:dev:
extends: .terraform_base
stage: apply
variables:
TF_WORKSPACE: "dev"
script:
- terraform apply -auto-approve -input=false tfplan-dev
dependencies:
- terraform:plan:dev
only:
- develop
when: manual
terraform:plan:prod:
extends: .terraform_base
stage: plan
variables:
TF_WORKSPACE: "prod"
script:
- terraform plan -out=tfplan-prod -input=false
artifacts:
paths:
- tfplan-prod
expire_in: 1 hour
only:
- main
terraform:apply:prod:
extends: .terraform_base
stage: apply
variables:
TF_WORKSPACE: "prod"
script:
- terraform apply -auto-approve -input=false tfplan-prod
dependencies:
- terraform:plan:prod
only:
- main
when: manual
Bu pipeline şöyle çalışır:
developbranch’ine push geldiğinde dev ortamı için plan çıkar, manuel onay sonrası apply edermainbranch’ine push geldiğinde prod için plan çıkar, manuel onay bekler- Merge request’lerde sadece validate ve format check yapar
Workspace ile Ortak Tuzaklar ve Çözümleri
Yanlış Workspace’de Apply Yapmak
En sık yapılan hata bu. Dev’de test edeceksin, prod workspace’indesin farkında değilsin ve apply basıyorsun. Bunu önlemek için prompt’una workspace bilgisini ekleyebilirsin:
# ~/.bashrc veya ~/.zshrc dosyasına ekle
tf_workspace_info() {
if [ -d .terraform ]; then
workspace=$(terraform workspace show 2>/dev/null)
if [ -n "$workspace" ]; then
echo "[TF: $workspace]"
fi
fi
}
# PS1'e ekle (bash)
PS1='u@h:w $(tf_workspace_info)$ '
Artık terminal prompt’unda hangi workspace’de olduğunu göreceksin.
Bir diğer güvenlik önlemi olarak, prod workspace’inde çalışırken ekstra onay isteyen bir wrapper script yazabilirsin:
#!/bin/bash
# /usr/local/bin/tf-safe adinda kaydet ve chmod +x yap
CURRENT_WORKSPACE=$(terraform workspace show)
if [ "$CURRENT_WORKSPACE" = "prod" ]; then
echo "DIKKAT: Su anda PROD workspace'indesiniz!"
echo "Devam etmek istediginizi yazmak icin 'prod-onayla' girin:"
read confirmation
if [ "$confirmation" != "prod-onayla" ]; then
echo "Iptal edildi."
exit 1
fi
fi
terraform "$@"
Workspace’e Göre Provider Konfigürasyonu
Farklı ortamlar için farklı AWS hesapları veya region kullanmak isteyebilirsin. Bu durumda alias kullanman gerekir:
# providers.tf
locals {
account_ids = {
dev = "111111111111"
staging = "222222222222"
prod = "333333333333"
}
assume_role_arns = {
dev = "arn:aws:iam::111111111111:role/TerraformRole"
staging = "arn:aws:iam::222222222222:role/TerraformRole"
prod = "arn:aws:iam::333333333333:role/TerraformRole"
}
}
provider "aws" {
region = var.region
assume_role {
role_arn = local.assume_role_arns[terraform.workspace]
}
default_tags {
tags = local.common_tags
}
}
Bu yaklaşımla her workspace farklı bir AWS hesabına deploy eder. Çok hesaplı ortam yönetimi için ideal.
Workspace Limitleri ve Alternatifler
Workspace her derde deva değil. Bazı durumlarda workspace yerine farklı bir yaklaşım tercih edilebilir.
Workspace kullanmayı düşün:
- Ortamlar yapısal olarak aynı ama konfigürasyon değerleri farklıysa
- Küçük-orta ölçekli projelerde
- Hızlı prototipler için
- CI/CD pipeline’larında branch bazlı deployment için
Workspace yerine ayrı dizin veya repo düşün:
- Ortamlar yapısal olarak ciddi ölçüde farklıysa (prod’da var olmayan servisler dev’de var gibi)
- Farklı ekiplerin farklı ortamlara erişmesi gerekiyorsa
- Compliance gereklilikleri nedeniyle tam izolasyon gerekiyorsa
- Çok büyük ve karmaşık altyapılarda
Terragrunt kullanıyorsan workspace yerine Terragrunt’ın kendi ortam yönetim yapısını tercih edebilirsin. Ama bu ayrı bir yazı konusu.
Outputs ve Workspace Farkındalığı
Output’larınızı da workspace’e göre düzenlemenizi öneririm. Özellikle başka Terraform modülleri veya scriptler bu output’ları kullanıyorsa ortamı bilmek önemlidir:
# outputs.tf
output "environment_info" {
description = "Ortam bilgileri"
value = {
workspace = terraform.workspace
instance_count = local.current_instance_count
instance_type = local.current_instance_size
db_class = local.current_db_class
}
}
output "web_instance_ips" {
description = "Web sunucu IP'leri"
value = aws_instance.web[*].private_ip
}
output "db_endpoint" {
description = "Veritabani endpoint'i"
value = aws_db_instance.main.endpoint
sensitive = true
}
output "deployment_summary" {
description = "Deployment ozeti"
value = <<-EOT
Ortam: ${terraform.workspace}
Instance Sayisi: ${local.current_instance_count}
Instance Tipi: ${local.current_instance_size}
DB Sinifi: ${local.current_db_class}
Multi-AZ: ${local.env == "prod" ? "Evet" : "Hayir"}
EOT
}
Tüm workspace’lerin durumunu tek bir scriptle kontrol etmek istiyorsan:
#!/bin/bash
# check-all-workspaces.sh
WORKSPACES=("dev" "staging" "prod")
ORIGINAL_WS=$(terraform workspace show)
echo "=== Tum Workspace Durumu ==="
for ws in "${WORKSPACES[@]}"; do
echo ""
echo "--- $ws ---"
terraform workspace select "$ws" > /dev/null 2>&1
terraform output environment_info 2>/dev/null || echo "Output alinamadi"
done
# Orijinal workspace'e don
terraform workspace select "$ORIGINAL_WS" > /dev/null 2>&1
echo ""
echo "=== $ORIGINAL_WS workspace'ine donuldu ==="
State Yönetimi ve Bakım
Workspace’ler büyüdükçe state yönetimi kritik hale gelir. Düzenli bakım alışkanlıkları edinmek şart:
# Belirli bir workspace'in state'ini listele
terraform workspace select dev
terraform state list
# State'i yedekle (prod için önemli)
terraform workspace select prod
terraform state pull > prod-state-backup-$(date +%Y%m%d).json
# Drift detection - altyapı gerçekten plana uygun mu?
terraform workspace select prod
terraform plan -detailed-exitcode
# Exit code 0: Degisiklik yok
# Exit code 1: Hata
# Exit code 2: Degisiklik var (drift var demek)
# Kaynak import - manuel yaratilan kaynagi state'e al
terraform workspace select dev
terraform import aws_instance.web[0] i-0123456789abcdef0
State lock sorunlarıyla karşılaşırsan:
# DynamoDB'deki lock'u zorla kaldir (dikkatli kullan!)
terraform force-unlock LOCK-ID
# Lock ID'yi hata mesajindan alabilirsin
# Ornek: Error acquiring the state lock: Lock Info:
# ID: 12345678-1234-1234-1234-123456789012
Sonuç
Terraform workspace’ler, altyapı kodunu DRY (Don’t Repeat Yourself) prensibine uygun tutmanın en pratik yollarından biri. Tek bir kod tabanıyla dev, staging, prod ortamlarını yönetebilmek hem bakım maliyetini düşürür hem de ortamlar arası tutarsızlıkları ortadan kaldırır. “Dev’de çalışıyor ama prod’da çalışmıyor” problemlerinin önemli bir kısmı farklı ortamlarda farklı kodların çalışmasından kaynaklanır. Workspace bunu köklü şekilde çözer.
Dikkat edilmesi gereken en kritik nokta, hangi workspace’de olduğunu her zaman bilmek. Bu yazıda gösterdiğim prompt değişikliği ve tf-safe wrapper script gibi güvenlik önlemlerini ciddiye alın. Prod’da yanlış apply kabusu herkesin başına bir kez gelir, ikinci kez gelmemesi için önlem almak lazım.
CI/CD pipeline entegrasyonuyla birlikte workspace’ler gerçek gücünü gösterir. Branch bazlı deployment, otomatik ortam seçimi ve manuel onay adımlarıyla hem hız hem de güvenlik sağlayabilirsiniz. Altyapınız büyüdükçe workspace’lerin sınırlarına da dikkat edin ve gerektiğinde Terragrunt veya ayrı dizin/repo yaklaşımına geçmekten çekinmeyin. Doğru araç, doğru problem için.
