Terraform Kurulumu ve İlk Provider Yapılandırması
Altyapıyı kod olarak yönetmek, modern DevOps dünyasının olmazsa olmaz pratiği haline geldi. Sunucu kurup elle yapılandırma dönemi geride kalıyor; artık her şey versiyon kontrollü, tekrarlanabilir ve otomatize edilmiş olmalı. Terraform, HashiCorp tarafından geliştirilen ve “Infrastructure as Code” (IaC) felsefesinin en popüler temsilcilerinden biri. AWS, Azure, GCP, hatta kendi veri merkeziniz olsun, Terraform ile hepsini tek bir araçla yönetebiliyorsunuz. Bu yazıda sıfırdan başlayarak Terraform kurulumunu yapacak, provider kavramını derinlemesine inceleyecek ve gerçek dünya senaryolarıyla ilk yapılandırmalarınızı oluşturacaksınız.
Terraform Nedir ve Neden Kullanmalısınız
Terraform, HCL (HashiCorp Configuration Language) adı verilen kendine özgü bir dil kullanarak altyapı kaynaklarını tanımlamanıza olanak sağlar. Klasik shell scriptlerden farkı şu: Terraform mevcut durumu takip eder. “Şu anda ne var, ne olması gerekiyor?” sorusunu sürekli kendine sorarak değişiklikleri hesaplar ve sadece gerekli olanı uygular.
Bir sistemi elle kurduğunuzda ne olur? Üç ay sonra kim neyi değiştirdi, neden değiştirdi, hangi port neden açık bilmiyorsunuz. Terraform kullandığınızda her şey Git reponuzda, her değişiklik commit geçmişinde. Yeni bir ekip üyesi geldiğinde “şu klasörü aç, terraform apply çalıştır” diyebiliyorsunuz.
Terraform’un öne çıkan avantajları:
- Çoklu provider desteği: 1000’den fazla provider mevcut, AWS’den Cloudflare’e, Kubernetes’ten GitHub’a kadar
- State yönetimi: Neyin var olduğunu takip eder, tutarsızlıkları saptar
- Plan-Apply döngüsü: Değişiklik yapmadan önce ne yapılacağını gösterir
- Modüler yapı: Tekrar kullanılabilir modüller yazabilirsiniz
- Büyük topluluk: Hazır modüller ve çözümler bolca mevcut
Kurulum
Linux’ta Kurulum
Debian/Ubuntu tabanlı sistemlerde HashiCorp’un resmi reposunu ekleyerek kurulum yapmanız en sağlıklı yol. Böylece güncellemeleri apt upgrade ile otomatik alırsınız.
# Gerekli araçları yükle
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
# HashiCorp GPG anahtarını ekle
wget -O- https://apt.releases.hashicorp.com/gpg |
gpg --dearmor |
sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg > /dev/null
# Repoyu ekle
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg]
https://apt.releases.hashicorp.com $(lsb_release -cs) main" |
sudo tee /etc/apt/sources.list.d/hashicorp.list
# Terraform'u kur
sudo apt-get update && sudo apt-get install terraform
RHEL/CentOS/Rocky Linux için:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum install terraform
Kurulumun başarılı olup olmadığını doğrulayın:
terraform version
# Çıktı: Terraform v1.7.x
# on linux_amd64
macOS’ta Kurulum
macOS kullanıcıları için Homebrew en temiz yöntem:
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# Güncellemek için
brew upgrade hashicorp/tap/terraform
Windows’ta Kurulum
Windows ortamında Chocolatey veya doğrudan binary indirme yöntemini kullanabilirsiniz. Chocolatey ile:
choco install terraform
Ya da HashiCorp’un sitesinden zip dosyasını indirip PATH’e ekleyebilirsiniz. WSL2 kullananlar için Linux kurulum adımlarını direkt uygulayabilirsiniz, ki bu daha pratik bir seçenek.
Shell Tamamlama Ayarları
Günlük kullanımda hayat kurtarır:
# Bash için
terraform -install-autocomplete
# Zsh kullananlar için ~/.zshrc'ye ekleyin
autoload -U +X bashcompinit && bashcompinit
complete -o nospace -C /usr/bin/terraform terraform
Temel Kavramlar: Provider Nedir
Terraform tek başına hiçbir şey yapamaz. AWS’de bir EC2 instance açmak istiyorsanız, Terraform’un AWS API’siyle nasıl konuşacağını bilmesi gerekir. İşte bu noktada provider devreye girer. Provider, belirli bir platformun kaynaklarını yönetmek için Terraform’a gerekli API entegrasyonunu sağlayan plugin’dir.
Provider’lar üç kategoride toplanır:
- Official: HashiCorp tarafından yönetilen AWS, Azure, GCP, Kubernetes gibi büyük platformlar
- Partner: HashiCorp ile ortak çalışan şirketlerin oluşturduğu, Datadog, MongoDB Atlas, Cloudflare gibi
- Community: Topluluk tarafından geliştirilen ve Terraform Registry’de yayınlanan
terraform init komutunu çalıştırdığınızda Terraform, .terraform.lock.hcl dosyasını oluşturur ve tanımladığınız provider’ları indirir. Bu dosyayı Git’e commit edin, böylece ekip üyeleri aynı provider versiyonlarını kullanır.
İlk Yapılandırma Dosyalarını Oluşturmak
Terraform projeleri birkaç temel dosya üzerine kurulur. Dosya isimlerinin .tf uzantısı olması yeterli, Terraform aynı dizindeki tüm .tf dosyalarını birleştirerek işler. Ancak genel kabul gören yapı şöyle:
- main.tf: Ana kaynak tanımları
- providers.tf: Provider yapılandırmaları
- variables.tf: Değişken tanımları
- outputs.tf: Çıktı tanımları
- terraform.tfvars: Değişken değerleri (Git’e push etmeyin, hassas veriler içerebilir)
Bir proje dizini oluşturarak başlayalım:
mkdir terraform-ilk-proje
cd terraform-ilk-proje
touch main.tf providers.tf variables.tf outputs.tf
AWS Provider Yapılandırması
En yaygın kullanım senaryolarından biri AWS. providers.tf dosyasını oluşturalım:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
}
}
}
Bu yapılandırmada dikkat edilmesi gereken birkaç nokta var. required_version ile Terraform’un minimum versiyonunu zorunlu kılıyoruz. ~> 5.0 ifadesi “5.x versiyonları kabul et ama 6.0’a geçme” anlamına geliyor. default_tags bloğu ise bu provider üzerinden oluşturulan tüm kaynaklara otomatik tag ekler, her kaynağa ayrı ayrı tag yazmak zorunda kalmıyorsunuz.
variables.tf dosyasını düzenleyelim:
variable "aws_region" {
description = "AWS region where resources will be created"
type = string
default = "eu-west-1"
}
variable "environment" {
description = "Deployment environment (dev, staging, prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
}
variable "project_name" {
description = "Project identifier for tagging"
type = string
}
validation bloğu önemli bir detay. Birisi yanlış environment değeri girdiğinde Terraform plan aşamasında hata verir, apply’a geç kalmadan sorunu yakalar.
Kimlik Doğrulama Yöntemleri
Provider yapılandırmasında credential nasıl yönetilir? Bu konu çok kritik. Asla access key ve secret key’i .tf dosyasına yazmayın. Üç temiz yöntem var:
Environment Variables ile Kimlik Doğrulama
En basit ve taşınabilir yöntem:
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="eu-west-1"
# Terraform otomatik olarak bu değişkenleri okur
terraform plan
AWS Profile Kullanımı
Birden fazla AWS hesabıyla çalışıyorsanız named profile kullanın:
# ~/.aws/credentials dosyasında
[proje-dev]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[proje-prod]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
Provider’da profili belirtin:
provider "aws" {
region = var.aws_region
profile = "proje-dev"
}
IAM Role ile Kimlik Doğrulama (Üretim Ortamı İçin)
EC2 üzerinde çalışıyorsanız veya CI/CD pipeline’ınız varsa IAM role kullanımı en güvenli yol:
provider "aws" {
region = var.aws_region
assume_role {
role_arn = "arn:aws:iam::123456789012:role/TerraformDeployRole"
session_name = "terraform-session"
}
}
Birden Fazla Provider Kullanmak
Gerçek dünya projelerinde genellikle birden fazla provider gerekir. Örneğin AWS altyapısı kurarken Cloudflare DNS kaydı da oluşturmak isteyebilirsiniz.
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5"
}
}
}
provider "aws" {
region = "eu-west-1"
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
random provider’ı harici bir servise bağlanmaz, sadece rastgele değerler üretir. Kaynak isimlerine unique suffix eklemek için çok kullanışlı.
Provider Alias ile Çoklu Region
Aynı provider’ı farklı konfigürasyonlarla kullanmanız gerekebilir. Multi-region deployment en yaygın senaryo:
# Primary region
provider "aws" {
region = "eu-west-1"
}
# DR region için alias
provider "aws" {
alias = "us-east"
region = "us-east-1"
}
# Kaynakta kullanımı
resource "aws_s3_bucket" "backup" {
provider = aws.us-east
bucket = "proje-yedek-bucket-us"
}
Bu pattern disaster recovery yapılarında, CloudFront dağıtımlarında (us-east-1 zorunlu) ve multi-region aktif-aktif mimarilerde sıkça kullanılır.
İlk terraform init ve Dosya Yapısı
Yapılandırmayı hazırladıktan sonra init çalıştırın:
terraform init
# Çıktı benzeri:
# Initializing the backend...
# Initializing provider plugins...
# - Finding hashicorp/aws versions matching "~> 5.0"...
# - Installing hashicorp/aws v5.31.0...
# - Installed hashicorp/aws v5.31.0 (signed by HashiCorp)
#
# Terraform has been successfully initialized!
Init sonrasında .terraform/ dizini oluşur, içinde provider binary’leri bulunur. Bu dizini .gitignore‘a ekleyin:
cat >> .gitignore << 'EOF'
.terraform/
*.tfstate
*.tfstate.backup
*.tfvars
.terraform.lock.hcl
EOF
Dikkat: .terraform.lock.hcl dosyasını bazı kaynaklar gitignore’a ekliyor ama bu yanlış. Lock dosyasını commit edin, provider versiyonlarının tutarlı kalması için gerekli.
Gerçek Dünya Senaryosu: VPC Altyapısı Kurulumu
Teoriden pratiğe geçelim. Bir web uygulaması için basit ama gerçekçi bir AWS altyapısı kuralım. main.tf dosyasına ekleyelim:
# Mevcut AZ'leri dinamik olarak çek
data "aws_availability_zones" "available" {
state = "available"
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
# Public subnet - her AZ için bir tane
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${count.index + 1}"
Tier = "public"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
# Route table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-public-rt"
}
}
# Route table association
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
outputs.tf ile önemli değerleri dışa aktaralım:
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "Public subnet ID listesi"
value = aws_subnet.public[*].id
}
output "vpc_cidr" {
description = "VPC CIDR blogu"
value = aws_vpc.main.cidr_block
}
Şimdi plan aşamasını çalıştırın:
# Değişkenleri tfvars dosyasına yazın
cat > dev.tfvars << 'EOF'
environment = "dev"
project_name = "webapp"
aws_region = "eu-west-1"
EOF
terraform plan -var-file="dev.tfvars"
Plan çıktısı size tam olarak ne oluşturulacağını, ne değiştirileceğini ve ne silineceğini gösterir. Yeşil + ekleme, sarı ~ değişiklik, kırmızı - silme anlamına gelir. Apply’dan önce bu çıktıyı dikkatle okuma alışkanlığı edinin.
terraform apply -var-file="dev.tfvars"
# "yes" yazarak onaylayın
State Dosyası Nedir, Nereye Koymalısınız
Terraform uyguladığı değişiklikleri terraform.tfstate dosyasında saklar. Bu dosya mevcut altyapınızın haritası. Yerel diskte tutmak tek kişilik projeler için kabul edilebilir ama ekip çalışmasında felaket reçetesi. İki kişi aynı anda apply çalıştırırsa state dosyası bozulur.
Çözüm: Remote backend. AWS S3 + DynamoDB kombinasyonu en yaygın kullanılan:
terraform {
backend "s3" {
bucket = "sirket-terraform-state"
key = "webapp/dev/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
DynamoDB tablosu state locking için kullanılır, aynı anda iki kişinin değişiklik yapmasını engeller. Backend’i değiştirdiğinizde terraform init -migrate-state komutunu kullanarak mevcut state’i taşıyabilirsiniz.
Sık Yapılan Hatalar ve Çözümleri
Terraform öğrenirken neredeyse herkesin takıldığı noktalara değinelim:
- Provider versiyonu sabitlememek:
version = "~> 5.0"yerineversion = ">= 5.0"yazmak ilerleyen zamanlarda breaking change’e yol açabilir. Her zaman tilde-major notation kullanın.
- Terraform.tfvars’ı Git’e push etmek: Credentials veya hassas veriler bu dosyada bulunabilir.
.gitignore‘a eklemek zorunlu.
- State dosyasını elle düzenlemek: State bozulursa
terraform statekomutlarını kullanın, dosyayı elle düzenlemeyin.
terraform destroy‘u yanlış ortamda çalıştırmak: Workspace veya-var-filekontrolünü atlamamak içinTF_WORKSPACEenvironment variable’ı ve shell alias’ları kullanın.
- Büyük monolitik
main.tf: Her şeyi tek dosyaya yazmak yerine modüler yapıya geçin. Network içinnetwork.tf, compute içincompute.tfgibi.
Terraform Fmt ve Validate
Kod kalitesi için iki komut günlük kullanımın parçası olmalı:
# Tüm .tf dosyalarını otomatik formatla
terraform fmt -recursive
# Sözdizimi ve mantık hatalarını kontrol et
terraform validate
# Çıktı:
# Success! The configuration is valid.
terraform fmt kodunuzu HCL standartlarına göre düzenler, boşlukları ve hizalamaları düzeltir. CI/CD pipeline’ınıza terraform fmt -check ekleyin, format hatası varsa pipeline başarısız olsun.
CI/CD Entegrasyonu için Temel Yapı
GitLab CI örneğiyle nasıl entegre edeceğinize kısa bir bakış:
# .gitlab-ci.yml
stages:
- validate
- plan
- apply
variables:
TF_ROOT: ${CI_PROJECT_DIR}
TF_VERSION: "1.7.0"
terraform:validate:
stage: validate
image: hashicorp/terraform:${TF_VERSION}
script:
- terraform init -backend=false
- terraform fmt -check -recursive
- terraform validate
only:
- merge_requests
terraform:plan:
stage: plan
image: hashicorp/terraform:${TF_VERSION}
script:
- terraform init
- terraform plan -var-file="prod.tfvars" -out=tfplan
artifacts:
paths:
- tfplan
only:
- main
terraform:apply:
stage: apply
image: hashicorp/terraform:${TF_VERSION}
script:
- terraform init
- terraform apply tfplan
when: manual
only:
- main
when: manual ile apply aşaması elle onay gerektiriyor. Production’a otomatik apply yapmak, ne kadar testiniz olursa olsun, riskli. İnsan gözü bir kez daha geçmeli.
Sonuç
Terraform’u kurmak ve ilk provider’ı yapılandırmak başlangıç noktası ama asıl öğrenme “plan-apply” döngüsünde ve gerçek projeler üzerinde çalışırken geliyor. Bu yazıda ele aldıklarımızı özetlemek gerekirse:
Terraform kurulumunu HashiCorp’un resmi reposundan yapın, bu sayede güncellemeler sorunsuz gelir. Provider yapılandırmasında versiyon sabitleme ve credential yönetimi en kritik iki konu. Hiçbir zaman kimlik bilgilerini .tf dosyasına yazmayın, environment variable veya IAM role kullanın. State dosyasını uzaktan yönetin, S3+DynamoDB kombinasyonu production için standart kabul edilebilir.
Altyapıyı küçük, anlaşılır parçalara bölün. Her şeyi tek main.tf‘e doldurmak kısa vadede kolay görünse de ekip büyüdükçe ve proje karmaşıklaştıkça idare edilemez hale gelir. terraform fmt ve terraform validate komutlarını alışkanlık edinin, CI/CD pipeline’ınızın parçası yapın.
Bir sonraki adım olarak Terraform modülleri, workspace kavramı ve remote state referanslarını incelemenizi öneririm. Bu üç konu, kurumsal ölçekte Terraform kullanımının temel taşları.
