CLOVER🍀

That was when it all began.

Terraform 1.6で远加されたTerraform testing frameworkterraform testを詊す

これは、なにをしたくお曞いたもの

Terraform 1.6で、Terraformのtesting frameworkterraform testコマンドが远加されたようなので、詊しおみようかなず。

Terraform 1.6 adds a test framework for enhanced code validation

今たでTerraformのテストずいえばlintずTerratestだったず思いたすが、遞択肢が広がりそうですね。

Terraform testing framework

Terraform 1.6に関するリリヌスブログはこちら。

Terraform 1.6 adds a test framework for enhanced code validation

この䞭に、Terraform test frameworkずいう芋出しがありたす。
これは、過去に提䟛しおきた実隓的機胜を眮き換え、テストフレヌムワヌクを導入するもののようです。

実隓的機胜ずいうのは、0.15で远加されたterraform testのこずのようです。

Module Testing Experiment - Configuration Language | Terraform | HashiCorp Developer

たた、Terraformには以前から以䞋のような機胜がありたした。

ずはいえ、これらではテストのニヌズを満たすこずができたせん。そこで登堎するのがTerraform test frameworkです、ず。

Terraform testing frameworkは、ざっくり以䞋の感じのもののようです。

  • terraform testで実行する
  • テストファむルは、ファむル名の末尟が.tftest.hclずなる
  • runブロックで定矩されたテストに基づいお、構成をプロビゞョニングする
  • カスタムアサヌションを評䟡
  • テスト終了時には、プロビゞョニングしたリ゜ヌスを砎棄

぀たり、実際にリ゜ヌスを䜜成しお、怜蚌しお砎棄するずいう流れになりたす。

Terraform testing frameworkに関するドキュメントは、このあたりですね。

たず、CLIに぀いお远っおみたしょう。抂芁から。

Testing Terraform - Terraform CLI | Terraform | HashiCorp Developer

testing frameworkは、単䜓テストおよびむンテグレヌションテストをタヌゲットにしおいたす。

terraform testコマンドで実行し、構成ディレクトリ内でテストファむルを芋぀けおテストを行いたすざっくり〜ず曞いた内容ず同じ。

テストファむルは、独自の構文で曞くこずになりたす。

Tests - Configuration Language | Terraform | HashiCorp Developer

コマンド自䜓の説明を芋おみたす。

Command: test | Terraform | HashiCorp Developer

terraform testは、以䞋の動䜜を行うようです。

  • 珟圚のディレクトリず指定されたテストディレクトリデフォルトでtestsでテストファむルを怜玢し、テストを実行する
  • テストファむルの仕様に埓っお、terraform planおよびterraform applyを行う
  • テストファむルの仕様に埓っお、プランファむルずステヌトファむルの怜蚌を行う
  • ステヌトはメモリヌ䞊で保持し、空から開始しお既存のステヌトに圱響を䞎えない
  • テスト甚のむンフラリ゜ヌスを䜜成し、テストの終了時に砎棄する。砎棄できなかった堎合は、察象のリ゜ヌスのリストをレポヌトする

䜿いそうなオプションずしおは、テストファむルを限定する-filter、テストディレクトリを指定する-test-directoryデフォルトではtests、
テストファむル内のrunに基づいお各ブロックのプランおよびステヌトを出力する-verboseあたりでしょうか。

あずは、チュヌトリアルを芋おみたしょう。

Write Terraform Tests | Terraform | HashiCorp Developer

チュヌトリアルを芋おいるず、テスト内でテストのヘルパヌモゞュヌル通垞の.tfファむルを䜜成するこずもあるようですね。

ドキュメントを眺めるのはこれくらいにしお、実際に䜿っおいっおみたしょう。

環境

今回の環境は、こちら。

$ terraform version
Terraform v1.6.0
on linux_amd64

テスト察象のリ゜ヌス䜜成には、LocalStackを䜿うこずにしたす。

$ python3 -V
Python 3.10.12


$ localstack --version
2.3.2

起動。

$ localstack start

AWS CLILocalStackも䜿いたしょう。

$ awslocal --version
aws-cli/2.13.25 Python/3.11.5 Linux/5.15.0-86-generic exe/x86_64.ubuntu.22 prompt/off

テスト察象のモゞュヌルを䜜成する

たずはテスト察象のルヌトモゞュヌルを䜜成したす。今回は、Amazon S3バケットを䜜るこずにしたしょう。

versions.tf

terraform {
  required_version = "1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.20.0"
    }
  }
}

main.tf

provider "aws" {
  access_key                  = "mock_access_key"
  secret_key                  = "mock_secret_key"
  region                      = "us-east-1"
  s3_use_path_style           = true
  skip_credentials_validation = true
  skip_metadata_api_check     = true
  skip_requesting_account_id  = true

  endpoints {
    apigateway     = "http://localhost:4566"
    cloudformation = "http://localhost:4566"
    cloudwatch     = "http://localhost:4566"
    dynamodb       = "http://localhost:4566"
    es             = "http://localhost:4566"
    firehose       = "http://localhost:4566"
    iam            = "http://localhost:4566"
    kinesis        = "http://localhost:4566"
    lambda         = "http://localhost:4566"
    route53        = "http://localhost:4566"
    redshift       = "http://localhost:4566"
    s3             = "http://localhost:4566"
    secretsmanager = "http://localhost:4566"
    ses            = "http://localhost:4566"
    sns            = "http://localhost:4566"
    sqs            = "http://localhost:4566"
    ssm            = "http://localhost:4566"
    stepfunctions  = "http://localhost:4566"
    sts            = "http://localhost:4566"
  }
}

resource "aws_s3_bucket" "standalone_bucket" {
  bucket = var.standalone_bucket_name
}

resource "aws_s3_bucket" "with_content_bucket" {
  bucket = var.with_content_bucket_name
}

resource "aws_s3_object" "content" {
  bucket  = aws_s3_bucket.with_content_bucket.bucket
  key     = var.content_key
  content = var.content_value
}

variables.tf

variable "standalone_bucket_name" {
  type = string
}

variable "with_content_bucket_name" {
  type = string
}

variable "content_key" {
  type    = string
  default = "test.txt"
}

variable "content_value" {
  type    = string
  default = "Hello World"
}

outputs.tf

output "standalone_bucket_name" {
  value = aws_s3_bucket.standalone_bucket.bucket
}

output "with_content_bucket_name" {
  value = aws_s3_bucket.with_content_bucket.bucket
}

output "content_key" {
  value = aws_s3_object.content.key
}

ずりあえず、暙準モゞュヌル構成に習っおみたした。

.
├── main.tf
├── outputs.tf
├── variables.tf
└── versions.tf

0 directories, 4 files

Amazon S3バケット名は、必須の倉数ずしおいたす。

確認しおおきたしょう。

$ terraform init
$ terraform plan -var 'standalone_bucket_name=my-bucket' -var 'with_content_bucket_name=my-with-content-bucket'
$ terraform apply -var 'standalone_bucket_name=my-bucket' -var 'with_content_bucket_name=my-with-content-bucket'

OKですね。

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

content_key = "test.txt"
standalone_bucket_name = "my-bucket"
with_content_bucket_name = "my-with-content-bucket"

AWS CLIでも確認しおおきたす。

$ awslocal s3 ls
2023-10-08 21:07:29 my-with-content-bucket
2023-10-08 21:07:29 my-bucket


$ awslocal s3 ls my-with-content-bucket/
2023-10-08 21:07:29         11 test.txt


$ awslocal s3 cp s3://my-with-content-bucket/test.txt -
Hello World

倧䞈倫そうなので、リ゜ヌスを砎棄しおおきたす。

$ terraform destroy -var 'standalone_bucket_name=my-bucket' -var 'with_content_bucket_name=my-with-content-bucket'

テストを曞いおみる

それでは、テストを曞いおみたす。テストはデフォルトではtestsディレクトリに眮くらしいので、今回はこれに習うこずにしたす。

$ mkdir tests

こんなテストを䜜成。テストはHCLHashiCorp Configuration Languageで蚘述したす。

tests/create_s3_buckets.tftest.hcl

run "create_buckets" {
  assert {
    condition     = aws_s3_bucket.standalone_bucket.bucket == "my-bucket"
    error_message = "standalone S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

terraform testを実行しおみたす。

$ terraform test

するず、倉数の指定がないず怒られたす。

tests/create_s3_buckets.tftest.hcl... in progress
  run "create_buckets"... fail
╷
│ Error: No value for required variable
│
│   on variables.tf line 1:
│    1: variable "standalone_bucket_name" {
│
│ The module under test for run block "create_buckets" has a required variable "standalone_bucket_name" with no set value. Use a -var or -var-file command line argument or add this variable
│ into a "variables" block within the test file or run block.
╵
╷
│ Error: No value for required variable
│
│   on variables.tf line 5:
│    5: variable "with_content_bucket_name" {
│
│ The module under test for run block "create_buckets" has a required variable "with_content_bucket_name" with no set value. Use a -var or -var-file command line argument or add this
│ variable into a "variables" block within the test file or run block.
╵
tests/create_s3_buckets.tftest.hcl... tearing down
tests/create_s3_buckets.tftest.hcl... fail

Failure! 0 passed, 1 failed.

では、variablesを远加しお指定。

run "create_buckets" {
  variables {
    standalone_bucket_name   = "my-bucket"
    with_content_bucket_name = "my-with-content-bucket"
  }

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

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

実行。

$ terraform test

今床はOKでした。

tests/create_s3_buckets.tftest.hcl... pass

Success! 1 passed, 0 failed.

曞いたテストを芋返しおみたしょう。

テストファむルに蚘述できる内容は、こちらです。

Tests - Configuration Language | Terraform | HashiCorp Developer

runブロックには、Terraformのコマンドをどのように実行するのかず、倉数やアサヌションを定矩したす。

アサヌションの定矩はCustom Conditionず同じです。

Custom Conditions - Configuration Language | Terraform | HashiCorp Developer

conditionには確認する内容をboolで評䟡できるもので指定し、倱敗した堎合はerror_messageに指定した内容が衚瀺されたす。

倱敗するアサヌションにしおみたしょう。

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


  assert {
    condition     = aws_s3_object.content.key == "test"
    error_message = "content key did not match expected"
  }

確認。

$ terraform test

するず、このような結果になりたした。

│ Error: Test assertion failed
│
│   on tests/create_s3_buckets.tftest.hcl line 8, in run "create_buckets":
│    8:     condition     = aws_s3_bucket.standalone_bucket.bucket == "bucket"
│     ├────────────────
│     │ aws_s3_bucket.standalone_bucket.bucket is "my-bucket"
│
│ standalone S3 bucket name did not match expected
╵
╷
│ Error: Test assertion failed
│
│   on tests/create_s3_buckets.tftest.hcl line 18, in run "create_buckets":
│   18:     condition     = aws_s3_object.content.key == "test"
│     ├────────────────
│     │ aws_s3_object.content.key is "test.txt"
│
│ content key did not match expected

テストも倱敗ですね。

Failure! 0 passed, 1 failed.

たた、テストのステップを芋返しおみたす。

tests/create_s3_buckets.tftest.hcl... in progress
  run "create_buckets"... pass
tests/create_s3_buckets.tftest.hcl... tearing down
tests/create_s3_buckets.tftest.hcl... pass

「in progress」ず出力されおいるずころで、実際にリ゜ヌスが䜜成されたす。この時に実際のリ゜ヌスを芋おみるず、リ゜ヌスが䜜成されお
いるこずが確認できるず思いたす。そしお「tearing down」で削陀されたす。

なので、実際にリ゜ヌスが䜜成されるこずは把握しおおくこずが倧切ですね。

variablesは、runず同じ䞊びに曞くこずもできたす。

tests/create_s3_buckets2.tftest.hcl

variables {
  standalone_bucket_name   = "my-bucket"
  with_content_bucket_name = "my-with-content-bucket"
}

run "create_buckets" {

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

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

こうするず、runブロックが耇数ある堎合はトップレベルで指定したvariablesの内容が枡されるこずになりたす。たた、runブロック内で
variablesを定矩しお、トップレベルで定矩した内容を䞊曞きするこずもできたす。

outputの内容を参照するこずもできたす。

tests/create_s3_buckets_condition_outputs.tftest.hcl

run "create_buckets" {
  variables {
    standalone_bucket_name   = "my-bucket"
    with_content_bucket_name = "my-with-content-bucket"
  }

  assert {
    condition     = output.standalone_bucket_name == "my-bucket"
    error_message = "standalone S3 bucket name did not match expected"
  }

  assert {
    condition     = output.with_content_bucket_name == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = output.content_key == "test.txt"
    error_message = "content key did not match expected"
  }
}

今回は、䟋瀺がリ゜ヌスの属性を芋おいるこずが倚いので、リ゜ヌスの属性を芋るこずにしたす。

planずapplyでテストしおみる

runブロックではTerraformのコマンドが実行されるず曞きたしたが、デフォルトではterraform applyが行われたす。

Tests / Run blocks

これをterraform planに倉曎するこずもできたす。たたrunブロックにはterraform plan甚のオプションもあるようですね。

こんな感じにしおみたした。

tests/create_s3_buckets_plan_and_apply.tftest.hcl

variables {
  standalone_bucket_name   = "my-bucket"
  with_content_bucket_name = "my-with-content-bucket"
}

run "create_buckets_plan" {
  command = plan

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

  # Condition expression could not be evaluated at this time. This means you have executed a `run` block with `command = plan` and one of the values your condition depended on is not known
  # until after the plan has been applied. Either remove this value from your condition, or execute an `apply` command from this `run` block.
  # assert {
  #   condition     = aws_s3_bucket.standalone_bucket.arn == "arn:aws:s3:::my-bucket"
  #   error_message = "standalone S3 bucket arn did not match expected"
  # }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  # Condition expression could not be evaluated at this time. This means you have executed a `run` block with `command = plan` and one of the values your condition depended on is not known
  # until after the plan has been applied. Either remove this value from your condition, or execute an `apply` command from this `run` block.
  # assert {
  #   condition     = aws_s3_bucket.with_content_bucket.arn == "arn:aws:s3:::my-with-content-bucket"
  #   error_message = "with content S3 bucket arn did not match expected"
  # }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

run "create_buckets_apply" {
  command = apply

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

  assert {
    condition     = aws_s3_bucket.standalone_bucket.arn == "arn:aws:s3:::my-bucket"
    error_message = "standalone S3 bucket arn did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.arn == "arn:aws:s3:::my-with-content-bucket"
    error_message = "with content S3 bucket arn did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

ポむントは、以䞋の指定ですね。

  command = plan

䞀芋、planずapplyで差がないように芋えたすが、以䞋のようにplanでは確認できないものもありたす。

  # Condition expression could not be evaluated at this time. This means you have executed a `run` block with `command = plan` and one of the values your condition depended on is not known
  # until after the plan has been applied. Either remove this value from your condition, or execute an `apply` command from this `run` block.
  # assert {
  #   condition     = aws_s3_bucket.standalone_bucket.arn == "arn:aws:s3:::my-bucket"
  #   error_message = "standalone S3 bucket arn did not match expected"
  # }


  # Condition expression could not be evaluated at this time. This means you have executed a `run` block with `command = plan` and one of the values your condition depended on is not known
  # until after the plan has been applied. Either remove this value from your condition, or execute an `apply` command from this `run` block.
  # assert {
  #   condition     = aws_s3_bucket.with_content_bucket.arn == "arn:aws:s3:::my-with-content-bucket"
  #   error_message = "with content S3 bucket arn did not match expected"
  # }

これぱラヌメッセヌゞずterraform planの結果を実際に芋るず想像が぀きたすが、「known after apply」぀たりterraform apply埌でないず
わからない倀に぀いおは、command = planを指定した時にアサヌションするこずができたせん。

  # aws_s3_bucket.standalone_bucket will be created
  + resource "aws_s3_bucket" "standalone_bucket" {
      + acceleration_status         = (known after apply)
      + acl                         = (known after apply)
      + arn                         = (known after apply)
      + bucket                      = "my-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)
    }

コメントにも曞いおいたすが、ムリに䜿っおも以䞋のような゚ラヌメッセヌゞが返っおきたす。

│ Error: Unknown condition value
│
│   on tests/create_s3_buckets_plan_and_apply.tftest.hcl line 17, in run "create_buckets_plan":
│   17:     condition     = aws_s3_bucket.standalone_bucket.arn == "arn:aws:s3:::my-bucket"
│     ├────────────────
│     │ aws_s3_bucket.standalone_bucket.arn is a string
│
│ Condition expression could not be evaluated at this time. This means you have executed a `run` block with `command = plan` and one of the values your condition depended on is not known
│ until after the plan has been applied. Either remove this value from your condition, or execute an `apply` command from this `run` block.

モゞュヌルを䜿う

テスト内でモゞュヌルを䜿うこずもできたす。

たずえば、今回のテストでは最初にterraform applyした時ず同じ倀をvariablesに指定しおいたす。

よっお、terraform applyしお

$ terraform plan -var 'standalone_bucket_name=my-bucket' -var 'with_content_bucket_name=my-with-content-bucket'

テストを実行するず今回はひず぀のテストファむルに絞っおいたす

$ terraform test -filter=tests/create_s3_buckets.tftest.hcl

以䞋のようにリ゜ヌスが競合するので倱敗したす。

│ Error: creating Amazon S3 (Simple Storage) Bucket (my-bucket): bucket already exists
│
│   with aws_s3_bucket.standalone_bucket,
│   on main.tf line 33, in resource "aws_s3_bucket" "standalone_bucket":
│   33: resource "aws_s3_bucket" "standalone_bucket" {
│
╵
╷
│ Error: creating Amazon S3 (Simple Storage) Bucket (my-with-content-bucket): bucket already exists
│
│   with aws_s3_bucket.with_content_bucket,
│   on main.tf line 37, in resource "aws_s3_bucket" "with_content_bucket":
│   37: resource "aws_s3_bucket" "with_content_bucket" {
│
╵

この埌、クリヌンアップが動きたすがすでに䜜成枈みのリ゜ヌスは削陀されたせん。テスト内で、䜜成に成功したリ゜ヌスのみを
削陀するようです。

では、テストで䜜成するリ゜ヌスが既存のものず競合しないようにvariablesを蚭定すればいいわけですが、今回はこれをランダムに
しおみたしょう。

5桁のランダム敎数を生成するモゞュヌルを䜜成。

tests/setup/random/main.tf

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = "3.5.1"
    }
  }
}

resource "random_integer" "bucket_prefix" {
  min = 10000
  max = 99999
}

output "bucket_prefix" {
  value = random_integer.bucket_prefix.id
}

このモゞュヌルを実行するrunブロックを含む、テストファむルを䜜成。

tests/create_s3_buckets_with_module.tftest.hcl

run "generate_random" {
  module {
    source = "./tests/setup/random"
  }
}

run "create_buckets" {
  variables {
    standalone_bucket_name   = "${run.generate_random.bucket_prefix}-my-bucket"
    with_content_bucket_name = "${run.generate_random.bucket_prefix}-my-with-content-bucket"
  }

  assert {
    condition     = aws_s3_bucket.standalone_bucket.bucket == "${run.generate_random.bucket_prefix}-my-bucket"
    error_message = "standalone S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.standalone_bucket.arn == "arn:aws:s3:::${run.generate_random.bucket_prefix}-my-bucket"
    error_message = "standalone S3 bucket arn did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.bucket == "${run.generate_random.bucket_prefix}-my-with-content-bucket"
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_bucket.with_content_bucket.arn == "arn:aws:s3:::${run.generate_random.bucket_prefix}-my-with-content-bucket"
    error_message = "with content S3 bucket arn did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

ポむントはこちらですね。

run "generate_random" {
  module {
    source = "./tests/setup/random"
  }
}

moduleのsourceには、terraformコマンドを実行する䜍眮からのパスを指定する必芁がありたす。このファむルからの盞察パスでは
ありたせん。

たた、モゞュヌルのoutputは以䞋のようにrun.[ブロック名].[output名]で参照できたす。

run "create_buckets" {
  variables {
    standalone_bucket_name   = "${run.generate_random.bucket_prefix}-my-bucket"
    with_content_bucket_name = "${run.generate_random.bucket_prefix}-my-with-content-bucket"
  }

  assert {
    condition     = aws_s3_bucket.standalone_bucket.bucket == "${run.generate_random.bucket_prefix}-my-bucket"
    error_message = "standalone S3 bucket name did not match expected"
  }

では、実行。

$ terraform test

するず、モゞュヌルがむンストヌルされおいないず蚀われるので

╷
│ Error: Module not installed
│
│   on tests/create_s3_buckets_with_module.tftest.hcl line 2, in run "generate_random":
│    2:   module {
│
│ This module is not yet installed. Run "terraform init" to install all modules required by this configuration.
╵

terraform initを実行したす。testsディレクトリ内や、モゞュヌルのあるディレクトリに移動する必芁はありたせん。

$ terraform init

今床はテストに成功したす。

tests/create_s3_buckets_with_module.tftest.hcl... in progress
  run "generate_random"... pass
  run "create_buckets"... pass

tests/create_s3_buckets_with_module.tftest.hcl... tearing down

tests/create_s3_buckets_with_module.tftest.hcl... pass

アサヌションを工倫する

アサヌションは、boolで指定すればいいものを曞けばよいのでした。

Custom Conditions / Condition Expressions

Terraformのドキュメントでは単玔に倀を等倀比范しおいるものが倚いのですが、関数なども䜿えるのかなず。

ずいうわけで、先ほどのモゞュヌルを䜿ったランダム敎数の箇所を正芏衚珟で確認するようにしおみたした。

tests/create_s3_buckets_assertion.tftest.hcl

run "generate_random" {
  module {
    source = "./tests/setup/random"
  }
}

run "create_buckets" {
  variables {
    standalone_bucket_name   = "${run.generate_random.bucket_prefix}-my-bucket"
    with_content_bucket_name = "${run.generate_random.bucket_prefix}-my-with-content-bucket"
  }

  assert {
    condition     = regex("\\d{5}-my-bucket", aws_s3_bucket.standalone_bucket.bucket) == aws_s3_bucket.standalone_bucket.bucket
    error_message = "standalone S3 bucket name did not match expected"
  }

  assert {
    condition     = regex("\\d{5}-my-with-content-bucket", aws_s3_bucket.with_content_bucket.bucket) == aws_s3_bucket.with_content_bucket.bucket
    error_message = "with content S3 bucket name did not match expected"
  }

  assert {
    condition     = aws_s3_object.content.key == "test.txt"
    error_message = "content key did not match expected"
  }
}

結局、最埌は等倀比范しおいるのですが。正芏衚珟あたりは、䜿いたくなるのではずいう気がしたので。

文字列内に${}を埋め蟌む以倖にもいろいろやりたくなるこずはあるず思うので、関数を䜿うこずも考えおおくずよいのかなず。

オマケ

䜿わなかったもの

今回、觊れおいない芁玠ずしおproviderずexpect_failuresがありたす。

providerを䜿うず、メむンずなる構成ファむルで䜿甚しおいるTerraform Providerをオヌバヌラむドできるようです。aliasも考慮される
ようです。

expect_failuresは、メむンずなる構成ファむルでcheckを䜿甚しおいる堎合で、か぀テストでそれが倱敗するこずがわかっおいる堎合は
抑止できる機胜です。

Custom Conditions / Checks with Assertions

実行するテストを絞り蟌む

terraform testは、terraform applyを行っおリ゜ヌスを䜜成するので、時間がかかるリ゜ヌスを䜜成する堎合はテストにも時間がかかりたす。

こういう時にterraform testで䞀括しお実行するず時間がかかるので、-filterオプションで実行するテストを指定するず䟿利かもしれたせん。

$ terraform test -filter=tests/create_s3_buckets.tftest.hcl

ずはいえ、そういう状況ではテストをたくさん䜜らない方がいい気はしたすが 。

デバッグする

各モゞュヌルがどのような動䜜をしおいるか確認したい堎合は、-verboseオプションを付けお実行するずよいでしょう。
※ -filterは実行するテストを絞っおいるだけです

$ terraform test -filter=tests/create_s3_buckets_with_module.tftest.hcl -verbose

するず、こんな感じで䜜成されたリ゜ヌスの結果などを確認できたす。

$ terraform test -filter=tests/create_s3_buckets_with_module.tftest.hcl -verbose
tests/create_s3_buckets_with_module.tftest.hcl... in progress
  run "generate_random"... pass

# random_integer.bucket_prefix:
resource "random_integer" "bucket_prefix" {
    id     = "41773"
    max    = 99999
    min    = 10000
    result = 41773
}


Outputs:

bucket_prefix = "41773"

  run "create_buckets"... pass

# aws_s3_bucket.standalone_bucket:
resource "aws_s3_bucket" "standalone_bucket" {
    arn                         = "arn:aws:s3:::41773-my-bucket"
    bucket                      = "41773-my-bucket"
    bucket_domain_name          = "41773-my-bucket.s3.amazonaws.com"
    bucket_regional_domain_name = "41773-my-bucket.s3.us-east-1.amazonaws.com"
    force_destroy               = false
    hosted_zone_id              = "Z3AQBSTGFYJSTF"
    id                          = "41773-my-bucket"
    object_lock_enabled         = false
    region                      = "us-east-1"
    request_payer               = "BucketOwner"
    tags_all                    = {}

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

    versioning {
        enabled    = false
        mfa_delete = false
    }
}

# aws_s3_bucket.with_content_bucket:
resource "aws_s3_bucket" "with_content_bucket" {
    arn                         = "arn:aws:s3:::41773-my-with-content-bucket"
    bucket                      = "41773-my-with-content-bucket"
    bucket_domain_name          = "41773-my-with-content-bucket.s3.amazonaws.com"
    bucket_regional_domain_name = "41773-my-with-content-bucket.s3.us-east-1.amazonaws.com"
    force_destroy               = false
    hosted_zone_id              = "Z3AQBSTGFYJSTF"
    id                          = "41773-my-with-content-bucket"
    object_lock_enabled         = false
    region                      = "us-east-1"
    request_payer               = "BucketOwner"
    tags_all                    = {}

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

    versioning {
        enabled    = false
        mfa_delete = false
    }
}

# aws_s3_object.content:
resource "aws_s3_object" "content" {
    bucket             = "41773-my-with-content-bucket"
    bucket_key_enabled = false
    content            = "Hello World"
    content_type       = "application/octet-stream"
    etag               = "b10a8db164e0754105b7a99be72e3fe5"
    force_destroy      = false
    id                 = "test.txt"
    key                = "test.txt"
    storage_class      = "STANDARD"
    tags_all           = {}
}


Outputs:

content_key = "test.txt"
standalone_bucket_name = "41773-my-bucket"
with_content_bucket_name = "41773-my-with-content-bucket"

╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":
│    1: provider "aws" {
│
│ See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for implications.
│
│ (and one more similar warning elsewhere)
╵

tests/create_s3_buckets_with_module.tftest.hcl... tearing down
╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":
│    1: provider "aws" {
│
│ See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for implications.
│
│ (and one more similar warning elsewhere)
╵
tests/create_s3_buckets_with_module.tftest.hcl... pass

Success! 2 passed, 0 failed.

こちらの譊告は今たで端折っおいたのですが、LocalStackを䜿っおいる関係で出おいるので今回は無芖で

╷
│ Warning: AWS account ID not found for provider
│
│   with provider["registry.terraform.io/hashicorp/aws"],
│   on main.tf line 1, in provider "aws":
│    1: provider "aws" {
│
│ See https://www.terraform.io/docs/providers/aws/index.html#skip_requesting_account_id for implications.
│
│ (and one more similar warning elsewhere)
╵

こんなずころでしょうか。

おわりに

Terraform 1.6で远加された、Terraform testing frameworkを詊しおみたした。

実際にリ゜ヌスを䜜るのが良い時もあればそうでない時もある気はしたすが、Terratestずは異なりHCLでテストを曞けるのが良いですね。
䜿えるずころでは䜿っおいきたしょう。