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

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

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

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

```

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

```hcl
variable "developer" {
  default = ["aoki0", "aoki1", "aoki2"]
}
```

## リソースを複数作成する

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

```hcl
resource "aws_iam_user" "developer" {
  count = 3
  name = "aoki"
  path = "/developer"
}
```

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

```bash
+ 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` というリストの長さを取得する関数がある。
こいつを、利用してリスト長分のリソースを作成する。

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

いい感じ。

```bash
+ 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` が使える。
これを、埋め込みたい箇所に `"${}"` でくくって記述する。

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

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

```bash
+ 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](https://github.com/hashicorp/terraform/issues/7705)が上がっていて、現在（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する。
たとえば、前述した`names`と`ips`をzipすると、 

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

となる。

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

## References
- [Terraform tips & tricks: loops, if-statements, and gotchas](https://blog.gruntwork.io/terraform-tips-tricks-loops-if-statements-and-gotchas-f739bbae55f9#.jlx1zzv26)
- [Configuring Variables - Terraform by HashiCorp](https://www.terraform.io/docs/configuration/variables.html)
- [Interpolation Syntax - Terraform by HashiCorp](https://www.terraform.io/docs/configuration/interpolation.html)
- [Cannot pass a list of maps to a resource/data · Issue #7705 · hashicorp/terraform · GitHub](https://github.com/hashicorp/terraform/issues/7705)