Terraform, HashiCorp tarafından piyasaya sürülen bir açık kaynak “infrastructure as a code” (IaC) tool’udur. Yani kod yazarak sistemimizde bir takım değişiklikler yapabiliyoruz. Bu değişiklikler kaynak ekleme, çıkartma veya kaynak yönetimi olabilir. Bu şekilde manual yaptığımız işlemleri otomatize etmiş oluyoruz. IaC olarak kullanılan bir çok araç vardır. Aşağıdaki resimde bu anlamda kullanılan araçları görebilirsiniz.
Biz bu ve devamındaki bir kaç yazıda Provisioning Tool’u olan Terraformu konu alacağız. Yukarıdaki resimde görüldüğü gibi benzer işleri yapan tool’lar ayrılmış provisioning anlamında da 2 tool görülüyor. CloudFormation sadece Amazon Web Service Provider’ında kullanılırken Terraform ise platform agnostic’tir. Yani birçok cloud provider’ında ve ayrıca on-premises sistemlerde de kullanılmaktadır.
Terraform Yükleme İşlemi
Bu adımda 2 farklı linux dağıtımında yükleme işlemini gösteriyor olacağım.
Ubuntu için, önce wget ile zip klasörünü indiriyoruz. Zipten çıkarttıktan sonra klasörü /bin altına taşıyoruz ve bitiyor.
wget https://releases.hashicorp.com/terraform/1.0.11/terraform_1.0.11_linux_amd64.zip](https://releases.hashicorp.com/terraform/1.0.11/terraform_1.0.11_linux_amd64.zipunzip terraform_1.0.11_linux_amd64.zipsudo mv ./terraform /bin
AWS içinde Amazon Linux 2 AMI’si için, aşağıdaki komutları uyguluyoruz.
sudo yum update -y
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
sudo yum -y install terraform
İşlem bittikten sonra, aşağıdaki kod ile yüklenip yüklenmediğini kontrol ediniz.
terraform --version
Başarılı bir kurulum sonrasında aşağıdaki komutu da çalıştırdığınızda otomatik doldurma özelliği sayesinde hızlı kod yazımı mümkün olabiliyor.
terraform -install-autocomplete
Terraform platform bağımsız demiştik onun dışında avantaj olarak sayabileceğimiz iki özelliği daha var. Birincisi durum yönetimi, yani state file içerisinde oluşturulan tüm kaynakların son durumunu takip edebiliyoruz. İkincisi ise kullanıcı hatalarına karşı yanlış yapılmasını önlüyor. Hatalı işlem yapıldığında sizi uyarıp bunu mu demek istediniz gibi yardımcı olabiliyor.
Bu yazı dizisinde neler yapacağız biraz bahsedelim. Öncelikle basit kodlar ile AWS tarafında kaynaklar oluşturacağız. Takiben değişkenlerin farklı kullanımlarına bakıp conditionals ve loops kullanımını işleyeceğiz. Daha sonra Data resource tanımlaması, Remote backend ve Provisioner konularına değineceğiz. Son olarak module ve import konularını işleyip bitireceğiz.
Şimdi yavaştan kod yazmaya başlayalım.
Human readable bir dili ve çok güzel dokümantasyonu var. Aşağıdaki resimden aynı işlemleri yapan shell scripti ve terraform arasındaki farkı görebiliriz.
Öncelikle çalışacağımız provider’ı tanımlamamız gerekiyor. Baştan söyleyelim ezberden ziyade terraform’un resmi sitesi https://registry.terraform.io/ adresini kullanacağız. Provider’ın kaynaklarına erişmek ve işlem yapmak için de authonticate yapmamız gerekiyor. Bunun için 3 farklı yöntem belirteceğim.
- Eğer AWS üzerinden bir Instance ile işlem yapacaksanız IAM Role ile EC2’nuza erişmek istediğiniz kaynaklar için rol tanımlaması yapabilirsiniz.
- Aşağıdaki gibi oluşturacağımız provider bloğunun içerisine gömebiliriz. Uygun bir yöntem değildir. Şifrenin bu şekilde açıkta olmaması gerekir.
provider "aws" {
region = "us-east-1"
# access_key = "my-access-key"
# secret_key = "my-secret-key"
## profile = "my-profile"
}
Yeri gelmişken provider bloğundan kısaca bahsedelim. aws ile amazon cloud provider olduğunu belirttik. Region ekleyerek her kaynak oluşturmada bize region sormasının önüne geçtik. profile ile de birden çok aws hesabını yönetiyorsanız farklı profiller tanımlaması yapabiliyoruz.
3. AWC CLI kullanıyorsanız CLI configuration yaptığınızda herhangi bir işlem yapmanıza gerek kalmaz. Terraform .aws klasörü içindeki config dosyasıdan bilgileri alır. En uygun yöntem budur.
Terraform kullanırken farklı kod blokları kullanacağız. Örneğin provider, resource, terraform, module, data gibi. Bu araç nasıl çalışıyor önce onu görelim.
Yukarıdaki resme bakacak olursak sağ taraftaki kaynakları oluşturmak için sol taraftaki .tf uzantılı dosya içerisine kodu yazıyoruz. Bu örnekte 3 adet kaynak oluşturulacak. Bunun için de 3 komutumuz var. Birincisi terraform init
. Bu komutu .tf uzantılı dosyanın olduğu klasörde çalıştırıyoruz. Yaptığı işlem, çalışılan klasör içerisinde provider’ın API’ı ile iletişime geçebilmek için provider’ı tespit edip gerekli plug-in ve konfigurasyon dosyaları indirilir. Bu işlem initialize adımı oluyor. Takiben terraform plan
komutunu uyguluyoruz. Bu komut zorunlu değildir. Bize ön izleme yapmamızı sağlar yani sen bir sonraki adımda apply komutu çalıştırırsan şu kaynaklar oluşacak şunlar silinecek şunlar değişecek gibi detaylı bir sonuç çıkartacak. Aşağıda işaretler ve anlamlarını görebilirsiniz.
“+” → oluşturulacak
“-” → silinecek
“~” → değiştirilecek
Son adımda ise terraform apply
uyguluyoruz burada da artık kaynaklar oluşacak bu komutu takiben tamamlanmadan bizden onay isteyecek “yes” denildiğinde kaynaklar oluşturulacak. Eğer onay için sormasın istersek terraform apply -auto-approve
apply komutunu bu şekilde kullanabiliriz. 3 komut gördük yavaş yavaş diğer komutları da kullanımlarıyla göreceğiz.
Öncelikle terraform bloğuna bakalım.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.70.0"
}
}
}
Terraform bloğu ile plug-in’lerini indireceğimiz provider’ın versiyonunu görebiliyoruz. Ayrıca ileride göreceğimiz backend konusunda backend bloğunu da bu blok içinde yanımlayacağız. terraform init komutu ile buradaki versiyon bilgileri alınacak. Offical provider’lar için zorunlu bir blok değil terraform bu blok olmasa da aws’i tanıyıp plug-in’leri buluyor. Bu blok bilgilerine https://registry.terraform.io/providers/hashicorp/aws/latest adresinden ulaşabilirsiniz.
Provider bloğunu üst kısımda anlatmıştık. Şimdi resource üzerinden kod bloğunu anlamaya çalışalım.
Resme bakacak olursak ilk satırda kod bloğu türü burada resource bloğu, devamında local ile provider ismi, file ile de local provider’ında bir kaynak tipini yazmış oluyoruz. Aynı satırın sonundaki pet o kaynağa verilen isim. Bu değeri istediğimizi verebiliriz daha sonra terraform içinde o kaynağa ulaşmak için local_file.pet şeklinde ulaşacağız. Harf veya alt çizgi ile başlamalıdır ve yalnızca harf, rakam, alt çizgi ve tire içerebilir.
Bir tane de AWS’de bir resource tanımlayalım.
resource "aws_instance" "my_ec2" {
ami = "ami-061ac2e015473fbe2"
instance_type = "t2.micro"
}
aws_instance >> aws provider’ında instance tipinde bir kaynak
my_ec2 >> resource’a verdiğimiz isim.
ami ve instance_type >> Ayağa kaldıracağımız makinenin imaj ve tipi
Resource oluştuktan sonra aynı klasörde bazı dosyalar oluşacak. Bunlardan bir tanesi terraform.tfstate dosyası. Apply komutu sonrasında son durum bilgilerinin tutulduğu dosyadır. Oluşan kaynakların tüm detayları bu dosyadadır. İkincisi terraform.tfstate.backup dosyası bu dosya da bir önceki durum bilgilerini tutar. En az 2 apply yapılması lazım bunun oluşması için. Üçüncü dosya .terraform.lock.hcl dosyası. Bu dosya sayesinde provider versiyon kilitlemesi yapılır. Karışıklık çıkmaması için versiyonun değişmemesi gerekir.
Kaynakları İzleme
Şimdi sırayla 3 komut göstereceğim. Bu komutlar oluşturduğumuz kaynaklar hakkında bilgi almak için kullanılacak. Birincisi terraform state list. Bu komut ile hangi kaynaklar oluşmuş özet şeklinde görebiliriz.
İkinci komut ise terraform show. Bu komut ile de her kaynağın önemli özellikleri gayet okunaklı bir çıktı ile gösterilir.
Üçüncü komut terraform graph
. Bu komutla oluşan kaynakları görsel olarak izleyebiliyoruz. Önce komutu çalıştırıyoruz. Çıkan sonucu kopyalayıp https://dreampuf.github.io/GraphvizOnline bu sayfayı açtıktan sonra sol kısma kopyalıyoruz.
Hata Kontrolü ve Görünüm Düzeltme
Şimdi de yazım formatında hata var mı diye terraform validate
komutunu ve komutun daha düzgün okunaklı olmasın için kullanılan terraform fmt
komutunu göstereceğim.
validate sentax hatasını kontrol eder. Yukarıdaki örnekte bilerek resource yerine resourse yazdım. Hatayı gösterdi ve bunu mu demek istediniz şeklinde öneri verdi.
Şimdi de .tf dosyamızın içini biraz bozdum aşağıdaki gibi. ve daha sonra fmt komutunu uyguladım farkına bakalım.
Okunaklı hale getirdi.
Oluşturduğumuz kaynakları temizlemek içinde terraform destroy
komutunu uyguluyoruz. Onay istedikten sonra hepsini temizlemiş oluyor.
2 resource’um silinecek. Ayrıca spesifik bir resource’u silmek istersek parametre ile destroy komutunu aşağıdaki gibi kullanabiliriz.
terraform destroy -target=aws_s3_bucket.b
Sıradaki komutumuz terraform console
. Bu komut ile interaktif bir komut penceresi açılır. Built-in fonfsiyonları veya herhangi bir değeri görmek için kullanılabilir. Oluşturduğumuz instance’ın public ip sine bakalım.
Output
Bu kısımda output bloğu oluşturmayı göreceğiz. Öncelikle şunu ifade edelim. Terraform çalıştırılan klasördeki tüm .tf uzantılı dosyaları sanki tek file’mış gibi değerlendirir. Biz kodumuz daha okunaklı olsun diye output, variable gibi blokları ayrı file’lara yazarız. Demek istediğim ister ana çalıştığınız resource oluşturduğunuz sayfaya ekleyin ister ayrı file yapın farketmez. Ama best-pratice ayrı file olmasıdır. Kullanım şekli aşağıdaki gibidir.
Şimdi oluşturduğumuz ec2’nun public_ip’sini ve s3 bucket’ının ismini çıktı olarak alalım. Bunun 2 yolu var birincisi her apply veya plan komutu ile bu değerleri gösterir.
İkincisi ise sadece output ları göstermesi için. terraform output
komutunu kullanabiliriz. Ayrıca parametre verip filtreleme yapabiliriz.
Refresh
Daha önce belirttiğimiz gibi terraform state file’ı ile halihazırdaki tüm kaynakların durum bilgileri tutulur. Terraform ile oluşturduğumuz kaynakları konsol üzerinden değiştirirsek eğer buradaki state file’ı içindeki bilgiler ile gerçek durum arasında uyumsuzluklar olur. Bunu gidermek için terraform refresh
komutu uyguluyoruz.
Şuan 2 kaynağımız var. Şimdi konsoldan S3 bucket’ını sileceğim ve tekrar aynı komutu çalıştıracağım.
Yine aynı sonucu aldım. Çünkü terraform bunu bilmiyor. Bunun için terraform refresh komutunu çalıştırdıktan sonra tekrar bakalım.
Şimdi burada bir sıkıntı daha var. state file’ı güncellendi ama bizim main.tf aynı kaldı. Yani bir sonraki apply komutu ile sildiğimiz bucket tekrar oluşacak. Bunun için maint.tf de oluşturduğumuz bucket için olan resource bloğunu da kaldırmamız gerekecek.
terraform apply -refresh=false
apply komutunu bu parametre ile kullanırsak da şu anlama geliyor. Sen apply yaparken state file ile gerçek durumu kontrol etme yani konsoldan yapılan değişiklikleri dikkate alma. Biraz önceki senaryoyu burada uygularsak. S3 bucket’ı sildim ve apply komutunu tekrar yukarıdaki parametre ile uyguladım.
refresh ile güncelleme yapmadığı için state file da bucket hala var gözüküyor. apply yapıldığında yeni kaynak yokmuş gibi değerlendirecek. Aslında bunu şu şekilde kullanıyoruz. Eğer konsoldan değişiklik yapamadığımıza emin isek vakit kaybı olmasın işlem hızlı gerçekleşsin kontrol yapmasın amacıyla kullanıyoruz.
Variables
Variable’lar neden kullanılır? Başlıca sebepleri şunlardır:
- Gizli olması gereken bilgileri saklamak (AWS credentials gibi).
- Değişme ihtimali olan bilgileri rahat kullanmak (AMI’ler region’a göre değişiklik gösterir).
- Hazır variable’ları tekrar ve kolayca kullanmak.
Farklı şekillerde kullanılabilir.
- Yaygın kullanım variable blokları halinde farklı bir variable.tf file’ı içinde kullanılabilir.
Aynı klasör içinde farklı bir variable.tf içerisinde değişkenler hazırlandı. Aynı main.tf içinde de olabilirdi bu şekilde daha okunaklı olmuş oldu. Bu kullanımda artık herhangi bir değer değiştirmek istenirse ana dosyaya hiç dokunmamıza gerek kalmayacak. Variable içinde optional olan 2 değer daha eklenebilir. Birincisi type, ikincisi description. Description kullanımı best practice’dir. Type olarak, string, number, bool, list, map, object, tuple destekler.
variable bloğu içerisinde default parametresi de aslında optional’dır. default tanımlandıktan sonra içeriği boş bırakılırsa terraform apply yapıldığında interaktif şekilde ilgili değişkenleri girmememiz gerekir.
2. “*.tfvars” dosyası ile değişken atama. Kişiler kendine özgü değişken dosyası oluşturabilir samet.tfvars gibi. Bu uzantılı dosyalarda öncelik sırası vardır. terraform.tfvars önceliklidir. Ama kendi dosyanızın daha öncelikli olmasını isterseniz samet.auto.tfvars şeklinde auto ilave etmek gerekir.
Herhangi bir isimle .tfvars dosyası oluşturduk ve buradakiler geçerli olsun istersek apply veya plan komutunu aşağıdaki gibi çalıştırmamız gerekecek.
terraform apply -var-file="samet.tfvars”
samet.tfvars dosyasını değiştirmeden terraform.tfvars dosyasını da yapalım.
Eğer terraform.tfvars dosyası yaptıysak bunu -var-file=”terraform.tfvars” şeklinde belirtmemize gerek yok terraform apply yaptığımızda değişkeni alır.
3. Diğer bir kullanım plan veya apply komutuyla birlikte -var parametresi kullanmak.
terraform plan -var="ec2_name=ec2-degisken-parametre”
2. Diğer bir kullanım environment variable ile aşağıdaki gibi tanımlama yapabiliriz.
export TF_VAR_ec2_name= env-den-geldi
Yukarıda 4 farklı değişken tanımlamayı gösterdim. Bunların da kendi aralarında öncelik sırası var;
-var
veya-var-file
parametreleri ile apply komutu çalıştırılırsa önce bunu değerlendirir.- .tfvars dosyası hazırlandı ise sırada bu gelir. Burada da kendi içinde *.auto.tfvars sonra terraform.tfvars en son *.tfvars dosyası değerlendirilir.
- Environment variables
- Default variables
count && for_each
Şu ana dek teker teker kaynak oluşturduk peki bir seferde birden fazla kaynak nasıl oluşturacağız? Burada terraform da count veya for_each ifadelerini kullanacağız. Aynı örneği her iki kullanımla da yapacağım. 3 adet iam user oluşturalım. Önce count ile başlayalım.
Aynı örneği for_each ile yapalım. Fakat burada bir ayrıntı var for_each de variable set veya map tipinde olmalı bu değişikliği variable file ında veya main.tf de yapabiliriz.
Conditionals
Yapı aşağıdaki gibi kurulur.
KOŞUL ? (KOŞUL-DOĞRU-İSE ÇALIŞACAK) : (KOŞUL-YANLIŞ-İSE ÇALIŞACAK)
count = var.num_of_buckets > 0 ? 5 : 3
Yukarıdaki örnek üzerinden bakacak olursak var.num_of_bucket değeri 0’dan büyükse count=5 olacak eğer 0 ise count=3 olacak.
Farklı bir örnek yapalım oluşturduğumuz iam_user’ların isimlerinin uzunlukları 4 karakterden büyükse user isimlerini büyük harfe çevirerek output olarak alalım.
Faydalı VSCode eklenti önerileri:
IntelliJ IDEA Keybindings
HashiCorp Terraform
Terraform doc snippets
Burada kısa bir ara verelim ilerleyen konuları bir sonraki yazıda işleyeceğiz.
Hoşçakalın.