Terraform Döngüler (Loops)
Terraform declarative bir dildir ve count
, for_each
ve for
ifadelerini sağlayarak bir döngü sayesinde işleri daha hızları ve verimli halletmemize olanak sağlar. Bu yazıda her biri farklı bir senaryoda kullanılmak üzere tasarlanmış birkaç farklı döngü yapısını göstereceğiz.
Terraform ile sunulan döngüler nedir görelim:
count
parametresi, kaynaklar ve modüller üzerinde döngü oluşturmak için kullanılır.for_each
ifadeleri, kaynaklar, kaynak içindeki satır içi bloklar ve modüller üzerinde döngü oluşturmak için kullanılır.for
ifadesi, listeler ve mapler üzerinde döngü oluşturmak için kullanılır.string directive
ile, bir dize içindeki listelerin ve maplerin üzerinde döngü yapmak içi kullanılır.
Şimdi her birine sırasıyla bakalım.
1. count
Parametresi İle Döngüler
Daha önceki yazılarımızda, AWS konsolunu ziyaret ederek bir AWS Identity and Access Management (IAM) kullanıcısı oluşturdunuz. Artık bu kullanıcıya sahip olduğunuza göre, Terraform ile gelecekteki tüm IAM kullanıcılarını oluşturabilir ve yönetebilirsiniz. live/global/iam/main.tf
'de bulunan aşağıdaki Terraform kodu göz önünde bulundurun:
provider "aws" {
region = "us-east-2"
}
resource "aws_iam_user" "example" {
name = "ismet"
}
Bu kod, tek bir yeni IAM kullanıcısı oluşturmak için aws_iam_user
kaynağını kullanır. Peki üç yeni IAM kullanıcısı oluşturmak istiyorsanız? Genel amaçlı bir programlama dilinde muhtemelen bir for döngüsü kullanırdınız:
# Bu sadece bir pseudo code. Bu kod Terraform'da çalışmaz.
for (i = 0; i < 3; i++) {
resource "aws_iam_user" "example" {
name = "ismet"
}
}
Terraform, dilde yerleşik for-loop'lara veya diğer geleneksel prosedürel mantığa sahip değildir, bu nedenle bu sözdizimi çalışmayacaktır. Bununla birlikte, her Terraform kaynağının, kullanabileceğiniz count
adı verilen bir meta parametresi vardır. count
, Terraform'un en eski, en basit ve en sınırlı yineleme yapısıdır. Tek yaptığı, kaynağın kaç kopyasının oluşturulacağını belirlemektir. Üç IAM kullanıcısı oluşturmak için count
u şu şekilde kullanabilirsiniz:
resource "aws_iam_user" "example" {
count = 3
name = "ismet"
}
Bu kodla ilgili bir sorun, kullanıcı adlarının unique olması gerektiğinden, üç IAM kullanıcısının hepsinin aynı ada sahip olması ve bunun da bir hataya neden olmasıdır. Standart bir for döngüsüne erişiminiz varsa, her kullanıcıya benzersiz bir ad vermek için for döngüsündeki index'i (i) kullanabilirsiniz:
# Bu sadece bir pseudo code. Bu kod Terraform'da çalışmaz.
for (i = 0; i < 3; i++) {
resource "aws_iam_user" "example" {
name = "ismet.${i}"
}
}
Aynı şeyi Terraform'da gerçekleştirmek ve döngüdeki her "yinelemenin" index'ini almak için count.index
'i kullanabilirsiniz:
resource "aws_iam_user" "example" {
count = 3
name = "ismet.${count.index}"
}
plan
komutunu önceki kodda çalıştırırsanız, Terraform'un her biri farklı bir ada ("ismet.0", "ismet.1", "ismet.2") sahip üç IAM kullanıcısı oluşturmak istediğini göreceksiniz:
Terraform will perform the following actions:
# aws_iam_user.example[0] will be created
+ resource "aws_iam_user" "example" {
+ name = "ismet.0"
(...)
}
# aws_iam_user.example[1] will be created
+ resource "aws_iam_user" "example" {
+ name = "ismet.1"
(...)
}
# aws_iam_user.example[2] will be created
+ resource "aws_iam_user" "example" {
+ name = "ismet.2"
(...)
}
Plan: 3 to add, 0 to change, 0 to destroy.
Tabii ki, "ismet.0" gibi bir kullanıcı adı özellikle kullanılamaz. count.index
'i Terraform'daki bazı yerleşik işlevlerle birleştirirseniz, "döngünün" her "yinelemesini" daha da özelleştirebilirsiniz.
Örneğin, live/global/iam/variables.tf
'deki bir girdi değişkeninde istediğiniz tüm IAM kullanıcı adlarını tanımlayabilirsiniz:
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
Döngüler ve diziler içeren genel amaçlı bir programlama dili kullanıyorsanız, var.user_names
dizisinde i
indexini arayarak her IAM kullanıcısını farklı bir ad kullanacak şekilde yapılandırırsınız:
# Bu sadece bir pseudo code. Bu kod Terraform'da çalışmaz.
for (i = 0; i < 3; i++) {
resource "aws_iam_user" "example" {
name = vars.user_names[i]
}
}
Terraform'da aynı şeyi, aşağıdakilerle birlikte count
kullanarak gerçekleştirebilirsiniz:
resource "aws_iam_user" "example" {
count = length(var.user_names)
name = var.user_names[count.index]
}
Bu kodda length(<ARRAY>)
liste boyutunu verirken, ARRAY[<INDEX>]
ilgili index'teki iteme ulaşmamızı sağlar.
Şimdi plan
komutunu çalıştırdığınızda, Terraform'un her biri benzersiz, okunabilir bir ada sahip üç IAM kullanıcısı oluşturmak istediğini göreceksiniz:
Terraform will perform the following actions:
# aws_iam_user.example[0] will be created
+ resource "aws_iam_user" "example" {
+ name = "ismet"
(...)
}
# aws_iam_user.example[1] will be created
+ resource "aws_iam_user" "example" {
+ name = "ozlem"
(...)
}
# aws_iam_user.example[2] will be created
+ resource "aws_iam_user" "example" {
+ name = "furkan"
(...)
}
Plan: 3 to add, 0 to change, 0 to destroy.
Bir kaynakta count
kullandıktan sonra, bunun tek bir kaynaktan ziyade bir dizi kaynak haline geldiğini unutmayın. aws_iam_user.example
artık bir IAM kullanıcısı dizisi olduğundan, o kaynaktan (<PROVIDER>_<TYPE>.<NAME>.<ATTRIBUTE>
) bir özniteliği okumak için standart sözdizimini kullanmak yerine, hangi IAM kullanıcısını kullanacağınızı belirtmelisiniz.
<PROVIDER>_<TYPE>.<NAME>[INDEX].ATTRIBUTE
Örneğin, listedeki ilk IAM kullanıcısının Amazon Resource Name (ARN) sini çıkış değişkeni olarak sağlamak istiyorsanız aşağıdakileri yapmanız gerekir:
output "first_arn" {
value = aws_iam_user.example[0].arn
description = "ilk kullanıcının ARN bilgileri"
}
Tüm IAM kullanıcılarının ARN'lerini istiyorsanız, dizin yerine "*
" uyarı ifadesini kullanmanız gerekir:
output "all_arns" {
value = aws_iam_user.example[*].arn
description = "Tüm kullanıcıların ARN bilgileri"
}
apply
komutunu çalıştırdığınızda, first_arn
çıktısı yalnızca ismet için ARN'yi içerecek, all_arns
çıktısı ise tüm ARN'lerin listesini içerecektir:
$ terraform apply
(...)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
first_arn = "arn:aws:iam::123456789012:ismet/neo"
all_arns = [
"arn:aws:iam::123456789012:user/ismet",
"arn:aws:iam::123456789012:user/ozlem",
"arn:aws:iam::123456789012:user/furkan",
]
Terraform 0.13'ten itibaren count
parametresi modüllerde de kullanılabilir. Örneğin, tek bir IAM kullanıcısı oluşturabilen module/landing-zone/iam-user/main.tf
adresinde bir modülünüz olduğunu hayal edin:
resource "aws_iam_user" "example" {
name = var.user_name
}
K
ullanıcı adı bu modüle bir giriş değişkeni olarak iletilir:
variable "user_name" {
description = "The user name to use"
type = string
}
Ve modül, oluşturulan IAM kullanıcısının ARN'sini bir çıktı değişkeni olarak döndürür:
output "user_arn" {
value = aws_iam_user.example.arn
description = "The ARN of the created IAM user"
}
Bu modülü, aşağıdaki gibi üç IAM kullanıcısı oluşturmak için bir count
parametresiyle kullanabilirsiniz:
module "users" {
source = "../../../modules/landing-zone/iam-user"
count = length(var.user_names)
user_name = var.user_names[count.index]
}
Yukarıdaki kod, bu kullanıcı adları listesi üzerinde dolaşmak için count
kullanır:
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
Ve oluşturulan IAM kullanıcılarının ARN'lerini aşağıdaki gibi çıkarır:
output "user_arns" {
value = module.users[*].user_arn
description = "The ARNs of the created IAM users"
}
Bir kaynağa sayı eklemek onu bir dizi kaynak haline getirirken, bir modüle sayı eklemek onu bir dizi modüle dönüştürür.
Bu kodda apply
komutunu çalıştırırsanız, aşağıdaki çıktıyı alırsınız:
$ terraform apply
(...)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
all_arns = [
"arn:aws:iam::123456789012:user/ismet",
"arn:aws:iam::123456789012:user/ozlem",
"arn:aws:iam::123456789012:user/furkan",
]
Gördüğünüz gibi, count
, kaynaklarla ve modüllerle aşağı yukarı aynı şekilde çalışır.
Ne yazık ki, count
kullanışlılığını önemli ölçüde azaltan iki sınırlaması vardır. İlk olarak, bir kaynağın tamamı üzerinde döngü yapmak için count kullanabilseniz de, satır içi bloklar üzerinde döngü yapmak için bir kaynak içinde count
kullanamazsınız. Örneğin, aws_autoscaling_group
kaynağında etiketlerin nasıl ayarlandığını düşünün:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnets.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
}
Her etiket; key, value ve propagate_at_launch
değerlerine sahip yeni bir satır içi blok oluşturmanızı gerektirir. Önceki kod, tek bir etiketi sabit kodlar, ancak kullanıcıların özel etiketler geçmesine izin vermek isteyebilirsiniz. Bu etiketler arasında döngü yapmak ve dinamik satır içi etiket blokları oluşturmak için count
parametresini kullanmayı denemek isteyebilirsiniz, ancak ne yazık ki bir satır içi blok içinde count
kullanılması desteklenmez.
count
ile ilgili ikinci sınırlama, onu değiştirmeye çalıştığınızda olan şeydir. Daha önce oluşturduğunuz IAM kullanıcılarının listesini düşünün:
variable "user_names" {
description = "Create IAM users with these names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
Bu listeden "ozlem" ı kaldırdığınızı hayal edin. Terraform planını çalıştırdığınızda ne olur?
$ terraform plan
(...)
Terraform will perform the following actions:
# aws_iam_user.example[1] will be updated in-place
~ resource "aws_iam_user" "example" {
id = "ozlem"
~ name = "ozlem" -> "furkan"
}
# aws_iam_user.example[2] will be destroyed
- resource "aws_iam_user" "example" {
- id = "furkan" -> null
- name = "furkan" -> null
}
Plan: 0 to add, 1 to change, 1 to destroy.
Bir saniye, muhtemelen beklediğiniz bu değildi! Plan çıktısı, yalnızca "ozlem" IAM kullanıcısını silmek yerine, Terraform'un "ozlem" IAM kullanıcısını "furkan" olarak yeniden adlandırmak ve orijinal "furkan" kullanıcısını silmek istediğini gösteriyor. Neler oluyor?
Bir kaynakta count
parametresini kullandığınızda, o kaynak bir dizi kaynak haline gelir. Ne yazık ki, Terraform'un dizi içindeki her bir kaynağı tanımlama şekli, o dizideki konumu (indeks) ile olur. Yani, üç kullanıcı adıyla ilk kez apply
çalıştırıldıktan sonra, Terraform'un bu IAM kullanıcılarının dahili temsili şuna benzer:
aws_iam_user.example[0]: ismet
aws_iam_user.example[1]: ozlem
aws_iam_user.example[2]: furkan
Bir dizinin ortasından bir öğeyi kaldırdığınızda, ondan sonraki tüm öğeler birer birer geri kayar, bu nedenle planı yalnızca iki kullanıcı adıyla çalıştırdıktan sonra, Terraform'un dahili temsili şöyle görünecektir:
aws_iam_user.example[0]: ismet
aws_iam_user.example[2]: furkan
Furkan'ın index 2'den index 1'e nasıl geçtiğine dikkat edin. Index'i Terraform'a bir kaynağın kimliği olarak gördüğünden, bu değişiklik kabaca "index 1'deki kullanıcı adını furkan olarak yeniden adlandırın ve index 2'deki kullanıcıyı silin" anlamına gelir. Başka bir deyişle, bir kaynak listesi oluşturmak için sayımı her kullandığınızda, listenin ortasından bir öğeyi kaldırırsanız, Terraform o öğeden sonraki tüm kaynakları silecek ve ardından bu kaynakları yeniden sıfırdan oluşturacaktır. Sonuç, elbette, tam olarak istediğiniz şeydir (yani, furkan ve ismet adlı iki IAM kullanıcısı), ancak kaynakları silmek ve değiştirmek muhtemelen oraya ulaşmak istediğiniz gibi değildir.
Bu iki sınırlamayı çözmek için for_each
kullanacağız.
2. for_each
Parametresi İle Döngüler
for_each
ifadesi, (a) tüm kaynakların birden çok kopyasını, (b) bir kaynak içindeki satır içi bloğun birden çok kopyasını veya (c) bir modülün birden çok kopyasını oluşturmak için listeler, kümeler ve mapler arasında döngü oluşturmanıza olanak tanır. Önce bir kaynağın birden çok kopyasını oluşturmak için for_each
nasıl kullanılacağını inceleyelim. Sözdizimi şöyle görünür:
resource "<PROVIDER>_<TYPE>" "<NAME>" {
for_each = <COLLECTION>
[CONFIG ...]
}
PROVIDER
bir sağlayıcının adıdır (ör. aws),TYPE
o sağlayıcıda oluşturulacak kaynak türüdür (ör. instance),NAME
, bu kaynağa atıfta bulunmak için Terraform kodu boyunca kullanabileceğiniz bir tanımlayıcıdır (ör. , my_instance),COLLECTION
, döngüye alınacak bir küme veya maptir (bir kaynakta for_each kullanılırken listeler desteklenmez!)CONFIG
, o kaynağa özgü bir veya daha fazla bağımsız değişkenden oluşur. CONFIG içinde, COLLECTION'daki geçerli öğenin anahtarına ve değerine erişmek içineach.key
veeach.value
kullanabilirsiniz.
Örneğin, bir kaynakta for_each
kullanarak aynı üç IAM kullanıcısını şu şekilde oluşturabilirsiniz:
resource "aws_iam_user" "example" {
for_each = toset(var.user_names)
name = each.value
}
var.user_names
listesini bir kümeye dönüştürmek için toset
kullanımına dikkat edin. Bunun nedeni, for_each
'in yalnızca bir kaynakta kullanıldığında kümeleri ve mapleri desteklemesidir. for_each
bu küme üzerinde döngü yaptığında, her bir kullanıcı adının each.value
ile kullanılabilir olmasını sağlar.
Bir kaynakta for_each
kullandığınızda, yalnızca bir kaynak (veya count
daki gibi bir dizi kaynak) yerine bir kaynak mapi haline gelir. Bunun ne anlama geldiğini görmek için orijinal all_arns
ve first_arn
çıktı değişkenlerini kaldırın ve yeni bir all_users
çıktı değişkeni ekleyin:
output "all_users" {
value = aws_iam_user.example
}
terraform apply
'ı çalıştırdığınızda şunlar olur:
$ terraform apply
(...)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
all_users = {
"morpheus" = {
"arn" = "arn:aws:iam::123456789012:user/morpheus"
"force_destroy" = false
"id" = "morpheus"
"name" = "morpheus"
"path" = "/"
"tags" = {}
}
"neo" = {
"arn" = "arn:aws:iam::123456789012:user/neo"
"force_destroy" = false
"id" = "neo"
"name" = "neo"
"path" = "/"
"tags" = {}
}
"trinity" = {
"arn" = "arn:aws:iam::123456789012:user/trinity"
"force_destroy" = false
"id" = "trinity"
"name" = "trinity"
"path" = "/"
"tags" = {}
}
}
Terraform'un üç IAM kullanıcısı oluşturduğunu ve all_users
çıktı değişkeninin, anahtarların for_each
içindeki anahtarlar (bu durumda kullanıcı adları) olduğu ve değerlerin tümünün o kaynağın çıktıları olduğu bir harita içerdiğini görebilirsiniz. all_arns
çıktı değişkenini geri getirmek istiyorsanız, values
metodunu (sadece bir mapten değerleri döndürür) ve bir uyarı ifadesini kullanarak bu ARN'leri çıkarmak için biraz fazladan çalışma yapmanız gerekir:
output "all_arns" {
value = values(aws_iam_user.example)[*].arn
}
Bu size beklenen çıktıyı verir:
$ terraform apply
(...)
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
all_arns = [
"arn:aws:iam::123456789012:user/ismet",
"arn:aws:iam::123456789012:user/ozlem",
"arn:aws:iam::123456789012:user/furkan",
]
Artık, count
ile olduğu gibi bir dizi kaynak yerine for_each
ile bir kaynaklar mapine sahip olmanız büyük bir yetenek çünkü bu, öğeleri bir koleksiyonun ortasından güvenli bir şekilde kaldırmanıza olanak tanıyor. Örneğin, var.user_names
listesinin ortasından "ozlem" i tekrar kaldırır ve terraform plan
ı çalıştırırsanız, şunları göreceksiniz:
$ terraform plan
Terraform will perform the following actions:
# aws_iam_user.example["trinity"] will be destroyed
- resource "aws_iam_user" "example" {
- arn = "arn:aws:iam::123456789012:user/ozlem" -> null
- name = "ozlem" -> null
}
Plan: 0 to add, 0 to change, 1 to destroy.
Bu daha doğru gibi! Artık diğerlerini kaydırmadan yalnızca tam olarak istediğiniz kaynağı siliyorsunuz. Bu nedenle, bir kaynağın birden çok kopyasını oluşturmak için hemen hemen her zaman için count
yerine for_each
kullanmayı tercih etmelisiniz.
for_each
, modüllerle aşağı yukarı aynı şekilde çalışır. Daha önceki iam-user
modülünü kullanarak, for_each
kullanarak şu şekilde üç IAM kullanıcısı oluşturabilirsiniz:
module "users" {
source = "../../../modules/landing-zone/iam-user"
for_each = toset(var.user_names)
user_name = each.key
}
Ve bu kullanıcıların ARN'lerinin çıktısını aşağıdaki gibi verebilirsiniz:
output "user_arns" {
value = values(module.users)[*].user_arn
description = "The ARNs of the created IAM users"
}
Bu kodda apply
çalıştırdığınızda, beklenen çıktıyı alırsınız:
$ terraform apply
(...)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
all_arns = [
"arn:aws:iam::123456789012:user/ismet",
"arn:aws:iam::123456789012:user/ozlem",
"arn:aws:iam::123456789012:user/furkan",
]
Şimdi dikkatimizi for_each
'in bir başka avantajına çevirelim: Bir kaynak içinde birden çok satır içi blok oluşturma yeteneği. Örneğin, web sunucusu küme modülünde ASG için dinamik olarak etiket satır içi blokları oluşturmak için for_each
kullanabilirsiniz. İlk olarak, kullanıcıların özel etiketler belirlemesine izin vermek için, modules/services/webserver-cluster/variables.tf
'ye custom_tags
adlı yeni bir map değişkeni ekleyin:
variable "custom_tags" {
description = "Custom tags to set on the Instances in the ASG"
type = map(string)
default = {}
}
Ardından, production ortamında live/prod/services/webserver-cluster/main.tf
'de aşağıdaki gibi bazı özel etiketler ayarlayın:
module "webserver_cluster" {
source = "../../../../modules/services/webserver-cluster"
cluster_name = "webservers-prod"
db_remote_state_bucket = "(YOUR_BUCKET_NAME)"
db_remote_state_key = "prod/data-stores/mysql/terraform.tfstate"
instance_type = "m4.large"
min_size = 2
max_size = 10
custom_tags = {
Owner = "team-foo"
ManagedBy = "terraform"
}
}
Önceki kod, birkaç faydalı etiket belirler: Owner
etiketi, bu ASG'ye hangi takımın sahip olduğunu belirtir ve ManagedBy
etiketi, bu altyapının Terraform kullanılarak yönetildiğini belirtir. Ekibiniz için bir etiketleme standardı bulmak ve bu standardı kod olarak uygulayan Terraform modülleri oluşturmak genellikle iyi bir fikirdir.
Artık etiketlerinizi belirlediğinize göre, onları aws_autoscaling_group
kaynağında gerçekte nasıl ayarlarsınız? İhtiyacınız olan şey, aşağıdaki pseudo koda benzer şekilde, var.custom_tags
üzerinde bir for döngüsüdür:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnets.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
# Bu sadece bir pseudo code. Bu kod Terraform'da çalışmaz.
for (tag in var.custom_tags) {
tag {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
Önceki pseudo kod çalışmayacak, ancak bir for_each
ifadesi çalışacaktır. Dinamik olarak satır içi bloklar oluşturmak için for_each
kullanmanın sözdizimi şöyle görünür:
dynamic "<VAR_NAME>" {
for_each = <COLLECTION>
content {
[CONFIG...]
}
}
VAR_NAME
, her bir "yinelemenin" değerini depolayacak değişken için kullanılacak addır (each
yerine),COLLECTION
, yinelenecek bir liste veya maptir vecontent
bloğu, her yinelemeden ne üretileceğidir. COLLECTION'daki geçerli öğenin sırasıyla anahtarına ve değerine erişmek için içerik bloğu içinde<VAR_NAME>.key
ve<VAR_NAME>.value
kullanabilirsiniz.
Bir listeyle for_each
kullandığınızda, anahtarın index olacağını ve değerin o dizindeki listedeki öğe olacağını ve bir map ile for_each
kullanırken, anahtar ve değerin mapteki key/value çifti olacağını unutmayın.
Bunların hepsini bir araya getirerek, aws_autoscaling_group
kaynağında for_each
kullanarak dinamik olarak etiket bloklarını nasıl oluşturabileceğiniz aşağıda açıklanmıştır:
resource "aws_autoscaling_group" "example" {
launch_configuration = aws_launch_configuration.example.name
vpc_zone_identifier = data.aws_subnets.default.ids
target_group_arns = [aws_lb_target_group.asg.arn]
health_check_type = "ELB"
min_size = var.min_size
max_size = var.max_size
tag {
key = "Name"
value = var.cluster_name
propagate_at_launch = true
}
dynamic "tag" {
for_each = var.custom_tags
content {
key = tag.key
value = tag.value
propagate_at_launch = true
}
}
}
Şimdi terraform plan
çalıştırırsanız, şuna benzeyen bir plan görmelisiniz:
$ terraform plan
Terraform will perform the following actions:
# aws_autoscaling_group.example will be updated in-place
~ resource "aws_autoscaling_group" "example" {
(...)
tag {
key = "Name"
propagate_at_launch = true
value = "webservers-prod"
}
+ tag {
+ key = "Owner"
+ propagate_at_launch = true
+ value = "team-foo"
}
+ tag {
+ key = "ManagedBy"
+ propagate_at_launch = true
+ value = "terraform"
}
}
Plan: 0 to add, 1 to change, 0 to destroy.
3. for
İle Döngüler
Artık kaynaklar ve satır içi bloklar üzerinde nasıl döngü oluşturulacağını gördünüz. Peki ya tek bir değer oluşturmak için bir döngüye ihtiyacınız varsa? Web sunucusu kümesiyle ilgisi olmayan bazı örneklere kısaca göz atalım. Bir isim listesi alan bir Terraform kodu yazdığınızı hayal edin:
variable "names" {
description = "A list of names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
Tüm bu isimleri büyük harfe nasıl çevirebilirsiniz? Python gibi genel amaçlı bir programlama dilinde aşağıdaki for döngüsünü yazabilirsiniz:
names = ["ismet", "ozlem", "furkan"]
upper_case_names = []
for name in names:
upper_case_names.append(name.upper())
print upper_case_names
# Çıktı: ['ISMET', 'OZLEM', 'FURKAN']
Python, list comprehension olarak bilinen bir sözdizimi kullanarak aynı kodu bir satırda yazmanın başka bir yolunu sunar:
names = ["ismet", "ozlem", "furkan"]
upper_case_names = [name.upper() for name in names]
print upper_case_names
# Çıktı: ['ISMET', 'OZLEM', 'FURKAN']
Python ayrıca bir koşul belirleyerek ortaya çıkan listeyi filtrelemenize de olanak tanır:
names = ["ismet", "ozlem", "furkan"]
short_upper_case_names = [name.upper() for name in names if len(name) < 6]
print short_upper_case_names
# Çıktı: ['ISMET', 'OZLEM']
Terraform, for
expression biçiminde benzer işlevsellik sunar (önceki bölümde gördüğünüz for_each
expression ile karıştırılmamalıdır). Bir for
ifadesinin temel sözdizimi şöyledir:
[for <ITEM> in <LIST> : <OUTPUT>]
LIST
, döngü yapılacak bir liste olduğunda, ITEM
, LIST
içindeki her öğeye atanacak yerel değişken adıdır ve OUTPUT
, ITEM
'i bir şekilde dönüştüren bir ifadedir. Örneğin, var.names
içindeki ad listesini büyük harfe dönüştürmek için Terraform kodu şu şekildedir:
variable "names" {
description = "A list of names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
output "upper_names" {
value = [for name in var.names : upper(name)]
}
Bu kodda terraform apply
komutunu çalıştırırsanız, aşağıdaki çıktıyı alırsınız:
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
upper_names = [
"ISMET",
"OZLEM",
"FURKAN",
]
Python'un list comprehension ında olduğu gibi, bir koşul belirterek ortaya çıkan listeyi filtreleyebilirsiniz:
variable "names" {
description = "A list of names"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
output "short_upper_names" {
value = [for name in var.names : upper(name) if length(name) < 6]
}
Bu kodda terraform apply
çalıştırmak size şunu verir:
short_upper_names = [
"ISMET",
"OZLEM"
]
Terraform'un for
ifadesi, aşağıdaki sözdizimini kullanarak bir map üzerinde döngü oluşturmanıza da olanak tanır:
[for <KEY>, <VALUE> in <MAP> : <OUTPUT>]
Burada MAP
, döngüye alınacak bir map, KEY
ve VALUE
, MAP
'taki her bir anahtar/değer çiftine atanacak yerel değişken adlarıdır ve OUTPUT
, KEY
ve VALUE
'u bir şekilde dönüştüren bir ifadedir. İşte bir örnek:
variable "hero_thousand_faces" {
description = "map"
type = map(string)
default = {
ismet = "mühendis"
ozlem = "öğretmen"
furkan = "asker"
}
}
output "bios" {
value = [for name, role in var.hero_thousand_faces : "${name} bir ${role}"]
}
Bu kodda terraform apply
komutunu çalıştırdığınızda, aşağıdakileri alırsınız:
bios = [
"ismet bir mühendis",
"ozlem bir öğretmen",
"furkan bir asker",
]
Aşağıdaki sözdizimini kullanarak bir liste yerine bir map çıktısı almak için for
ifadeleri de kullanabilirsiniz:
# Loop over a list and output a map
{for <ITEM> in <LIST> : <OUTPUT_KEY> => <OUTPUT_VALUE>}
# Loop over a map and output a map
{for <KEY>, <VALUE> in <MAP> : <OUTPUT_KEY> => <OUTPUT_VALUE>}
Tek fark, (a) ifadeyi köşeli parantezler yerine küme parantezlerine sarmanız ve (b) her yinelemede tek bir değer vermek yerine, bir okla ayrılmış bir anahtar ve değer çıktısı almanızdır. Örneğin, tüm anahtarları ve değerleri büyük harf yapmak için bir mapi nasıl dönüştürebileceğiniz aşağıda açıklanmıştır:
variable "hero_thousand_faces" {
description = "map"
type = map(string)
default = {
ismet = "mühendis"
ozlem = "öğretmen"
furkan = "asker"
}
}
output "upper_roles" {
value = {for name, role in var.hero_thousand_faces : upper(name) => upper(role)}
}
İşte bu kodu çalıştırmanın çıktısı:
upper_roles = {
"ISMET" = "MÜHENDİS"
"OZLEM" = "ÖĞRETMEN"
"FURKAN" = "ASKER"
}
4. String Directive İle Döngüler
Daha önceki yazılarımızda, String içinde Terraform koduna başvurmanıza izin veren dize enterpolasyonlarını öğrendiniz:
"Merhaba, ${var.name}"
String directive, string enterpolasyonlarına benzer bir sözdizimi kullanarak string içinde kontrol ifadeleri (örneğin, döngüler ve if ifadeleri) kullanmanıza izin verir, ancak dolar işareti (${…}
) yerine yüzde işareti kullanırsınız (%{…}
).
Terraform, iki tür string directive destekler: döngüler ve koşul yönergeleri. Bu bölümde, for
döngülerini inceleyeceğiz; Koşullara bir sonraki yazımızda geri döneceğiz. for
string directive aşağıdaki sözdizimini kullanır:
%{ for <ITEM> in <COLLECTION> }<BODY>%{ endfor }
COLLECTION
, döngüye alınacak bir liste veya map olduğunda, ITEM
, COLLECTION
'daki her öğeye atanacak yerel değişken adıdır ve BODY
, her yinelemenin (ITEM
'e başvurabilir) oluşturulacağı şeydir. İşte bir örnek:
variable "names" {
description = "Names to render"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
output "for_directive" {
value = "%{ for name in var.names }${name}, %{ endfor }"
}
terraform apply
çalıştırdığınızda, aşağıdaki çıktıyı alırsınız:
$ terraform apply
(...)
Outputs:
for_directive = "ismet, ozlem, furkan, "
Ayrıca, for
döngüsünde size indexi veren for
string directive sözdiziminin bir sürümü de vardır:
%{ for <INDEX>, <ITEM> in <COLLECTION> }<BODY>%{ endfor }
İşte indexi kullanan bir örnek:
variable "names" {
description = "Names to render"
type = list(string)
default = ["ismet", "ozlem", "furkan"]
}
output "for_directive_index" {
value = "%{ for i, name in var.names }(${i}) ${name}, %{ endfor }"
}
terraform apply
çalıştırdığınızda, aşağıdaki çıktıyı alırsınız:
$ terraform apply
(...)
Outputs:
for_directive = "(0) ismet, (1) ozlem, (2) furkan, "
Her iki çıktıda da fazladan bir virgül ve boşluk olduğuna dikkat edin. Bunu, bir sonraki yazımızda açıklandığı gibi koşullu ifadeleri, özellikle if string directive kullanarak düzeltebilirsiniz.