後ろを向いて後退します

これって前に進んでいることになりませんか?

Terraform組み込み関数・演算子紹介

一人Terraform Advent Calendar

この記事は一人Terraform Advent Calendarの4日目の記事です。

adventar.org

Terraform組み込み関数・演算子紹介

Terraformは、HCLが持つ組み込み関数と演算子を利用してレシピの高度な抽象化を可能としています。一般的なプログラミング言語に用意されているような関数・演算子と同様に扱うことができ、より柔軟にインフラを運用することができます。

www.terraform.io

紹介する組み込み関数・演算子

この記事では、よく使うものや便利なものをいくつかピックアップして紹介します。全ての関数・演算子を参照したい場合は上記公式ドキュメントの説明を読んでください。

  • 組み込み関数
    • cidrhost(iprange, hostnum)
    • concat(list1, list2, ...)
    • contains(list, element)
    • element(list, index)
    • format(format, args, ...)
    • index(list, elem)
    • join(delim, list)
    • length(list)
    • list(items, ...)
    • lookup(map, key, [default])
    • map(key, value, ...)
    • merge(map1, map2, ...)
    • split(delim, string)
    • timestamp()
    • title(string)
    • values(map)
  • 演算子

演算子解説

四則演算

+, -, *, /, %(integerのみ)がサポートされています。

# integer
add = "${1 + 2 + 3}" # add = 6
sub = "${10 - 5}" # sub = 5
mul = "${2 * 5}" # mul = 10
div = "${10 / 5}" # div = 2
mod = "${11 % 3}" # mod = 2

# float
add_f= "${1.5 + 2.5 + 3.5}" # add_f = 7.5
sub_f = "${10 - 4.5}" # sub_f = 5.5
mul_f = "${2.5 * 5}" # mul_f = 12.5
div_f = "${12.5 / 5.0}" # div_f = 2.5

等価演算子

==, != がサポートされています。

true_result_eq = "${1 == 1}" # true_result_eq = true
false_result_eq = "${1 == 2}" # false_result_eq = false
true_result_ne = "${1 != 2}" # true_result_ne = true
false_result_ne = "${1 != 1}" # false_result_ne = false

比較演算子

>, <, >=, <=が数値型にのみサポートされています。

result_gt = "${10 > 10}" # result_gt = false
result_ge = "${10 >= 10}" # result_ge = true
result_lt = "${5 < 10}" # result_lt = true
result_le = "${10 <= 5}" # result_le = false

二値論理演算子

&&, ||, !がboolean型にのみサポートされています

true_result_and = "${true && true}" # true_result_and = true
false_result_and = "${truel && false}" # false_result_and = false
true_result_or = "${true || false}" # true_result_or = true
fasle_result_or = "${false || false}" # false_result_or = false
true_result_unary = "${true && ! false}" # true_result_unary = true
false_result_unary = "${!(true || false)}" # false_result_unary = false

三項演算子

一般的な三項演算子の文法でsome_bool ? true_val : false_valのような形で書くことができます。some_boolにはboolean型の値を渡すようにしてください。

true_cond = "${true ? 10 : 0}" # true_cond = 10
false_cond = "${false ? 10 : 0}" # false_cond = 0

組み込み関数解説

cidrhost(iprange, hostnum)

iprangeで与えられたCIDRブロック内で有効なIPアドレスのうちhostnum番目のIPアドレスを返します。hostnumが負の値の場合は後ろから数えてIPアドレスを返します。

ip_addr_1 = "${cidrhost("192.168.0.0/16", 10)}" # ip_addr_1 = "192.168.0.10"
ip_addr_2 = "${cidrhost("192.168.0.0/16", -10)}" # ip_addr_2 = "192.168.255.246"

concat(list1, list2, ...)

引数で与えられたlist型の値を全て結合したlistを返します。

concat_list = "${concat(list(1, 2, 3, 4, 5), list(6, 7, 8, 9, 10))}" # concat_list = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"

contains(list, element)

listelementを要素として含んでいる場合にtrueを返します。

true_contain = "${contains(list(1, 2, 3, 4, 5), 1)}" # true_contain = true

element(list, index)

listindex番目の値を返します。list[index - 1]と同じ挙動です。ネストされたリストに対しては正常に振る舞いません。

item = "${element(list(1, 10, 100, 1000, 10000), 3)}" # item = 100 (1オリジン)

format(format, args, ...)

一般的なフォーマットストリングと同じ使い方です。Go言語のfmtパッケージにあるSprintf()を使って実装されているので、サポートされている記法もそれに準拠しています。

fmt - The Go Programming Language

str_1 = "${format("https://%s.%s", "example", "com")}" # str_1 = "https://example.com"
str_2 = "${format("my_%s_%03d", "ec2_instance", 5)}" # str_2 = "my_ec2_instance_005"

index(list, elem)

element(list, index)の逆で、listの中の指定された要素のindexを返します。ネストされたリストに対しては正常に振る舞いません。

item_index = "${index(list(1, 10, 100, 1000, 10000), 100)}" # item_index = 2 (0オリジン)

join(delim, list)

listの要素をdelimを区切り文字にして結合した文字列を返します。ネストされたリストに対しては正常に振る舞いません。

joined = "${join(" ", list("Come", "on", "baby", "America"))}" # joined = "Come on baby America"

length(list)

listの要素数を返します。ネストされているリストは1として数えられます。

len_1 = "${length(list(1, 2, 3, 4, 5))}" len_1 = 5
len_2 = "${length(list(list(1, 2, 3), list(4, 5, 6)))}" len_2 = 2

list(items, ...)

引数で受け取った値をlistにして返します。

list_1 = "${list(1, 2, 3, 4, 5)}" # list_1 = [1, 2, 3, 4, 5]
list_2 = "${list(list(1, 2, 3), list(4, 5, 6))}" # list_2 = [[1, 2, 3], [4, 5, 6]]

lookup(map, key, [default])

mapの中でkeyに対応する値を返します。keyがない場合はエラーになりますが、defaultが定義されている場合はdefaultを返します。

some_map = {
  "hoge": "fuga",
  "foo": "bar"
}
val_1 = "${lookup(some_map, "hoge")}" # val_1 = "fuga"
val_2 = "${lookup(some_map, "fuu", "Not Found")}" # val_2 = "Not Found"

map(key, value, ...)

2N個の引数を受け取り、奇数番目の引数にそれぞれ次の引数の値を対応づけてmapを生成し返します。奇数番目のkeyになる引数は全てstring型でなければなりません。

map_1 = "${map("key_1", "val_1")}" # map_1 = {"key_1": "val_1"}
map_2 = "${map("key_1", "val_1", "key_2", "val_2")}" # map_2 = {"key_1": "val_1", "key_2": "val_2"}

merge(map1, map2, ...)

2つ以上のmapを結合したmapを返します。keyが重複している場合、対応する値は順に上書きされていきます。

merged_map_1 = "${merge(map("key_1", "val_1"), map("key_2", "val_2"))}" # merged_map_1 = {"key_1": "val_1", "key_2": "val_2"}
merged_map_2 = "${merge(map("key_1", "val_1"), map("key_1", "val_2"))}" # merged_map_2 = {"key_1": "val_2"}

split(delim, string)

join(delim, list)とは逆にstringdelimで区切った文字列のリストを返します。

split_str_1 = "${split(",", "a,b,c,d,e")}" # split_str_1 = ["a", "b", "c", "d", "e"]
split_str_2 = "${split(".", "a,b,c,d,e")}" # split_str_2 = ["a,b,c,d,e"]

timestamp()

関数がコールされたときのタイムスタンプを返します。実行の度に値が変わるので、リソースの作成日時をタグ付けしたい場合やリソースの更新日時を常に記録しておきたいような場合に有用です。

time = "${timestamp()}" # time = time = 2018-12-03T22:58:10Z

title(string)

引数のstringに含まれる全ての単語の頭文字を大文字にした文字列を返します。アルファベットと数字とアンダースコア(_)以外のアスキー文字は文章のセパレータとして認識され、セパレータの前後の文字列は全て別々の単語としてパースされます。

title_1 = "${title("this is a sample comment")}" # title_1 = "This Is A Sample Comment"
title_2 = "${title("a1a_a")}" # title_2 = "A1a_a"
title_3 = "${title("a`a~a!a@a#a$a%a^a&a*a(a)a-a=a+a[a{a]a}a|a;a:a'a,a<a.a>a/a?a")}" # title_3 = "A`A~A!A@A#A$A%A^A&A*A(A)A-A=A+A[A{A]A}A|A;A:A'A,A<A.A>A/A?A"

values(map)

引数のmapが持つkeyに対応する全ての値をリストにして返します。

values = "${values(map("key_1", "value_1", "key_2", "value_2"))}" # values = ["values_1", "values_2"]

さいごに

まだTerraform自体にバグがいくつか見つかったり、HCL自体に起因するバグがあるせいで関数が予期しない挙動をすることも多々あります。複雑なロジックを仕込むときにはdry-runをするなどして期待通りの値になっているかどうか確認することをオススメします。