はじめに
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."
}
}
ポイントは以下の通りです。
-
validationで変数の入力に制限を設ける
- 今回はテスト用に、変数
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." } - 今回はテスト用に、変数
./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にエラー値hoge(tcp または 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,
]
}
ポイントは以下の通りです。
-
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
-
HashiCorp Developer > Tests
- Terraform公式のテスト機能(terraform test)ドキュメント
-
HashiCorp Developer > variable block reference
- Terraformにおける変数の設定方法