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】モジュールをお気軽にテストしたい

0
Posted at

はじめに

Terraformでモジュールを作成後、動作検証するのは重要です。
Terraform v1.6.0から標準で追加されたテストフレームワークを使うと、手軽にテストが実施できます。
本記事では、AWSのセキュリティグループ作成モジュールを例に、terraform testを用いたテストの書き方や実際の実行例をご紹介します。

TL;DR

この記事を読めば、以下のことができるようになります。

  • terraform testを用いたテストの書き方がわかる
  • validation に違反した際のエラー発生テスト(command = plan)ができる
  • 実際にリソースを作成・破棄して出力を検証するテスト(command = apply)ができる

前提条件

  • テスト用のVPCが作成済みであること

実行環境

  • Terraform: v1.15.5

詳細

1. フォルダ構造

モジュールはmodulesフォルダ配下に作成します。モジュール名は、ec2_sgとします。
modules/ec2_sg配下がモジュールの本体で、modules/test/ec2_sg配下がテストコードです。

.
└── modules
    ├── ec2_sg
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    └── test
        └── ec2_sg
            ├── 01_plan.tftest.hcl
            └── 02_apply.tftest.hcl

2-1. ファイル説明(./modules/ec2_sgフォルダ配下)

./modules/ec2_sg/main.tf

今回はセキュリティグループに適用するインバウンドルール(ingress_rules)をリストで複数適用可能にしています。
セキュリティグループに紐づけるVPCは事前に作成しておいたものを使用します。

resource "aws_security_group" "my_sg" {
  name        = var.sg_name
  vpc_id      = var.vpc_id

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = var.sg_name
  }
}

./modules/ec2_sg/variables.tf

./modules/ec2_sg/main.tfの説明でもあったように、インバウンドルールは複数適用可能にしています。
変数ingress_rulesにあるように、オブジェクトのリストという形で代入可能です。

variable "vpc_id" {
  type        = string
  description = "The ID of the VPC where the security group will be created."
}

variable "sg_name" {
  type        = string
  description = "The name of the security group."
}

variable "ingress_rules" {
  type = list(object({
    protocol    = string
    from_port   = number
    to_port     = number
    cidr_blocks = list(string)
  }))
  default     = []
  description = "List of allowed ingress rules."

  # protocolはtcpまたはudpのみ
  validation {
    condition     = alltrue([for rule in var.ingress_rules : contains(["tcp", "udp"], rule.protocol)])
    error_message = "The protocol for each ingress rule must be one of: tcp, udp."
  }
}

ポイントは以下の通りです。

  1. validationで変数の入力に制限を設ける

    • 今回はテスト用に、変数ingress_rules内のprotocoltcpudpのみ設定可能としました
    validation {
      condition     = alltrue([for rule in var.ingress_rules : contains(["tcp", "udp"], rule.protocol)])
      error_message = "The protocol for each ingress rule must be one of: tcp, udp."
    }
    

./modules/ec2_sg/outputs.tf

出力はシンプルに、セキュリティグループIDとインバウンドルールのみとします。

output "security_group_id" {
  description = "The ID of the created security group."
  value       = aws_security_group.my_sg.id
}

output "ingress_rules" {
  description = "All ingress rules for the security group."
  value       = aws_security_group.my_sg.ingress
}

2-2. ファイル説明(./modules/test/ec2_sgフォルダ配下)

./modules/test/ec2_sg/01_plan.tftest.hcl

このファイルでは、terraform planを実行して想定通りのエラーが発生するかをテストしてみます。
今回はテストtest_invalid_variableとして、protocolにエラー値hogetcp または udp 以外の値)を代入してエラーが発生するかを確認します。

run "test_invalid_variable" {
  command = plan

  module {
    source = "../../ec2_sg"
  }

  variables {
    vpc_id = "vpc-XXXXXXXX"    # 事前に作成しておいたVPCのID
    sg_name       = "sg_test_plan_failure"
    ingress_rules = [
      {
        protocol    = "hoge" # validationに違反する値
        from_port   = 443
        to_port     = 443
        cidr_blocks = ["192.0.2.1/32"]
      }
    ]
  }

  # plan時にingress_rules変数のvalidationが失敗することを期待する
  expect_failures = [
    var.ingress_rules,
  ]
}

ポイントは以下の通りです。

  1. expect_failuresによって、ネガティブテスト(想定通りのエラーが発生したか)が実施可能

    • ここでは、変数ingress_rulesによってエラーが発生することをテストする
    expect_failures = [
      var.ingress_rules,
    ]
    

./modules/test/ec2_sg/02_apply.tftest.hcl

このファイルでは、terraform applyを実行して成功するかをテストしてみます。

run "test_sg_creation" {
  command = apply

  module {
    source = "../../ec2_sg"
  }

  variables {
    vpc_id = "vpc-XXXXXXXX"    # 事前に作成しておいたVPCのID
    sg_name      = "sg_test_apply_success"
    ingress_rules = [
      {
        protocol    = "tcp"
        from_port   = 8443
        to_port     = 8444
        cidr_blocks = ["192.0.2.1/32", "192.0.2.2/32"]
      },
      {
        protocol    = "udp"
        from_port   = 8443
        to_port     = 8444
        cidr_blocks = ["192.0.2.1/32", "192.0.2.2/32"]
      }
    ]
  }

  # セキュリティグループIDが空でないことを確認
  assert {
    condition     = output.security_group_id != ""
    error_message = "Security group ID should not be empty."
  }

  # インバウンドルールの数が0より大きいことを確認
  assert {
    condition     = length(output.ingress_rules) > 0
    error_message = "The number of ingress rules should be greater than 0."
  }
}

3. 実行結果

テストコードが配置されている./modules/test/ec2_sgフォルダに移動して Terraform を実行します(terraform testを実行すると、同フォルダ内の*.tftest.hclファイルを自動検出してくれます)。

  • コマンド

    $ terraform init
    $ terraform test
    

terraform testの実行結果は以下のようになります。02_apply.tftest.hclに関しては、terraform applyの後に自動で環境の削除まで実行してくれます。

  • 実行結果
$ terraform test
01_plan.tftest.hcl... in progress
  run "test_invalid_variable"... pass
01_plan.tftest.hcl... tearing down
01_plan.tftest.hcl... pass
02_apply.tftest.hcl... in progress
  run "test_sg_creation"... pass
02_apply.tftest.hcl... tearing down
02_apply.tftest.hcl... pass

さいごに

普段書き慣れているHCLだけでサクッとテストが書けました。特に command = apply のテストでは、検証後に自動でテスト用リソースの破棄をしてくれるのでとても便利です。
モジュール開発時の品質担保に役立ちますので、ぜひ試してみてください。

参考URL

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?