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 は plan モードなら $0 で書ける話

0
Posted at

はじめに

IaC にもテストを入れたいと思いつつ、「テスト=実際にリソースを作って確認」だと思っていて、課金が怖くてずっと後回しにしていました。

でも Terraform 標準の terraform test は、command = plan を指定すれば実リソースを作らずに設定を検証できます。実リソースを作らないので、課金リスクをかなり抑えてテストが書けました。

先に結論:terraform test は既定だと apply(実際に作る)ですが、各テストに command = plan を書けばプランの計算だけで評価され、リソースは作られません。

「ほぼ $0」と書いているのは、plan でも provider の認証や data source 参照で AWS の API 呼び出しは発生するためです。通常は大きな課金要因ではありませんが、「絶対に 0 円」と言い切らずにおきます。

環境

  • Terraform(ネイティブの terraform test / .tftest.hcl
  • 対象は AWS 向けの構成(Lambda・S3・API Gateway・CloudFront など)
  • リージョン: ap-northeast-1(東京)

きっかけ:直したものが、また戻らないように守りたい

直前に「Lambda のランタイムが古くなる」対応をして、現行の nodejs22.x に上げたばかりでした。こういう修正は、あとで誰か(未来の自分含む)がうっかり戻すと困ります。

そこで「この構成ではランタイムに nodejs22.x を使い続ける」をテストで固定することにしました。これが最初の1本です。

# tests/serverless.tftest.hcl
run "lambda_uses_supported_runtime" {
  command = plan                      # ← 実リソースを作らない

  module {
    source = "./modules/lambda"       # ← モジュール単体を検証
  }
  variables {
    project       = "aws-study"
    function_name = "hello"
  }

  assert {
    condition     = aws_lambda_function.this.runtime == "nodejs22.x"
    error_message = "Lambda runtime は、この構成の標準である nodejs22.x を使う"
  }
}

terraform test で実行すると、これが通ります。何も作らずに「ランタイムは想定どおりか?」だけを確認できました。

どのランタイムが現行/非推奨かは時期で変わります。実際に固定する版は、公開時点の AWS Lambda の対応ランタイム表(参考リンク)で確認してください。

なお、module { source = "./modules/lambda" } の相対パスは、テストを実行する構成ディレクトリ(terraform test を叩く場所)を基準に解決されます。私はそこを基準に確認しました。

plan と apply の違い(ここが安く済む肝)

公式ドキュメントによると、run ブロックの command は**省略すると既定は apply**で、実際にリソースを作って検証します。command = plan を指定すると、プランの計算結果に対してアサートするので、リソースは作られません。

command = apply (既定)… 実際に作る → 課金・後片付けが要る
command = plan        … 作らない   → ほぼ $0

plan テストで向いていること / apply が要ること

全部 plan で済むわけではないので、最初に切り分けておくと迷いません。

plan テストで向いていること

  • 変数の validation が効くこと
  • IAM ポリシーや公開設定など、設定値の確認
  • ランタイム・タグ・命名規則の固定
  • モジュールの入力と出力の確認

apply テストが必要になりやすいこと

  • 実際の AWS サービス連携
  • Lambda の実行結果
  • API Gateway 経由の疎通
  • CloudFront 配信後の挙動

「設定が意図どおりか」を確かめたいだけなら plan、「実際に動くか」を見たいなら apply、という住み分けです。

いろいろな検証を plan で書く

モジュール内部を検証する(module {}

「S3 は公開を全ブロックしているか」。

run "s3_blocks_all_public_access" {
  command = plan
  module { source = "./modules/static_site" }
  variables {
    project = "aws-study"
    suffix  = "test"
  }
  assert {
    condition = (
      aws_s3_bucket_public_access_block.this.block_public_acls &&
      aws_s3_bucket_public_access_block.this.block_public_policy &&
      aws_s3_bucket_public_access_block.this.ignore_public_acls &&
      aws_s3_bucket_public_access_block.this.restrict_public_buckets
    )
    error_message = "S3 バケットは公開を全ブロックすべき"
  }
}

「不正な入力はちゃんと弾かれる」を検証する(expect_failures

正しく動くことだけでなく、「ダメな入力をちゃんと拒否する」のもテストできます。SSH 元 IP に全開放(0.0.0.0/0)を渡したら、variable の validation で弾かれてほしい。

run "rejects_wide_open_my_ip" {
  command = plan
  variables {
    my_ip = "0.0.0.0/0"
  }
  expect_failures = [
    var.my_ip,         # ← この変数の検証が失敗することを“期待”する
  ]
}

expect_failures は「ここは失敗するのが正しい」という宣言です。逆に、正しい値(203.0.113.10/32)なら通ることも、別の run で確認しています。

こうして、ランタイム・S3 公開ブロック・API Gateway の構成・CloudFront の既定ルート・入力バリデーションなどを plan のまま 8 本書いて、全部 pass しました。

ハマったところ:依存を足したら init し直し

途中で、テストに新しい module { source = "..." } を追加したら、「初期化されていない」と怒られました。テストが参照するモジュールやプロバイダは、実行前に取得されている必要があります。新しいモジュールや provider の依存を追加・変更したら、terraform init を再実行して依存を取得・更新してから terraform test を回す、で解決しました。

学び

  • terraform test は既定が apply。command = plan を書けば実リソースを作らず、ほぼ $0
  • 「設定値が意図どおりか」は plan、「実際に動くか」は apply、と住み分ける。
  • module {} でモジュール単体を、assert で値を、expect_failures で「不正入力を弾くこと」を検証できる。
  • 依存(モジュール/provider)を足したら terraform init を忘れずに
  • 「戻したくない修正」は、テストで固定しておくと安心。

おわりに

「テスト=お金がかかる」という思い込みで避けていたのが、plan モードを知って一気に書けるようになりました😊 まずは1本、「この値はこうあってほしい」を plan で固定するところから始めると、とっつきやすいと思います🙌

参考

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?