【新機能!】バージョン1.6.0で登場したterraform testsを試してみる!

どもども、最近炭酸水にどハマりしている僕です。


今回はterraformバージョン1.6.0で登場したtests機能を試してみたので紹介したいと思います。ていうかterraformってもう1.6なんですね。早いなぁ。

terraform tests

公式ドキュメントには以下のように記載されています。

Terraform tests allow authors to repeatability validate the functionality of their configuration in a safe environment.

https://developer.hashicorp.com/terraform/language/tests

これまではterraform planの結果をもとにインフラの設定が意図したものかを確認する、あるいはサードパーティ製のツールで検証するなどが一般的だったかと思います。


しかし今回新しく登場した機能を使用することで、HCLあるいはJSONで記載したテストケースをもとに構築したインフラの再現性を確認することができます。


実際に試してみましょう。

tests を試してみる

まずはterraformの利用バージョンを変更しておきましょう。以下はtfenvでバージョン管理している場合の例ですが、それぞれの環境に合わせてあらかじめ変更しておいてください。

$ tfenv install 1.6.0
$ tfenv use 1.6.0
$ tfenv list
* 1.6.0 (set by /usr/local/Cellar/tfenv/2.2.0/version)
...

続いて検証環境を準備します。

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.0"
    }
  }
}

provider "aws" {
  region  = local.region
  profile = local.profile
}
locals {
  region  = "<リージョン>"
  profile = "<profile名>"

  name_prefix = "terraform-test"
}

ここで一度"terraform init"を実行して初期化してください。これでterraformを利用する準備が完了します。

続いてresourceファイルを作成します。

resource "aws_s3_bucket" "sample_bucket" {
  bucket = "${local.name_prefix}-bucket"
}

これでいつも通りterraform planは通るはずです。続いてtestケースを書いていきます。".tftest.hcl"または".tftest.json"拡張子のファイルがテストに利用されるファイルとして検出されます。

run "valid_bucket_name" {

  command = plan

  assert {
    condition     = aws_s3_bucket.sample_bucket.bucket == "terraform-test-bucket"
    error_message = "S3 bucket name did not match expected"
  }

}

まずは公式ドキュメンにもあるように作成するバケットの名前が意図したものになるかテストしてみましょう。今回はインフラのプロビジョニングまでは行わないため

command = plan

のように設定を変更します。デフォルトではapplyとなります。

テストケースの記載方法は

  assert {
    condition     = 期待される状態
    error_message = 期待される状態ではなかった場合のエラーメッセージ
  }

となります。


それではテストを実行してみましょう。"terraform test"でテストが実行されます。

$ terraform test
sample.tftest.hcl... in progress
  run "valid_bucket_name"... pass
sample.tftest.hcl... tearing down
sample.tftest.hcl... pass

Success! 1 passed, 0 failed.

無事テストが通っていることがわかります。ではバケット名をtypoしてしまった場合を想定してすこしコードを書き換えてみましょう。

locals {
  region  = "ap-northeast-1"
  profile = "terraform"

  name_prefix = "teraform-test" # teraにtypoしている
}

再度テストを実行すると、しっかりエラーになっていることがわかりました。

❯ terraform test
sample.tftest.hcl... in progress
  run "valid_bucket_name"... fail
╷
│ Error: Test assertion failed
│ 
│   on sample.tftest.hcl line 6, in run "valid_bucket_name":
│    6:     condition     = aws_s3_bucket.sample_bucket.bucket == "terraform-test-bucket"
│     ├────────────────
│     │ aws_s3_bucket.sample_bucket.bucket is "teraform-test-bucket"
│ 
│ S3 bucket name did not match expected
╵
sample.tftest.hcl... tearing down
sample.tftest.hcl... fail

Failure! 0 passed, 1 failed.

続いてテストケースを追加してみましょう。例えば作成したS3バケットはオブジェクトが空ではない場合は削除できないようにしてほしいという要件があったとします。ちゃんと設定が行われているか確認しましょう。


この場合はs3の強制削除が無効になっていればいいので

force_destroy = false

となっていれば問題ありません。こればデフォルトの設定なのでコードに追記する必要はないです。そのままテストケースを追記しましょう。

run "valid_bucket_name" {

  command = plan

  assert {
    condition     = aws_s3_bucket.sample_bucket.bucket == "terraform-test-bucket"
    error_message = "S3 bucket name did not match expected"
  }

}

# 以下追記
run "unexpected_force_destroy_configration" {

  command = plan

  assert {
    condition     = aws_s3_bucket.sample_bucket.force_destroy == false
    error_message = "S3 bucket force_destroy configration did not match expected"
  }

}

見ての通りテストケースはrunブロックごとに一つの塊になっているため、runブロックないに条件を追記するか、新たにrunブロックを作成すればテストケースを追加できます。

今回はrunブロックを追加してテストを実行してみましょう。前回のテストで行ったバケット名typoの修正を忘れずに!

$ terraform test
sample.tftest.hcl... in progress
  run "valid_bucket_name"... pass
  run "unexpected_force_destroy_configration"... pass
sample.tftest.hcl... tearing down
sample.tftest.hcl... pass

Success! 2 passed, 0 failed.

2つのテストが無事通っていますね!それではs3の設定を少し変更してみましょう。

resource "aws_s3_bucket" "sample_bucket" {
  bucket = "${local.name_prefix}-bucket"
  force_destroy = true
}

あえてこのように設定する場面は少ないかもしれませんが、この状態でもう一度テストを実行してみます。

$ terraform test
sample.tftest.hcl... in progress
  run "valid_bucket_name"... pass
  run "unexpected_force_destroy_configration"... fail
╷
│ Error: Test assertion failed
│ 
│   on sample.tftest.hcl line 17, in run "unexpected_force_destroy_configration":
│   17:     condition     = aws_s3_bucket.sample_bucket.force_destroy == false
│     ├────────────────
│     │ aws_s3_bucket.sample_bucket.force_destroy is true
│ 
│ S3 bucket force_destroy configration did not match expected
╵
sample.tftest.hcl... tearing down
sample.tftest.hcl... fail

Failure! 1 passed, 1 failed.

新たに設定したテストケースが失敗していることがわかりました。

まとめ

今回はterraformで新たに追加された機能、testsを使用してみました。


考えられるユースケースは様々ありますが、例えば要件定義に合わせて先にテストケースを作成しておくと良さそうに思います。リソースごとに期待されている設定になっているかあらかじめテスト出来るのはいいですね。planによる確認ではやはり設定ミスがありますので…。


ここまで読んでいただきありがとうございました!

Terraform

Posted by CY