IaCの運用が辛い原因は考え方じゃなくてツールだと言いたい

## 言いたいこと

* IaCの運用がビジネスのスピードに合わなくなってきている
* IaCが辛い原因は考え方じゃなくてツールにあると思う
* Terraform のmoduleの再利用性がないのは DI ができないから
* DIのできるインフラ構築ツールがでてくれば、インフラ構築の大変さが劇的に変わる（はず）

## 発端: IaC って辛いよね

発端となったのはこちらのスライドです。
[Infrastructure as Codeに疲れたので、僕たちが本来やりたかったことを整理する](https://t.co/7LC6mNO5fV)

簡単に説明すると、「IaCっていい加減つらいよね。なんでもかんでもCode化すればいいってもんじゃないよね。」という話です。
辛みについてはすごく共感できましたが、ただ、ここに語られてることって、 **IaCの辛さじゃなくて、terraform の辛さだと思うんです**。
ツールが現状の運用のニーズに合わなくなってきただけだと思うんです。

## Terraform がなぜ辛いのか？

Terraform が辛い原因を一言でいうと、 module のポータビリティーの低さだと私は考えています。

ここからは具体例で説明します。

### 例: ELB + SecurityGroup + Route53 record からなるモジュール

ELB, ACM, Security Group などの public 層のリソースをまとめたモジュールを作りたいとします。
ELBは以下のような定義であるとします:

```golang:elb.tf
resource "aws_elb" "internal" {
  name            = "${var.app_name}-${var.env}-internal"
  security_groups = ["${aws_security_group.internal-elb.id}"]
  subnets         = [
    "${lookup(var.subnet_ids, "private-1a")}",
    "${lookup(var.subnet_ids, "private-1c")}",
  ]
  internal        = true

  listener {
    instance_port      = "${var.listener_port["instance"]}"
    instance_protocol  = "http"
    lb_port            = "${var.listener_port["lb"]}"
    lb_protocol        = "https"
    ssl_certificate_id = "${data.aws_acm_certificate.wildcard.arn}"
  }

  health_check {
    healthy_threshold   = "${var.health_check["healthy_threshold"]}"
    unhealthy_threshold = "${var.health_check["unhealthy_threshold"]}"
    timeout             = "${var.health_check["timeout"]}"
    target              = "${var.health_check["target"]}"
    interval            = "${var.health_check["interval"]}"
  }

  cross_zone_load_balancing   = true
  idle_timeout                = 60
  connection_draining         = true
  connection_draining_timeout = 300

  tags {
    "Name" = "${var.app_name}-${var.env}-internal"
  }

  lifecycle {
    ignore_changes = ["access_logs", "idle_timeout"]
  }
}
```


このとき、 ELB を internet faced にするか inner にするかくらいなら、 boolean 変数で注入すれば外からコントロールできますよね。

```golang:elb.tf
  internal        = "${var.is_internal}"
```

ところが、ELBのリスナーをサービスAとサービスBで独自のものを作りたい場合があります。
運用に即した例としては、 **HTTP から HTTPS にリダイレクトする ELB** を作りたいとします。

ところが、現状のterraform はこれを外から注入する手段はありません。

## AsIs の解決策

AsIs の解決策としては、以下の2つです（module 使わないは論外として）:

1. ELB の定義をこの moudle から外に出す
1. ELBのリスナーだけ異なる似たような module を2つ作る

### 解決策1: ELBの定義を module の外に出す

この方法だと、そもそもALBの定義が module の外に出てしまうのだから、この module には重要なことが書かれなくなるわけです。これは module を使わない、といっているのと似たような感じになります。

### 解決策2: ELBの定義だけ差し替えた2つの module を作る

以下のような2つのモジュールを作ります。

* module1: `public-elb`
* module2: `public-elb-with-redirect`

module2 のELBの定義は以下のようになります（module1との差分のみ表示）:

```go:public-elb-with-redirect/elb.tf
// ほかはmodule1と同じ
...
  listener {
    instance_port      = "${var.listener_port_ssl["instance"]}"
    instance_protocol  = "http"
    lb_port            = "${var.listener_port_ssl["lb"]}"
    lb_protocol        = "https"
    ssl_certificate_id = "${data.aws_acm_certificate.wildcard.arn}"
  }
...
```

この方法だと、 **似てるけど listner の部分だけ違う module** が複数できるわけです。これでは他の部分のコードが二重管理になります。さらに別の部分も変更したいとなると、似てるmoduleがどんどん増えていきます。これは管理がとても煩雑です。

### 本来やりたいこと = DI (Dependency Injection)

これに対して、listener 定義の部分だけ外部から注入できれば、module は1つですみます。これによって、汎用的な用途で使える public レイヤの module ができるわけです。これはいろんなところに使い回せます。

```js:本来やりたかったこと
// こんな言語はありませんが、あくまでイメージです。。
// public_elb module
const public_elb = new Elb({ listener: new Listener({ redirect: false }) });
// public_elb_with_redirect module
const public_elb_with_redirect = new Elb({ listener: new Listener({ redirect: true }) });
```

参考: [Dependency Injection (依存性の注入)](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5)

## terraform は module に DI を導入するか？

たぶん **No** だと思います。
terraform でこれをやろうとすると、根本的にアーキテクチャの変更を強いられると思うんです。だから多分terraform に DI は来ないと思う。
一方で、 [pulumi](https://github.com/pulumi/pulumi) や [aws-cdk](https://github.com/awslabs/aws-cdk) みたいなツールは汎用プログラミング言語でインフラ定義をするので、DIが比較的導入しやすいんじゃないかと思います。だからこれらのツールはこれから有望な選択肢になるんじゃないかと思うんです。
（もちろん、ツール側がDIを導入してくれなかったら意味はないんですが。。）


## DIができるツールが出てくればインフラ構築の辛さが劇的に変わる

DIができるツールが出てくれば、moduleに相当するもののポータビリティも上がるので、githubとかでいろいろな開発者がmoduleを公開していくようになると思います。
そうなると、オープンソースの資産が充実してきて、同じ要件をアリもので代用できるようになっていくと思います。つまり、インフラ定義が楽になると思うんです。

## 終わりに

つまるところ、だからみなさん一緒に aws-cdk や pulumi を勉強しませんか？という話ですw


## 参考

この一連のツイート
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">辛みについてはすごく共感できましたが、IaCが辛いんじゃなくて、terraformが現状の要件に合わなくなってきただけだと思う。aws-cdk とかpulumi みたいなterraform alternativeなツールも出てきていて、辛いところは解決する方向に向かっていくと思う。 <a href="https://t.co/dUTUpvCUie">https://t.co/dUTUpvCUie</a></p>&mdash; billthelizard (@billthelizardx) <a href="https://twitter.com/billthelizardx/status/1099488119659741184?ref_src=twsrc%5Etfw">2019年2月24日</a></blockquote>

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">terraform module 辛いって方、意見をお願いします。next terraform なツールの重要な要件だと思うんですよ。<a href="https://twitter.com/hashtag/jawsdays?src=hash&amp;ref_src=twsrc%5Etfw">#jawsdays</a> <a href="https://twitter.com/hashtag/jawsdays2019?src=hash&amp;ref_src=twsrc%5Etfw">#jawsdays2019</a> <a href="https://t.co/LDsZt5bq4u">https://t.co/LDsZt5bq4u</a></p>&mdash; billthelizard (@billthelizardx) <a href="https://twitter.com/billthelizardx/status/1099813706114822144?ref_src=twsrc%5Etfw">2019年2月24日</a></blockquote>