今回は最近の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内のコメントを参照)
期待して待ちましょう!!!
以上です。