Terraformで作った Amazon API Gatewayに変更があっても再デプロイされない問題に対処する

## 前提
- [aws_api_gateway_deployment](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html) リソースを使ってデプロイまでTerraformで実行している

	```sh:api.tf
	resource "aws_api_gateway_deployment" "production" {
	  depends_on = [
	    "aws_api_gateway_integration.lambda_integration",
	  ]

	  rest_api_id = "${aws_api_gateway_rest_api.main.id}"
	  stage_name  = "prod"
	}
	```
- 1回以上 `terraform apply` しているときにAPI Gatewayに関する変更を加えた


## 問題
[aws_api_gateway_rest_api](https://www.terraform.io/docs/providers/aws/r/api_gateway_rest_api.html) などに変更が追加しても、デプロイは初回に実行されているので `aws_api_gateway_deployment` に変更は生じず、再デプロイされない
（再デプロイされないのでAPIにアクセスしても意図した動作にならない）


### 再デプロイが必要になる変更例
[バイナリサポートを有効化](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html?shortFooter=true) するために [AWS: aws_api_gateway_rest_apiのbinary_media_types](https://www.terraform.io/docs/providers/aws/r/api_gateway_rest_api.html#binary_media_types) を追加

```diff:api.tf
resource "aws_api_gateway_rest_api" "main" {
  name = "${var.prefix}"
+
+  binary_media_types = [
+    "*/*",
+  ]
}
```

## 解決策
API Gateway に関係する tfファイルのハッシュを [stage_description](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html#stage_description) に含める。


```diff:api.tf
resource "aws_api_gateway_deployment" "production" {
  depends_on = [
    "aws_api_gateway_integration.lambda_integration",
  ]

  rest_api_id = "${aws_api_gateway_rest_api.main.id}"
  stage_name  = "prod"
+
+  # API Gatewayの変更時に再デプロイさせるためのハック
+  stage_description = "setting file hash = ${md5(file("api.tf"))}"
}
```

tfを書き換えてAPI Gatewayの設定が変更するためファイルハッシュをチェックすれば再デプロイが必要かどうか判定できる。

### リソースが再生成される条件
- [stage_description](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html#stage_description) に変更がある場合、 `forces new resource` となりリソースが再生成 == 再デプロイとなる

```sh:stage_description変更時のplan結果
-/+ aws_api_gateway_deployment.production (new resource required)
      id:                "4s1vdj" => <computed> (forces new resource)
      created_date:      "2019-02-03T02:22:17Z" => <computed>
      execution_arn:     "arn:aws:execute-api:ap-northeast-1:991090258880:al17cenlok/prod" => <computed>
      invoke_url:        "https://al17cenlok.execute-api.ap-northeast-1.amazonaws.com/prod" => <computed>
      rest_api_id:       "al17cenlok" => "al17cenlok"
      stage_description: "setting file hash = 104b824aa7f41a9f55658a512b8c229f" => "setting file hash = af89ba1e8a8db87c195ee35796b8e0c1" (forces new resource)
      stage_name:        "prod" => "prod"


Plan: 1 to add, 0 to change, 1 to destroy.
```

- [description](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html#description) はリソースの再作成ではなく変更となるため再デプロイされないのでNG

```sh:description変更時のplan結果
  ~ aws_api_gateway_deployment.production
      description: "setting file hash = ed3085ab0ec5ea2f6dfda005fe5009ea" => "setting file hash = 86934e55913dcfa50cf8dc975d4662ab"


Plan: 0 to add, 1 to change, 0 to destroy.
```

※ 変更があったときに再実行させる方法としては [triggers](https://www.terraform.io/docs/provisioners/null_resource.html#triggers) があるが、これは [Provisioners: null_resource](https://www.terraform.io/docs/provisioners/null_resource.html) 専用であるため、[AWS: aws_api_gateway_deployment](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html) では使えない

```text:triggersを設定した場合のplan結果
Error: aws_api_gateway_deployment.production: : invalid or unknown key: triggers
```

### 注意点
API Gatewayに関するtfファイルのみをチェック対象にしているため、対象ファイル中で使用されている [Input Variables](https://learn.hashicorp.com/terraform/getting-started/variables.html) (`"${var.hoge}"`) に変更があった場合は再デプロイされない。
この場合は、 `Input Variables` も `stage_description` に含めればOK。
あるいは、[timestamp()](https://www.terraform.io/docs/configuration/interpolation.html#timestamp-) を使うことで、毎回必ず再デプロイさせることも可能[^timestampを使った再デプロイ]。

```sh:aws_api_gateway_deployment
  stage_description = "timestamp = ${timestamp()}"
```

[^timestampを使った再デプロイ]: `${timestamp()}`を使う方法は、変更がない場合もリソースが再生成されてしまうのでおすすめしません

## 参考
### ソースコード[^ソースコードのlang指定]
[Terraform-Amazon-API-Gateway-AWS-Lambda/api.tf](https://github.com/tksugimoto/Terraform-Amazon-API-Gateway-AWS-Lambda/blob/f0b47a2ff4cc4b512b2c12c6b2e92b958d3bff58/api.tf)

[^ソースコードのlang指定]: Qiitaではterraform(HCL)をcodeのlangに指定できないらしいので、本記事中では見やすい`sh`を指定しています

### 関連Issue
- [aws_api_gateway_deployment doesn't get updated after changes · Issue #6613 · hashicorp/terraform](https://github.com/hashicorp/terraform/issues/6613)
    - 最初のIssue
    - ファイルハッシュを使ったハックが解決策として [コメントされた](https://github.com/hashicorp/terraform/issues/6613#issuecomment-322264393)
- [aws_api_gateway_deployment doesn't get updated after changes · Issue #162 · terraform-providers/terraform-provider-aws](https://github.com/terraform-providers/terraform-provider-aws/issues/162)
    - リポジトリ分割によって最初のIssueはCloseされ、 `terraform-provider-aws` リポジトリに移動された

### 関連記事
- [terraformでAPIGatewayのステージが自動更新されない時 - Qiita](https://qiita.com/ys_tydy/items/1783d7e6ed290239ec8e)
    - [variables](https://www.terraform.io/docs/providers/aws/r/api_gateway_deployment.html#variables) を使う方法