• トップ
  • ブログ一覧
  • terraform のテストをHCLで書く方法
  • terraform のテストをHCLで書く方法

    やのけん(エンジニア)やのけん(エンジニア)
    2024.01.15

    IT技術

    はじめに

    今までは、terraformでテストを実行するときは、Terratestなどの外部ツールを利用する必要がありました。

    2023年10月4日にリリースされた、terraform 1.6.0の機能のterraform test コマンドを利用することで、インフラストラクチャの更新や作成のテストを、Terraform単体で完結できるようになりました。

    本記事はterraform test コマンドの紹介記事になります。

    リリースノート
    Releases · hashicorp/terraform 160-october-4-2023

    terraform testのメリット

    Terraform 1.6.0以前では、テストコードの作成にTerratestなどの外部ツールを使用する必要があり、HCL以外のプログラミング言語、例えばGo言語の学習が必須でした。

    テストコードとインフラストラクチャの更新・作成の記述がHCLに統一されることで、学習コストが削減され、実際のプロジェクトへの導入のハードルが下がります。

    テスト実行方法

    実行方法としてはシンプルで、拡張子が tftest.hclもしくは、tftest.jsonのファイルにテストコードを記述し、terraform test  コマンドを実行するのみです。

    terraform testの動作

    デフォルトのテストの動作です。

    1. terraformがテストファイルを検知
    2. テスト用のインフラストラクチャを作成
    3. 作成されたインフラストラクチャに対してアサーションと検証を実行
    4. テスト用に作成されたインフラストラクチャを削除

    テスト実行時にインフラを実際に作成したくない場合は、commandオプションにplanを指定することで、実際にインフラを作成せず。planに対してのチェックを実施することができます。

    テストコードの記述方法

    tftestファイルの基本的な記述方法です。

    • 一つのrunブロックに対して、複数のassertを記述することができます。
    • assertブロックでは、conditionに条件を記載し、error_messageにテスト失敗時のメッセージを設定します。
    • conditionでは、比較演算子を使用して検証を行います。また、for式の利用やlengthなどの関数の記述も可能です。
    1run "テスト名" {
    2
    3    command = plan // 実行する動作(デフォルト:apply)
    4
    5    assert {
    6        condition = リソースの設定値 == "あるべき値"
    7        error_message = テスト失敗時のメッセージ
    8    }
    9
    10      // assert は、1つのrunブロックに対して複数記述できます。
    11    assert {
    12        condition = リソースの設定値 != "あるべきでない値"
    13        error_message = "エラーメッセージ"
    14    }
    15}

    実際にテストコードを書いてみる

    今回はGCPへのリソースの作成に対して、テストコードを記述していきます。

    以降はterraform v1.6.5で実施しています

    1% terraform -v
    2Terraform v1.6.5
    3on darwin_arm64

    Cloud Storageのオブジェクト名のチェック

    tfファイルでCloud Storageのバケットにindex.htmlを追加します。

    以下のtfファイルを作成し、同じディレクトリ内に空のindex.htmlファイルを作成します。

    cloud_storage.tf

    1provider "google" {
    2  project = "project"
    3}
    4
    5resource "google_storage_bucket_object" "test-object" {
    6  name   = "index.html"
    7  bucket = "my-bucket"
    8  source = "index.html"
    9  content_type = "text/html"
    10}

    テストコードを、tftest.hclファイルに記述します。

    cloud_storage.tftest.hcl

    1run "valid_gcs_object_name" {
    2    command = plan
    3
    4    assert {
    5        condition = google_storage_bucket_object.test-object.name == "index.html"
    6        error_message = "Object name is not index.html"
    7    }
    8}

    コンソールでterraform test を実行

    成功

    1% terraform test
    2cloud_storage.tftest.hcl... in progress
    3  run "valid_gcs_object_name"... pass
    4cloud_storage.tftest.hcl... tearing down
    5cloud_storage.tftest.hcl... pass
    6
    7Success! 1 passed, 0 failed.

    次は失敗させてみます。

    cloud_storage.tfのオブジェクト名をtest.htmlに変更

    1resource "google_storage_bucket_object" "test-object" { 
    2     name = "test.html" 
    3     bucket = "my-bucket" 
    4     source = "index.html" 
    5     content_type = "text/html" 
    6}

    コンソールでterraform test を実行

    テストが失敗します。失敗したときは、テストコードassertブロックに設定しているerror_messageが表示されます。

    Object name is not index.html 

    1% terraform test
    2cloud_storage.tftest.hcl... in progress
    3  run "valid_gcs_object_name"... fail
    45Error: Test assertion failed
    67│   on cloud_storage.tftest.hcl line 5, in run "valid_gcs_object_name":
    85:         condition = google_storage_bucket_object.test-object.name == "index.html"
    9│     ├────────────────
    10│     │ google_storage_bucket_object.test-object.name is "test.html"
    1112Object name is not index.html
    1314cloud_storage.tftest.hcl... tearing down
    15cloud_storage.tftest.hcl... fail
    16
    17Failure! 0 passed, 1 failed.

    セキュリティポリシーの妥当性をチェック

    続いて、アクセスを許可するIPアドレスをを間違えて "*" が設定してしまった際に、テスト時点で検知できるようにしていきます。

    src_ip_rangesに"*"を間違えて追加してください。

    cloud_armor.tf

    1resource "google_compute_security_policy" "policy" {
    2  name = "test-policy"
    3
    4  rule {
    5    action = "allow"
    6    priority = 2147483647
    7
    8    match {
    9      versioned_expr = "SRC_IPS_V1"
    10
    11      config {
    12               src_ip_ranges = ["*", "192.0.2.0/24", "198.51.100.0/24"] // *を追加してしまった。
    13      }
    14    }
    15
    16    description = "Allow access to IPs"
    17  }
    18}

    src_ip_rangesに "*"が含まれていたら、テストを失敗させます。

    cloud_armor.tftest.hcl

    1run "アクセス制限に*が含まれていないこと" {
    2    command = plan
    3
    4    assert {
    5        condition = [for allow_ip in tolist(google_compute_security_policy.policy.rule.*.match[0].*.config[0].*.src_ip_ranges)[0] : allow_ip if allow_ip == "*"][0] != "*"
    6        error_message = "アクセス制限に*が含まれています"
    7    }
    8}

    terraform test  実行

    1cloud_armor.tftest.hcl... in progress
    2  run "アクセス制限に*が含まれていないこと"... fail
    34Error: Test assertion failed
    56│   on cloud_armor.tftest.hcl line 5, in run "アクセス制限に*が含まれていないこと":
    75:         condition = [for allow_ip in google_compute_security_policy.policy.rule.*.match[0].*.config[0].*.src_ip_ranges[0] : allow_ip if allow_ip == "*"][0] != "*"
    8│     ├────────────────
    9│     │ google_compute_security_policy.policy.rule is set of object with 1 element
    1011│ アクセス制限に*が含まれています
    1213main.tftest.hcl... tearing down
    14main.tftest.hcl... fail
    15
    16Failure! 0 passed, 1 failed.

    ちゃんと失敗していますね。src_ip_rangesから、"*"を削除するとテストが通ることも確認してみてください。

    conditionの説明

    1[for allow_ip in google_compute_security_policy.policy.rule.*.match[0].*.config[0].*.src_ip_ranges[0] : allow_ip if allow_ip == "*"][0] != "*"

    [for <ITEM> in <LIST> : <OUTPUT> <条件式>] の形で、<LIST>の中から条件式に該当する値を<OUTPUT>として取得することができます。

    <OUTPUT>は配列として出力されるので、最後にインデックス[0]を指定して、src_ip_ranges内の"*"を文字列として取得しています。

    for文とテストコードを組み合わせることで、src_ip_rangesにIPが複数設定されていても全て検証するようにしています。

    terraformのネストが分かりづらいですね。。もっとシンプルに書きたいところです。

    最後に

    いかがだったでしょうか?

    terraform のテストコードがtfファイルと同じように書けるのはとても便利ですね。

    インフラストラクチャの更新の際に、ここだけは変えないでほしい箇所をテストコードに記述しておくとよさそうです。

    次はCI/CDツールとの連携も書きたいですね

    現在(2023年12月20日時点)beta版の1.7.0では、tfファイル内のoutputをテストコード内で取得したり、プロバイダーやモジュールをモック化する機能が追加されていました。

    正式リリースが楽しみですね。更新があれば、また記事にしていきたいと思います。

    Terraformリリースノート
    Releases · hashicorp/terraform

    terraform test ドキュメント

    Tests - Configuration Language | Terraform | HashiCorp Developer

    やのけん(エンジニア)

    やのけん(エンジニア)

    おすすめ記事