【モダンTerraform】v0.11以降でdynamicとfor_eachが実装されるかも

今回は最近のhashicorp/terraformでの開発状況から、現時点での最新版であるv0.11.3で未実装な機能の中で個人的にかなり期待している機能について紹介します。
countパラメータとその限界
全てのリソースにはMeta-parametersが指定できる
Terraformには全てのリソースに対しMeta-parametersという属性を指定できます。
これはData Sourcesに対しても指定可能となっています。
具体的には以下のようなものがあります。(詳細はドキュメントを参照)
count (int)
リソースの作成数を指定 ※現時点ではモジュールには指定不可depends_on (list of strings)
リソースが依存するリソースを指定 -> 具体的には作成順序などが制御されるprovider (string)
マルチプロバイダーの定義をしている場合に利用するプロバイダーを指定できる
(例: AWSのリージョンごとにプロバイダー定義している場合にaws.westみたいに指定)lifecycle (configuration block)
リソースの作成/更新/破棄の挙動を調整する
それぞれに興味深いトピックはあるのですが、今日はcountについて扱います。
countパラメータの基本的な使い方
countパラメータは以下のように利用します。
以下の例ではディスクリソースを2つ作成しています。
resource sakuracloud_disk "disks" {
name = "${format("disk-%02d", count.index+1)}"
count = 2
}
countパラメータを指定すると、そのリソースの定義内でcount.indexという属性が利用できます。
これは自身のインデックスを参照するもので、0から始まる数値型の値となっています。
これを利用することで、localsなどとの併用で各リソースに固有の値を指定しておくことが可能です。
locals {
# 監視先のドメインとパスを指定
targets = [
{
domain = "example.com"
path = "/"
},
{
domain = "example.jp"
path = "/foo/"
},
]
}
resource sakuracloud_simple_monitor "monitors" {
# 自身のインデックスでマップ(targetsの各要素)を取得、キーがdomainの値を参照する
target = "${lookup(locals.target[count.index], "domain"}"
health_check = {
protocol = "https"
delay_loop = 60
path = "${lookup(locals.target[count.index], "path"}"
status = "200"
}
count = "${length(locals.target)}"
}
countが利用できない場面
上手く使えばtfファイルを非常にシンプルにしてくれるcountですが、利用できない場面というのがあります。
- リソースの属性
- モジュール
例えば以下の例ではリソースにsettingという属性があるのですが、これに対してのcount指定はできません。
resource "aws_elastic_beanstalk_environment" "myEnv" {
name = "test_environment"
application = "testing"
setting {
# !ここではcountが使えない!
namespace = "aws:elasticbeanstalk:application:environment"
name = "HTTP_PROXY"
value = "10.1.2.1:8080"
}
setting {
namespace = "aws:elasticbeanstalk:application:environment"
name = "TMPDIR"
value = "/var/myapp/tmp"
}
}
議論中: dynanicとfor_each
この問題はGitHub上でも解決に向けた議論が行われています。
参考: GitHub(hashicorp/terraform) - Support count in resource fields #7034
このIssueの中で提案されているのがdynamicブロックとfor_eachです。
これを利用して先ほどの例を書き直すと以下のようになります。
# DRAFT: まだ提案段階でv0.11時点ではこの書き方は利用できません
resource "aws_elastic_beanstalk_environment" "myEnv" {
name = "test_environment"
application = "testing"
dynamic "setting" {
for_each = var.environment_variables # map型の変数を指定
content {
namespace = "aws:elasticbeanstalk:application:environment"
name = setting.foreach.key # 現在のイテレーションの要素は`foreach`キーで参照できる
value = setting.foreach.value
}
}
}
マップ型変数や他で定義したリソースの属性などを利用して、各要素をイテレーションしながら値の参照ができるようです。
この例だとsettingブロック内のfor_eachでイテレーションされる各要素はsetting.foreachで参照でき、それぞれkeyとvalueのように参照できるようです。
以下のようにイテレーターに名前をつけることでネストも可能なようです。
# DRAFT: Not yet integrated into Terraform, and details may change before final release
dynamic "example" {
for_each = var.example_items
iterator = parent # 外側のループのイテレータの名前を指定
content {
dynamic "example" {
for_each = parent.children
iterator = child # 内側のループのイテレータの名前を指定
content {
value = child.value
parent_value = parent.value
}
}
}
}
これは嬉しいですね!
これまでこれを行うにはプロバイダー側で親リソース/子リソースに分割して実装する必要があり、
プロバイダーの実装者としてはなかなか辛い思いをしていました。
(リソースを親子に分割すると作成順序とかロックとか色々考慮が増えるのです…)
現在の実装状況は?
本日の時点では@apparentlymart氏が実験的に実装を進めているようです。
(前出のIssue内のコメントを参照)
期待して待ちましょう!!!
以上です。