CloudFormationとTerraformを組み合わせるケーススタディ
はじめに
これまで、CloudFormationとTerraformをそれぞれ単体で学んできました。どちらのツールも強力ですが、実際のプロジェクトでは、それぞれの強みを活かして併用するケースが少なくありません。今回は、CloudFormationとTerraformを組み合わせる具体的なケーススタディを解説します。
なぜ両者を組み合わせるのか?
CloudFormationとTerraformは、それぞれ異なる得意分野を持っています。
ツール | 得意なこと |
---|---|
CloudFormation | AWS公式サービスとの深い統合、イベント駆動型デプロイ、IAMリソースの管理、自動ロールバック |
Terraform | マルチクラウド対応、複雑なモジュール化、ローカルでの実行計画確認、状態管理 |
これらの強みを活かすことで、単一のツールでは難しい、柔軟で効率的なIaCを実現できます。
ケーススタディ:ハイブリッドIaCモデル
このモデルでは、インフラを**「基盤レイヤー」と「アプリケーションレイヤー」**に分割し、それぞれに最適なツールを割り当てます。
1. 基盤レイヤー(Terraformで管理)
プロジェクト全体で共有される、変更頻度の低い共通インフラをTerraformで管理します。
対象リソース:
- VPC、サブネット、インターネットゲートウェイなどのネットワークリソース
- 共通のIAMロール、ポリシー
- S3バケット、RDSサブネットグループなど
- セキュリティグループの基本設定
選択理由:
- 再利用性の向上: 共通のVPC構成などをモジュール化し、複数のプロジェクトで再利用できます
- 柔軟な管理: 必要に応じて、異なるクラウド(例:VPC Peering経由でGCPと連携)を管理する可能性があります
- 状態管理: Terraformの強力な状態管理機能により、インフラの変更を安全に管理できます
Terraformコード例:基盤リソースのOutputs
# outputs.tf (基盤レイヤー)
output "main_vpc_id" {
description = "The ID of the main VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "List of public subnet IDs"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "List of private subnet IDs"
value = aws_subnet.private[*].id
}
output "security_group_web_id" {
description = "Security group ID for web servers"
value = aws_security_group.web.id
}
2. アプリケーションレイヤー(CloudFormationで管理)
アプリケーション固有の、変更頻度が高いリソースをCloudFormationで管理します。
対象リソース:
- EC2インスタンス、Auto Scaling Group
- アプリケーションロードバランサー(ALB)
- アプリケーション固有のIAMロール
- Lambda関数やECSサービス
選択理由:
- AWSサービスとの親和性: アプリケーションリソースは、ECSやLambdaといったAWSの他のサービスと密接に連携することが多いため、公式のCloudFormationが最も信頼性が高く、連携がスムーズです
- 自動ロールバック: デプロイ時にエラーが発生しても、CloudFormationが自動でロールバックしてくれるため、アプリケーションのデプロイが安全に行えます
- イベント統合: CloudFormationのイベントをSNS/SQSと連携させ、デプロイ通知を自動化できます
CloudFormationコード例:アプリケーションリソース
# application.yaml (アプリケーションレイヤー)
AWSTemplateFormatVersion: "2010-09-09"
Description: "Application layer resources"
Parameters:
VpcId:
Type: String
Description: The ID of the existing VPC from Terraform
PublicSubnetIds:
Type: CommaDelimitedList
Description: List of public subnet IDs from Terraform
WebSecurityGroupId:
Type: String
Description: Security group ID for web servers from Terraform
Resources:
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: MyAppALB
Scheme: internet-facing
Type: application
Subnets: !Ref PublicSubnetIds
SecurityGroups:
- !Ref WebSecurityGroupId
WebServerLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: WebServerTemplate
LaunchTemplateData:
ImageId: ami-0abcdef1234567890 # Amazon Linux 2023
InstanceType: t3.micro
SecurityGroupIds:
- !Ref WebSecurityGroupId
IamInstanceProfile:
Arn: !GetAtt WebServerInstanceProfile.Arn
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: WebServerASG
LaunchTemplate:
LaunchTemplateId: !Ref WebServerLaunchTemplate
Version: !GetAtt WebServerLaunchTemplate.LatestVersionNumber
MinSize: 1
MaxSize: 3
DesiredCapacity: 2
VPCZoneIdentifier: !Ref PublicSubnetIds
TargetGroupARNs:
- !Ref ALBTargetGroup
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: WebServerTargetGroup
Port: 80
Protocol: HTTP
VpcId: !Ref VpcId
HealthCheckPath: /health
WebServerRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
WebServerInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref WebServerRole
Outputs:
LoadBalancerDNS:
Description: DNS name of the load balancer
Value: !GetAtt ApplicationLoadBalancer.DNSName
Export:
Name: !Sub "${AWS::StackName}-LoadBalancerDNS"
連携方法:OutputsとParametersの活用
Terraformで管理する基盤レイヤーのOutputsを、CloudFormationで管理するアプリケーションレイヤーのParametersに渡すことで、両者を連携させます。
連携の具体的手順
-
TerraformでOutputsを定義: Terraformの
outputs.tf
で、VPCのIDやサブネットのIDを定義 -
TerraformでCloudFormationをデプロイ: Terraformの
aws_cloudformation_stack
リソースを使用 - パラメータの受け渡し: TerraformのOutputsをCloudFormationのParametersに渡す
Terraformコード例:CloudFormationスタックの呼び出し
# main.tf (Terraform)
locals {
stack_name = "MyApplicationStack"
}
# CloudFormationテンプレートの読み込み
data "template_file" "application_template" {
template = file("${path.module}/application.yaml")
}
# CloudFormationスタックのデプロイ
resource "aws_cloudformation_stack" "app_stack" {
name = local.stack_name
template_body = data.template_file.application_template.rendered
parameters = {
VpcId = aws_vpc.main.id
PublicSubnetIds = join(",", aws_subnet.public[*].id)
WebSecurityGroupId = aws_security_group.web.id
}
capabilities = ["CAPABILITY_IAM"]
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
}
}
# CloudFormationスタックの出力を取得
data "aws_cloudformation_stack" "app_stack_outputs" {
name = aws_cloudformation_stack.app_stack.name
}
プロジェクト構成例
実際のプロジェクトでは、以下のようなディレクトリ構成を推奨します:
infrastructure/
├── terraform/ # 基盤レイヤー
│ ├── modules/
│ │ └── network/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── environments/
│ │ ├── dev/
│ │ │ ├── main.tf
│ │ │ ├── terraform.tfvars
│ │ │ └── backend.tf
│ │ └── prod/
│ │ ├── main.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ └── cloudformation/ # CloudFormationテンプレート
│ ├── application.yaml
│ └── database.yaml
├── scripts/
│ ├── deploy.sh
│ └── destroy.sh
└── README.md
メリット・デメリットと注意点
メリット
- 最適なツールの使い分け: 各レイヤーで最適なツールを使用可能
- リスクの分散: 基盤とアプリケーションで変更の影響範囲を分離
- チーム分業: インフラチームとアプリチームで責任分担が可能
デメリット・注意点
- 複雑性の増大: 2つのツールを管理する必要がある
- 学習コスト: 両方のツールの知識が必要
- 依存関係の管理: 基盤レイヤーの変更がアプリケーションレイヤーに影響する可能性
- デバッグの困難さ: 問題発生時の原因特定が複雑になる場合がある
ベストプラクティス
- 明確な責任分界点: どのリソースをどちらで管理するかを明確に定義
- バージョン管理: 両方のコードを同じリポジトリで管理し、変更履歴を追跡
- CI/CDパイプライン: 自動化されたデプロイメントパイプラインの構築
- モニタリング: 両レイヤーのリソース状態を監視する仕組みの構築
- ドキュメント化: 連携方法や依存関係を明確に文書化
まとめ
CloudFormationとTerraformのハイブリッドモデルは、それぞれのツールの弱点を補完し、より堅牢でスケーラブルなIaCを実現します。
レイヤー | 管理ツール | 主な理由 |
---|---|---|
基盤 | Terraform | 再利用性、マルチクラウド対応、モジュール化 |
アプリケーション | CloudFormation | AWSサービスとの親和性、自動ロールバック、イベント統合 |
このアプローチは、大規模なインフラを管理する多くの企業で採用されており、一つのツールに固執せず、目的に応じて最適なツールを使い分けることの重要性を示しています。
ただし、複雑性が増すことも事実なので、チームのスキルレベルやプロジェクトの規模を考慮して導入を検討することが重要です。小規模なプロジェクトでは、単一ツールでの管理の方が効率的な場合もあることを念頭に置いて選択しましょう。