はじめに
Terraformでインフラを管理していると、「Webサーバー用」「アプリケーション用」「データベース用」といった複数の類似リソースを作成する場面が頻繁に発生します。 従来の方法では、リソースごとに個別にコードを書く必要がありましたが、for_eachを使うことで効率的かつ保守性の高いコードが実現できます。
本記事では、Security GroupとEC2インスタンスを例に、実際の運用で役立つfor_eachのテクニックを解説します。
基本的なfor_eachの使い方
for_eachを使うことで、mapやsetから複数のリソースを一度に作成できます。 以下は複数のSecurity Groupを作成する例です。
resource "aws_security_group" "main" {
for_each = var.security_groups
name = each.key
description = each.value.description
vpc_id = each.value.vpc_id
tags = {
Name = each.key
}
}
設定ファイル(terraform.tfvars)では以下のように定義します。
security_groups = {
"web-sg" = {
description = "Security group for web servers"
vpc_id = "vpc-12345678"
}
"app-sg" = {
description = "Security group for application servers"
vpc_id = "vpc-12345678"
}
}
この方法により、新しいSecurity Groupが必要になった場合も、設定ファイルにエントリを追加するだけで対応できます。
EC2インスタンスとSecurity Groupの関連付け
作成したSecurity Groupを、同じくfor_eachで作成するEC2インスタンスに関連付けることができます。
resource "aws_instance" "main" {
for_each = var.ec2_instances
ami = each.value.ami
instance_type = each.value.instance_type
vpc_security_group_ids = [
for sg_key in each.value.vpc_security_group_ids :
aws_security_group.main[sg_key].id
]
tags = {
Name = each.key
}
}
重要なポイントは、aws_security_group.main[sg_key].idの部分です。これにより、for_eachで作成したSecurity GroupのIDを動的に参照できます。
実践的なコツ
基本的なfor_eachの使い方を理解したところで、実際の運用で役立つより高度なテクニックを見ていきましょう。
countとfor_eachの使い分け
Terraform公式ドキュメント では以下のように説明されています:
"If your instances are almost identical, count is appropriate. If some of their arguments need distinct values that can't be directly derived from an integer, it's safer to use for_each."
実践的な判断基準:
- count: 同じ設定のリソースを複数作成する場合(例:同一設定のEC2を3台)
- for_each: 各リソースが異なる設定値を持つ場合(例:Web用、App用、DB用のSecurity Group)
for_eachを使うことで、リストの順序変更時にリソースが再作成されるリスクも回避できます。
条件付きリソース作成
実際の運用では、環境(開発・本番)によって異なるリソースを作成したい場合があります。 例えば、「本番環境でのみSSHアクセスを制限する」「開発環境では全ポートを開放する」といったケースです。 環境別セキュリティルールの適用
resource "aws_security_group_rule" "production_only" {
for_each = {
for key, rule in var.security_group_rules : key => rule
if rule.environment == "production"
}
type = each.value.type
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
security_group_id = aws_security_group.main[each.value.security_group_id].id
cidr_blocks = each.value.cidr_blocks
}
この方法により、同一のコードベースで環境ごとに適切なセキュリティ設定を適用できます。
リソース間の依存関係管理
複数のモジュールでリソースを管理する場合、作成したリソースを他のモジュールから参照する必要があります。 例えば、ネットワークモジュールで作成したSecurity GroupをEC2モジュールで使用するケースです。
outputを活用したモジュール間連携
# ネットワークモジュールのoutput
output "security_group_ids" {
description = "Map of security group names to IDs"
value = {
for key, sg in aws_security_group.main : key => sg.id
}
}
# EC2モジュールでの参照
resource "aws_instance" "main" {
for_each = var.ec2_instances
vpc_security_group_ids = [
for sg_name in each.value.security_groups :
var.security_group_ids[sg_name]
]
# ... その他の設定
}
では、モジュール間でのリソース参照にはoutputを使用することを推奨しています。 これにより、依存関係が明確になり、変更時の影響範囲を把握しやすくなります。
まとめ
for_eachを活用することで、以下のような保守性の高いTerraformコードが実現できます。
- 意味のあるキー: リソース名で直接識別でき、デバッグや運用が容易
- 柔軟な条件分岐: 環境や要件に応じたリソース作成が可能
- 明確な依存関係: outputを通じたモジュール間の連携で、変更影響を最小化
新しいリソースが必要になった場合も、設定ファイルへの追加だけで対応でき、コードの重複を避けながらスケーラブルなインフラ管理が可能になります。 ぜひ実際のプロジェクトで活用してみてください。