Terraform でループして複数のリソースを作成する

TerraformでListの変数を使ってループさせたい時がある。 例えば、ユーザアカウントを複数作りたいとき。

以下のようなリソース定義を必要な分だけ書かないといけない。10個あったら10個。メッチャ大変。

resource "aws_iam_user" "developer" {
  name = "foo"
  path = "/developer"
}

普通に考えて、変数を定義してループさせたくなる。 まず変数を定義する。あとは、コイツを上手く回す方法を考える。

variables "developer" {
  default = ["aoki0", "aoki1", "aoki2"]
}

リソースを複数作成する

リソースにはcountというパラメータがあり、この値を指定するとn個のリソースを作成してくれる。便利。

resource "aws_iam_user" "developer" {
  connt = 3
  name = "aoki"
  path = "/developer"
}

すると、3つの aws_iam_user が作成される。

+ aws_iam_user.developer.0
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

+ aws_iam_user.developer.1
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

+ aws_iam_user.developer.2
    arn:           "<computed>"
    force_destroy: "false"
    name:          "aoki"
    path:          "/developer"
    unique_id:     "<computed>"

Plan: 3 to add, 0 to change, 0 to destroy.

しかし、2つの課題がある。 - 生成する数(3)をハードコーディングしてる - name が全部同じ

変数に指定されたリストの長さを取得する

Terraformには length というリストの長さを取得する関数がある。 こいつを、利用してリスト長分のリソースを作成する。

resource "aws_iam_user" "developer" {
  count = "${ length( var.developer ) }"
  name = "aoki"
  path = "/developer"
}

いい感じ。

+ aws_iam_user.developer.0
+ aws_iam_user.developer.1
+ aws_iam_user.developer.2
Plan: 3 to add, 0 to change, 0 to destroy.

リストの要素を利用する

リストの要素を取得するには element という関数、 現在のインデックスを取得するには count.index が使える。 これを、埋め込みたい箇所に "${}" でくくって記述する。

resource "aws_iam_user" "developer" {
  count = "${ length( var.developer ) }"
  name = "${element(var.developer, count.index)}"
  path = "/developer"
}

ちゃんと、値も個数も設定ファイルから取得できた。

+ aws_iam_user.developer.0
    name:          "aoki0"
+ aws_iam_user.developer.1
    name:          "aoki1"
+ aws_iam_user.developer.2
    name:          "aoki2"
Plan: 3 to add, 0 to change, 0 to destroy.

あとは、nameも上手く取得したい。

MapのListも使える

変数の値を次のようにして、Listで回せばイロイロできそう。

ips = [
  { name = "foo", ip = "aaa.bbb.ccc.ddd/32", description = "foo IP" },
  { name = "bar", ip = "bbb.bbb.ccc.ddd/32", description = "bar IP" },
  { name = "baz", ip = "ccc.bbb.ccc.ddd/32", description = "baz IP" }
]

と、いいたいところなんですが、GitHubにIssueが上がっていて、現在(2016/11/29)使えない😢。

zipmap

対処療法として、二つのListを用意してzipmapで無理やり結合する。

# tfvars
names = ["foo", "bar", "baz"]
ips = [
 "aaa.bbb.ccc.ddd/32",
 "bbb.bbb.ccc.ddd/32",
 "ccc.bbb.ccc.ddd/32"
]

zipmap は二つのリストをzippingする。 たとえば、前述したnamesipsをzipすると、

{
  foo = "aaa.bbb.ccc.ddd/32",
  bar = "bbb.bbb.ccc.ddd/32",
  baz = "ccc.bbb.ccc.ddd/32"
}

となる。

はやくIssueが解決されて欲しい。

References