terraform の test コマンドを 30 分ほど触ってみました
はじめに
terraform の v1.6 から test
というコマンドが GA されたということなので触ってみました。
本記事で扱う環境は以下の通りです。
$ sw_vers
ProductName: macOS
ProductVersion: 14.0
BuildVersion: 23A344
$ terraform version
Terraform v1.6.5
on darwin_amd64
+ provider registry.terraform.io/hashicorp/aws v5.29.0
ちなみに、この記事は YAMAP エンジニア Advent Calendar 2023 三日目の記事です。
触ってみる
main.tf
ドキュメントに掲載されているサンプルコードをそのまま利用させて頂きます。
以下のように main.tf を用意します。
# main.tf
provider "aws" {
region = "eu-central-1"
}
variable "bucket_prefix" {
type = string
}
resource "aws_s3_bucket" "bucket" {
bucket = "${var.bucket_prefix}-bucket"
}
output "bucket_name" {
value = aws_s3_bucket.bucket.bucket
}
S3 バケットを作成するだけのとてもシンプルなコードです。これを terraform plan
すると、以下のような結果となります。
$ terraform plan -no-color
var.bucket_prefix
Enter a value: test
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.bucket will be created
+ resource "aws_s3_bucket" "bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "test-bucket"
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags_all = (known after apply)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ bucket_name = "test-bucket"
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
テストコード
続いてテストコードを用意します。デフォルトでは、同じディレクトリに .tftest.hcl
又は .tftest.json
という拡張子で用意する必要があります。
以下のように valid_string_concat.tftest.hcl を用意します。
# valid_string_concat.tftest.hcl
variables {
bucket_prefix = "test"
}
run "valid_string_concat" {
command = plan
assert {
condition = aws_s3_bucket.bucket.bucket == "test-bucket"
error_message = "S3 bucket name did not match expected"
}
}
このテストコードは、main.tf において、バケット名が test-bucket
という名前で作成されることを検証するコードになっています。run
ブロック内に検証内容を記載します。run
ブロックの詳細についてはドキュメントを確認しましょう。
command
にはお馴染みの plan
と apply
を指定することが出来ますが、apply
を指定すると、実際に AWS 上にリソースを作成して検証を行うようです。
尚、テストコードは、テストコード専用ディレクトリを用意して置いておくことも可能とのことです。その際には、以下のようにディレクトリを指定するオプションを付与する必要があります。
$ terraform test -test-directory=path
上記の例だと、path
というディレクトリ以下にテストコードを置いてある状態となります。
terraform test
terraform test
コマンドを実行してみます。
$ terraform test
valid_string_concat.tftest.hcl... in progress
run "valid_string_concat"... pass
valid_string_concat.tftest.hcl... tearing down
valid_string_concat.tftest.hcl... pass
Success! 1 passed, 0 failed.
run
ブロックの command
は plan
なので、リソースを作成するロジックとしては問題ないと判断出来ます。試しに command
を apply
にしてみましょう。
# valid_string_concat.tftest.hcl
variables {
bucket_prefix = "test"
}
run "valid_string_concat" {
command = apply
assert {
condition = aws_s3_bucket.bucket.bucket == "test-bucket"
error_message = "S3 bucket name did not match expected"
}
}
terraform test
コマンドを実行してみます。
$ terraform test
valid_string_concat.tftest.hcl... in progress
run "valid_string_concat"... fail
╷
│ Error: creating Amazon S3 (Simple Storage) Bucket (test-bucket): BucketAlreadyExists: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.
│ status code: 409, request id: BVDRRB6V1W38XE5N, host id: 1V/R45wOABWWye67vXvhDP1A2jdtjgTGppfX4dFza3eHHE333lt6pXAk8d0wtGHEUZVIgcmg1tU=
│
│ with aws_s3_bucket.bucket,
│ on main.tf line 9, in resource "aws_s3_bucket" "bucket":
│ 9: resource "aws_s3_bucket" "bucket" {
│
╵
valid_string_concat.tftest.hcl... tearing down
valid_string_concat.tftest.hcl... fail
Failure! 0 passed, 1 failed.
テストが失敗しました。既に test-bucket
は存在しているとのことです (S3 のバケット名は全てのユーザーで一意にする必要がありますね) のでテストの失敗は想定内です。では、バケット名が一意になるようにランダムな文字列をバケット名にしてみましょう。
# valid_string_concat.tftest.hcl
# ランダムな文字列をバケット名のプレフィックスにしてみました
variables {
bucket_prefix = "rin9wee8teziem4a"
}
run "valid_string_concat" {
command = apply
assert {
condition = aws_s3_bucket.bucket.bucket == "test-bucket"
error_message = "S3 bucket name did not match expected"
}
}
テストコード内に variables
ブロックを書くことが出来るので、任意の変数を指定することが出来ます。
この状態でテストを実行してみましょう。
$ terraform test
valid_string_concat.tftest.hcl... in progress
run "valid_string_concat"... fail
╷
│ Error: Test assertion failed
│
│ on valid_string_concat.tftest.hcl line 10, in run "valid_string_concat":
│ 10: condition = aws_s3_bucket.bucket.bucket == "test-bucket"
│ ├────────────────
│ │ aws_s3_bucket.bucket.bucket is "rin9wee8teziem4a-bucket"
│
│ S3 bucket name did not match expected
╵
valid_string_concat.tftest.hcl... tearing down
valid_string_concat.tftest.hcl... fail
Failure! 0 passed, 1 failed.
バケット名は test-bucket
を期待していますが、実際に生成されるバケット名とは異なるので S3 bucket name did not match expected
というエラーメッセージが表示されました。
以上
terraform plan
と terraform test
は何が違うんだ? という思いから、ざくっと test
コマンドを触ってみました。
plan
はインフラ構成の変更を tfstate ファイルを用いてシュミレートし、意図した通りにインフラの変更が行われるかを検証するものである一方、test
は実際にリソースを作って terraform コードの振る舞いそのものを検証するという違いがあることで腹落ちしました。(私の理解に誤り等ありましたらご指摘頂けると幸いです。
おまけ
command
を apply
にしてテストをした場合、本当にリソース作っているのかなーと気になったので、terraform test
を実行した後に、愚直に aws cli を叩いて確認してみました。
本当にリソースを作っていましたw
Discussion