🧪

terraform test: 応用機能

2023/12/17に公開

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

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

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

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

はじめに

前回の記事では、 terraform test の基本的な機能の紹介をしました。
前回の記事の内容でも十分に terraform module のテストを書くことができると思います。

しかし、今回紹介する応用的な機能を使うとより複雑な module のテストを実行することができるようになります。
この記事では、やりたいことをベースに機能を紹介していきます。

terraform test 機能説明: 応用編

テスト前に依存するリソースを作成したい

terraform test に module ブロックを使用することで、テスト対象のリソースを作成する前に別のリソースを作成することができます。

テスト対象となる module を実行するために事前にリソースが必要となるケースがあります。
例えば、EKS cluster を作成する module をテストする前に VPC を作成するなどです。

terraform test には module ブロックを設定することで対応できます。
module ブロックは run ブロック内に module を設定する形で定義します。

ここでは公式ドキュメントのサンプルコードをもとに説明します。

下記のコードでは、ローカルファイルを S3 Bucket にアップロードする terraform コードとなります。
下記の terraform コードを実行するためには、事前に S3 Bucket が必要となります。

# main.tf

variable "bucket" {
  type = string
}

variable "files" {
  type = map(string)
}

data "aws_s3_bucket" "bucket" {
  bucket = var.bucket
}

resource "aws_s3_object" "object" {
  for_each = var.files

  bucket = data.aws_s3_bucket.bucket.id
  key = each.key
  source = each.value

  etag = filemd5(each.value)
}

上記の terraform コードを実行するために必要となる terraform コードを用意します。
下記のように S3 Bucket を作成します。

# testing/setup/main.tf

variable "bucket" {
  type = string
}

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

tftest ファイルには、事前に必要となる S3 Bucket を作成するコードを実行する run ブロックを最初に定義します。(run setup)
最初の run ブロックには module ブロックを定義し、事前に必要となる S3 Bucket を作成する terraform コードのパスを指定します。

次の run ブロックでテスト対象となる terraform コードの実行とテストの定義を記載します。(run execute)

# file_count.tftest.hcl

variables {
  bucket = "saikyo-tftest-bucket"
  files = {
    "file-one.txt": "data/files/file_one.txt"
    "file-two.txt": "data/files/file_two.txt"
  }
}

provider "aws" {
  region = "us-east-1"
}

run "setup" {
  # Create the S3 bucket we will use later.

  module {
    source = "./testing/setup"
  }
}

run "execute" {
  assert {
    condition = length(aws_s3_objects.objects) == 2
    error_message = "wrong number of files"
  }
}

テスト対象のリソースを作成したあと、関連リソースを作成する動作確認をしたい

このようなことをしたい場合も、「テスト前に依存するリソースを作成したい」と同様に module ブロックを使用することで実現できます。

例えば下記のようなケースです。

  1. S3 で静的ウェブサイトを作成する module で、リソース作成後に http でアクセスできるか確認したい
  2. EKS クラスターを作成する module で、リソース作成後に kubectl でアクセスできるか確認したい

下記は「テスト前に依存するリソースを作成したい」のサンプルコードの続きで説明します。
ここでは S3 Object としてアップロードしたファイルを data リソースにより取得し、Object 数が意図した通りかをテストしています。

# testing/loader/main.tf

variable "bucket" {
  type = string
}

data "aws_s3_objects" "objects" {
  bucket = var.bucket
}

「テスト前に依存するリソースを作成したい」と同様に run ブロック内に module ブロックを定義して事後確認用のコードを呼び出します。

# file_count.tftest.hcl

variables {
  bucket = "saikyo-tftest-bucket"
  files = {
    "file-one.txt": "data/files/file_one.txt"
    "file-two.txt": "data/files/file_two.txt"
  }
}

provider "aws" {
  region = "us-east-1"
}

run "setup" {
  # Create the S3 bucket we will use later.

  module {
    source = "./testing/setup"
  }
}

run "execute" {
  assert {
    condition = length(aws_s3_object.object) == 2
    error_message = "wrong number of files"
  }
}

run "verify" {
  # Load and count the objects created in the "execute" run block.

  module {
    source = "./testing/loader"
  }

  assert {
    condition = length(data.aws_s3_objects.objects.keys) == 2
    error_message = "created the wrong number of s3 objects"
  }
}

異常系のテストをしたい

terraform test では expect_failures ブロックにより、plan / apply を失敗することをテストすることができます。

expect_failuresCustom Conditions と連携して使用します。

下記は Custom Conditions により variable が受け取れる値の条件を設定し、条件に合わない値を入力した場合の失敗のテストをするコードの例です。

# main.tf

variable "input" {
  type = number

  validation {
    condition = var.input % 2 == 0
    error_message = "must be even number"
  }
}

異常系のテストを実行するには run ブロック内に expect_failures を定義します。

# input_validation.tftest.hcl

variables {
  input = 0
}

run "zero" {
  # The variable defined above is even, so we expect the validation to pass.

  command = plan
}

run "one" {
  # This time we set the variable is odd, so we expect the validation to fail.

  command = plan

  variables {
    input = 1
  }

  expect_failures = [
    var.input,
  ]
}

上記は簡単な例ですが、 Custom Conditions は複雑なことができます。

例えば下記のような事ができます。

  • check ブロックにより、作成したリソースに対して http アクセスを行い、status code を確認する。
  • state 外の IAM Role を data ブロックで取得し、postcondition ブロックを使用することで、取得した IAM Role の信頼関係に指定されているサービスが任意のものかを確認する。

応用編はここまでとなります。
次は terraform test の挙動を紹介します!

Discussion