0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Terraform】terraform testでモジュールのテストを書く(モックプロバイダー対応)

0
Posted at

Terraformのモジュールを修正するとき、変更が他の環境や呼び出し元に影響しないかを、毎回planの結果を目で追って確かめていないでしょうか。アプリケーションのコードにはテストを書くのに、インフラのコードはテストなしで変更している、という現場は少なくありません。

この記事では、Terraform 1.6で追加された terraform test を使い、モジュールのテストを書く方法を紹介します。Terraform 1.7以降のモックプロバイダーを使えば、AWSにリソースを作らずにテストできます。

terraform testでできること

  • planの結果を検証する:変数を渡してplanし、リソースの属性が期待どおりかを確かめる
  • 入力のチェックを検証する:不正な値を渡したときに、variableのvalidationで正しくエラーになるかを確かめる
  • applyして検証する:実際にリソースを作り、出力値などを確かめてから自動で削除する
  • モックで検証する:プロバイダーをモックに差し替え、AWSにアクセスせずにテストする

テストは .tftest.hcl という拡張子のファイルに書き、terraform test コマンドで実行します。

テスト対象のモジュール

例として、命名規則とタグを強制するS3バケットのモジュールを用意します。

# modules/s3_bucket/main.tf
variable "project" {
  type = string
}

variable "env" {
  type = string

  validation {
    condition     = contains(["dev", "stg", "prod"], var.env)
    error_message = "env は dev / stg / prod のいずれかを指定してください。"
  }
}

variable "purpose" {
  type = string
}

locals {
  bucket_name = "${var.project}-${var.env}-${var.purpose}"
}

resource "aws_s3_bucket" "this" {
  bucket = local.bucket_name

  tags = {
    Project = var.project
    Env     = var.env
  }
}

resource "aws_s3_bucket_versioning" "this" {
  bucket = aws_s3_bucket.this.id

  versioning_configuration {
    status = var.env == "prod" ? "Enabled" : "Suspended"
  }
}

output "bucket_name" {
  value = aws_s3_bucket.this.bucket
}

このモジュールには、守りたいルールが3つあります。

  • バケット名は「プロジェクト名-環境名-用途」の形式にする
  • 環境名はdev・stg・prodのいずれかに限る
  • 本番環境(prod)だけバージョニングを有効にする

テスト1:planの結果を検証する

モジュールのディレクトリに tests/ を作り、テストファイルを置きます。

# modules/s3_bucket/tests/s3_bucket.tftest.hcl
variables {
  project = "myapp"
  purpose = "assets"
}

run "dev_bucket_name" {
  command = plan

  variables {
    env = "dev"
  }

  assert {
    condition     = aws_s3_bucket.this.bucket == "myapp-dev-assets"
    error_message = "バケット名が命名規則に従っていません。"
  }

  assert {
    condition     = aws_s3_bucket_versioning.this.versioning_configuration[0].status == "Suspended"
    error_message = "dev環境でバージョニングが有効になっています。"
  }
}

run "prod_versioning_enabled" {
  command = plan

  variables {
    env = "prod"
  }

  assert {
    condition     = aws_s3_bucket_versioning.this.versioning_configuration[0].status == "Enabled"
    error_message = "prod環境でバージョニングが有効になっていません。"
  }
}
  • variables:ファイルの先頭に書いた値は、すべてのrunブロックで共通に使われる。runブロックの中のvariablesで個別に上書きできる
  • run:テストの単位。上から順に実行される
  • command = plan:applyせず、planの結果だけで検証する。AWSにリソースは作られない
  • assert:conditionがfalseになったら、error_messageを表示してテストを失敗にする

テスト2:不正な入力がエラーになることを検証する

validationが正しく働くかも、テストで確かめられます。

run "invalid_env_is_rejected" {
  command = plan

  variables {
    env = "production"
  }

  expect_failures = [
    var.env,
  ]
}

expect_failuresに指定した変数のvalidationでエラーになれば、テストは成功です。逆に、エラーにならなければテストは失敗します。「誤った値をきちんと弾けるか」は、レビューでは見落としやすい部分です。

テスト3:モックプロバイダーで検証する

command = planでは、AWSが決める値(ARNなど)は「apply後に決まる値」になり、検証できません。一方、applyで検証すると実際にリソースが作られ、時間も料金もかかります。

Terraform 1.7以降では、モックプロバイダーを使うと、AWSにアクセスせずにapplyまでのテストができます。

# modules/s3_bucket/tests/s3_bucket_mock.tftest.hcl
mock_provider "aws" {}

variables {
  project = "myapp"
  env     = "stg"
  purpose = "logs"
}

run "output_bucket_name" {
  assert {
    condition     = output.bucket_name == "myapp-stg-logs"
    error_message = "出力のバケット名が正しくありません。"
  }
}
  • mock_provider "aws" {}:AWSプロバイダーを、何もしないモックに差し替える。認証情報も不要になる
  • commandの省略:省略するとapplyになるが、モックなのでAWSには何も作られない
  • output:モジュールの出力値も検証できる

モックでは、ARNやIDのようにAWSが決める値は、ランダムな文字列で埋められます。特定の値が必要なら、mock_resourceブロックで既定値を指定できます。

テストを実行する

モジュールのディレクトリで、次のコマンドを実行します。

cd modules/s3_bucket
terraform init
terraform test

すべてのテストが通ると、次のような結果が表示されます(表示は例です)。

tests/s3_bucket.tftest.hcl... in progress
  run "dev_bucket_name"... pass
  run "prod_versioning_enabled"... pass
  run "invalid_env_is_rejected"... pass
tests/s3_bucket.tftest.hcl... tearing down
tests/s3_bucket.tftest.hcl... pass
tests/s3_bucket_mock.tftest.hcl... in progress
  run "output_bucket_name"... pass
tests/s3_bucket_mock.tftest.hcl... tearing down
tests/s3_bucket_mock.tftest.hcl... pass

Success! 4 passed, 0 failed.

plan用のテストファイルにはmock_providerを書いていないため、実際のAWSプロバイダーが初期化されます。実行には、AWSの認証情報と、環境変数AWS_REGIONなどでのリージョンの指定が必要です(リソースは作られません)。CIで認証情報を使いたくない場合は、すべてのテストファイルにmock_providerを書く方法もあります。

どこまでテストを書くか

すべてのリソースの全属性をテストする必要はありません。次のような「壊れると困るルール」に絞ると、少ない手間で効果が出ます。

  • 命名規則:リソース名やタグの付け方
  • 環境ごとの分岐:本番だけ有効にする設定(バージョニング、削除保護、Multi-AZなど)
  • 入力のチェック:validationで弾くべき値
  • セキュリティ上の前提:暗号化が有効か、パブリックアクセスがブロックされているか

まとめ

  • terraform testで、モジュールのplan結果や出力値を自動で検証できる
  • expect_failuresで、不正な入力がvalidationで弾かれることを確かめられる
  • モックプロバイダーを使えば、AWSにリソースを作らずにapplyまでテストできる
  • テストは「壊れると困るルール」に絞って書く

テストがあれば、モジュールを修正したときに「どこかの環境を壊していないか」をplanの目視ではなく、コマンド1つで確かめられるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?