Terratestを利用したTerraformコードのテスト

## 概要
　IaCは同じ環境を何個でも何度でも作成することができるとの特徴があります。多くの方がIaCを利用する最も大きいな理由はこの特徴の活用のためであるたろうと考えています。ただ実施する前後でテストしたくありませんか。

　本記事ではTerratestを利用Terraformをテストする方法について紹介します。想定の環境はAWSです。

## Terratestでのテスト

#### Terratestとは
　TerratestはTerraform、Kubernetesなどをテストをするために利用されるツールであり、Go言語を利用しています。（詳細については紹介しません）
　Terratestは単体テストと結合テストによく利用されています。

　Terratestを利用する前提として以下の2点があります。
・Go言語が利用できる環境であること
・Goでプログラムが書けること

#### IaCにおける単体テスト、結合テストの流れ

IaCにおける単体テストと結合テストはアプリケーション開発における単体テストと結合テストと異なり、「デプロイ」が発生します。
IaCにおけるテストはリソースをデプロイし、デプロイしたリソースに対して設定が正しいことを確認します。確認後テストで作成したリソースをすべて削除します。

以下のように流れになり、本記事は主にValidateについて説明します。
・Deploy
・Validate
・Delete

#### 準備
TerratestはGo言語で書かれたテストコードファイルが存在すればテストすることが可能です。Terratestの公式的に提供しているGithubのコードを拝見する限り別フォルダに分けるべきであることから筆者は以下のフォルダ構成をよく利用しています。。
※TerratestのテストファイルはGo言語のテストファイルの命名ルールに従い[xxx_test.go]との形式を守る必要があります。
```
├── main.tf
├── output.tf
├── provider.tf
├── terraform.tf
├── test
│   ├── go.mod
│   ├── terraform_aws_vpc_test.go →これがTerratestで書かれたテストコードファイル
├── userdata
│   └── setup.sh
└── variables.tf
```

上記のテストコードファイルは以下のようなコードの構成になります。
以下のリンクを参考しました。
https://github.com/gruntwork-io/terratest/blob/master/test/terraform_hello_world_example_test.go

```go
package test

import (
	"testing"

	"github.com/gruntwork-io/terratest/modules/terraform"
	"github.com/stretchr/testify/assert"
)

func TestTerraformHelloWorldExample(t *testing.T) {
	// TerraformのOption
　　　　　　　　// 主にTerraformコードのディレクトリを指します。上記のような構成の場合に以下のようになります。
	terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
		TerraformDir: "../",
　　　　　　　　　　　　　　　　// 下記に変数の置き換えが可能です
　　　　　　　　　　　　　　　　Vars: map[string]interface{}{
			"instance_name": expectedName,
			"instance_type": instanceType,
		},

		// 環境変数の設定も可能です
		EnvVars: map[string]string{
			"AWS_DEFAULT_REGION": awsRegion,
		},
	})

	// テスト後リソースを削除するコード
	defer terraform.Destroy(t, terraformOptions)

	// Terraform InitとApplyを実行するコード
	terraform.InitAndApply(t, terraformOptions)

	// Outputが想定どおりであることをAssert Moduleを利用して比較する
	output := terraform.Output(t, terraformOptions, "hello_world")
	assert.Equal(t, "Hello, World!", output)
}
```

上記のコードで示している通り、リソースのデプロイ（作成）と削除は既存の関数を利用しているので、特に難しいところはありません。
Validateの部分に関しては筆者が使ってみた感想を共有したいと思います。

##### Terratestが提供しているライブラリを活用しましょう

 TerratestではAWS、Azure、GCPなどのよく利用されるクラウドのためにModuleを提供しています。
例としてAWSのModuleを利用する場合に以下のコードをImportに追加する形で利用できます。

```golang
import (
...
"github.com/gruntwork-io/terratest/modules/aws"
...
)
```
Terratestにて提供しているAWSのModuleはよく利用されている関数を提供していて、とても使いやすいです。

詳細は以下を参照：https://github.com/gruntwork-io/terratest/tree/master/modules/aws

#### Terratestが提供しているModuleが万能ではありません

場合によってTerratestにて使いたい機能が存在しないこともあります。(例えばVPCのSecurity Groupの詳細のルールの取得)

その場合はAWSなどで提供しているModuleを活用しましょう。
詳細は以下の参照：https://github.com/aws/aws-sdk-go/


#### Terratestが提供しているExampleを活用しましょう

Terratestにて提供しているExampleにて多くのテスト例が存在していて、IaCでどんなテストすればいいか、またテストの例を参照しながらテストコードを開発すると効率の向上にも繋がります。

Exampleのリンク：https://github.com/gruntwork-io/terratest/tree/master/examples


## 感想

Terratestはよくできているツールだと思います。ただ現状IaCに対してテストコードを開発するとの考え方を持っているプロジェクトがそもそもすくない状況ですので、IaCを採用することになったら検討してみてください。
またTerraformのライセンスが今年の8月にかわりましたので、今後の採用がもっと難しくなるかとおもいますが、IaCに対するテストが必要であるとの考え方が大事ですので、参考になればと思います。