🧪

terraform test: 基本機能

2023/12/16に公開

この記事は 3-shake Advent Calendar 2023 17 日目の記事です!

この記事に書いてあること

この記事を含め 3 回に渡って terraform test の機能を紹介します。

  1. terraform test: 基本機能 <- 今ここ
  2. terraform test: 応用機能
  3. terraform test: 細かい挙動

terraform test とはなにか

概要

terraform test は Terraform module を実際に plan / apply して動作を確認するツールです。

ドキュメントにも明記されている通り、主な使用用途は module の動作確認になります。

できること

terraform test でできることの一例は下記のとおりです。

  • terraform plan をした結果が想定どおりかをテストする。
  • terraform apply をした結果が想定どおりかをテストする。
    • arn のような plan 時点では (known after apply) となってしまうプロパティの確認
    • plan ではエラーが出ないが、apply に失敗するケースの確認
      • 指定したリージョンで作ろうとしたサービスが使えないなど
  • terraform apply をした後に、別のリソースを使用してテストする。
  • テスト用のリソースの作成と削除
    • テスト対象のリソースを作成する前に、依存するリソースを作成することもできる。
      • 例えば EKS cluster を作成する module をテストする前に VPC を作成するなど

何が嬉しいのか

terraform test の嬉しいことは下記の点です。

  • Terraform module のテストが簡単になる
    • 依存するリソースやテスト対象のリソースを別々に terraform apply して目視で確認、リソースの削除を一度に行うことができ、負荷が下がります。
    • テストの負荷が減るとテストをするモチベーションが増します。
  • Terraform module のテストを CI に組み込みやすくなる。
    • コマンド一つで必要リソースの作成、テスト、リソースの削除ができるので CI に組み込む負荷が低くなります。
    • exit code で失敗がわかるので検知が容易です。

terraform test 機能説明: 基本編

基本的な使い方

terraform test では、 .tftest.hcl (もしくは .tftest.json) というファイルに run ブロックでテスト内容を定義することでテストを実行できます。

公式ドキュメントに記載されている S3 Bucket を作成する terraform をもとにサンプルを記載します。

# 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
}

上記のような terraform コードに対して、 run ブロック内に assert ブロックにて期待する動作を定義することでテストを行うことができます。

下記のサンプルコードでは、terraform plan を実行し、 S3 Bucket の名前が想定通りかを確認するコードです。

# 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"
  }

}

上記のコードがあるディレクトリで下記のコマンドを実行することでテストが実行できます。

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 ブロックごとに 1 ケースとして扱われ、 pass / fail の数が表示されます。
また、デフォルトでは plan や apply の結果は表示されません。

plan / apply 結果を表示する

terraform test を実行しただけだと、テストの結果 (success or fail) しか表示されません。

plan や apply の結果を確認したい場合、-verbose オプションを付けます。

terraform test -verbose

-verbose オプションをつけると、テスト結果は下記のようになります。

plan の場合は下記のように通常の terraform plan 結果が表示されます。

valid_string_concat.tftest.hcl... in progress
  run "valid_string_concat"... pass

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"

valid_string_concat.tftest.hcl... tearing down
valid_string_concat.tftest.hcl... pass

Success! 1 passed, 0 failed.

apply の場合は下記のような形となり、 plan のときに (known after apply) となっていたプロパティに値が入っていることがわかります。

valid_string_concat.tftest.hcl... in progress
  run "valid_string_concat"... pass

# aws_s3_bucket.bucket:
resource "aws_s3_bucket" "bucket" {
    arn                         = "arn:aws:s3:::saikyo-tftest-sample-bucket"
    bucket                      = "saikyo-tftest-sample-bucket"
    bucket_domain_name          = "saikyo-tftest-sample-bucket.s3.amazonaws.com"
    bucket_regional_domain_name = "saikyo-tftest-sample-bucket.s3.eu-central-1.amazonaws.com"
    force_destroy               = false
    hosted_zone_id              = "Z21DNDUVLTQW6Q"
    id                          = "saikyo-tftest-sample-bucket"
    object_lock_enabled         = false
    region                      = "eu-central-1"
    request_payer               = "BucketOwner"
    tags_all                    = {}

    grant {
        id          = "bf298b76840af7a84e1c52e0f05d1315347776dc9b7a3583b4aaed70df26e4f9"
        permissions = [
            "FULL_CONTROL",
        ]
        type        = "CanonicalUser"
    }

    server_side_encryption_configuration {
        rule {
            bucket_key_enabled = false

            apply_server_side_encryption_by_default {
                sse_algorithm = "AES256"
            }
        }
    }

    versioning {
        enabled    = false
        mfa_delete = false
    }
}


Outputs:

bucket_name = "saikyo-tftest-sample-bucket"

valid_string_concat.tftest.hcl... tearing down
valid_string_concat.tftest.hcl... pass

Success! 1 passed, 0 failed.

テストファイルのディレクトリ構成

terraform test はテスト対象の terraform コードがあるディレクトリで実行することを想定しています。

テストコードは、カレントディレクトリ内または tests/ ディレクトリ内の .tftest.hcl.tftest.json を検出して実行します。

variables

terraform test では、variables ブロックを使用することでテスト対象の module に variable の値を渡すことができます。

下記は variables ブロックにより module に値を渡すサンプルコードです。

# variable_precedence.tftest.hcl

variables {
  bucket_prefix = "test"
}

run "uses_root_level_value" {

  command = plan

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

}

run "overrides_root_level_value" {

  command = plan

  variables {
    bucket_prefix = "other"
  }

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

}

variables を指定できる場所は下記の二箇所あります。
どこに記載するかによって variable の効果範囲が異なります。

No. 場所 効果範囲
1 tftest.hcl の直下 tftest.hcl 内のすべての run ブロック
2 run ブロック内 variable を設定した run ブロック

効果範囲を変えることにより、一度 apply してから、variable を変更するようなテストもできます。
(例えば特定の variable の変更が replace を伴わないことを確認するなど)

providers

terraform test では、provider ブロックを使用することでテスト実行時の provider 設定を変更することができます。

下記は provider ブロックによりテストで使用可能な AWS アカウントを制限するサンプルコードです。

# provider.tftest.hcl
provider "aws" {
  allowed_account_ids = [
    "123456789012",
  ]
}

variables {
  bucket_prefix = "test"
}

run "test" {

  command = plan

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

terraform test で参照できる値

assert で参照できるリソースは、テスト対象の terraform コードから直接呼び出し可能な Named Values となります。

具体的には下記の値となります。

  1. Resources
  2. Input variables
  3. Local values
  4. Child module outputs
  5. Data sources
  6. Filesystem and workspace info
  7. Block-local values

基本編はここまでとなります。
次は terraform test の応用機能を紹介します!

Discussion