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

    はじめに

    今までは、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

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    やのけん(エンジニア)
    やのけん(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background