Terraformのベストプラクティス:ディレクトリ構成と命名規則
はじめに
Terraformは強力なInfrastructure as Code(IaC)ツールですが、プロジェクトの規模が拡大するにつれて、コードの管理が複雑になります。特にチーム開発では、一貫したルールがなければ、コードの可読性や保守性が著しく低下し、運用コストの増大や障害リスクの向上につながります。
本記事では、Terraformプロジェクトを効率的かつ安全に管理するためのディレクトリ構成と命名規則について、実践的なベストプラクティスを詳しく解説します。
1. 推奨されるディレクトリ構成
1.1 基本構成
Terraformのコードを整理する上で最も重要な原則は、関心の分離と再利用性の確保です。以下の構成を推奨します:
terraform-project/
├── modules/ # 再利用可能なモジュール
│ ├── networking/ # ネットワーク関連モジュール
│ │ ├── vpc/
│ │ │ ├── main.tf
│ │ │ ├── variables.tf
│ │ │ ├── outputs.tf
│ │ │ └── versions.tf
│ │ └── security-groups/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── compute/ # コンピュート関連モジュール
│ │ ├── ec2/
│ │ └── ecs/
│ └── storage/ # ストレージ関連モジュール
│ ├── s3/
│ └── rds/
├── environments/ # 環境ごとの構成
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tfvars
│ │ └── versions.tf
│ ├── staging/
│ └── prod/
├── shared/ # 共通リソース(Route53ホストゾーンなど)
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── scripts/ # 補助スクリプト
│ ├── init.sh
│ └── validate.sh
└── docs/ # ドキュメント
├── README.md
└── architecture.md
1.2 各ディレクトリの役割
📁 modules/ ディレクトリ
- 目的: 再利用可能な共通コンポーネントの定義
- 構成: 機能領域(networking, compute, storage)で分類
- 利点: DRY原則の実践、コードの標準化、テストの容易性
📁 environments/ ディレクトリ
- 目的: 環境固有の設定とモジュールの組み合わせ
- 構成: 各環境(dev, staging, prod)の独立した設定
- 利点: 環境間の設定差分の明確化、デプロイの安全性向上
📁 shared/ ディレクトリ
- 目的: 環境間で共有されるリソースの管理
- 例: Route53ホストゾーン、IAMロール、共有VPC
- 注意: 変更時の影響範囲を慎重に検討する
1.3 モジュール設計のベストプラクティス
# modules/networking/vpc/main.tf の例
resource "aws_vpc" "main" {
cidr_block = var.cidr_block
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags = merge(
var.common_tags,
{
Name = "${var.project_name}-${var.environment}-vpc"
}
)
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = merge(
var.common_tags,
{
Name = "${var.project_name}-${var.environment}-igw"
}
)
}
2. ファイル構成と役割
2.1 必須ファイル
main.tf
- リソースの定義とモジュールの呼び出し
- 論理的な構成要素ごとにコメントで区切る
variables.tf
variable "project_name" {
description = "プロジェクト名(リソース名のプレフィックスに使用)"
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]*[a-z0-9]$", var.project_name))
error_message = "プロジェクト名は小文字、数字、ハイフンのみ使用可能です。"
}
}
variable "environment" {
description = "環境名(dev, staging, prod)"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "環境名はdev, staging, prodのいずれかである必要があります。"
}
}
outputs.tf
output "vpc_id" {
description = "VPCのID"
value = aws_vpc.main.id
}
output "vpc_cidr_block" {
description = "VPCのCIDRブロック"
value = aws_vpc.main.cidr_block
}
versions.tf
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
2.2 環境固有のファイル
terraform.tfvars
# environments/dev/terraform.tfvars
project_name = "my-project"
environment = "dev"
region = "ap-northeast-1"
# VPC設定
vpc_cidr = "10.0.0.0/16"
# EC2設定
instance_type = "t3.micro"
⚠️ セキュリティ注意事項:
- 機密情報(パスワード、APIキー)は
terraform.tfvarsに含めない - AWS Systems Parameter StoreやHashiCorp Vaultを使用する
-
.tfvarsファイルの.gitignoreへの追加を検討
3. 命名規則
3.1 リソース論理名
基本ルール:
- 小文字のスネークケース(snake_case)を使用
- 意味のある説明的な名前を付ける
- 複数形は避ける(例:
instance、instancesではない)
# ✅ 良い例
resource "aws_vpc" "main" { }
resource "aws_subnet" "public_web" { }
resource "aws_security_group" "web_server" { }
# ❌ 悪い例
resource "aws_vpc" "VPC1" { }
resource "aws_subnet" "pub-subnet-1" { }
resource "aws_security_group" "sg1" { }
3.2 AWSリソース名
命名パターン:
{project_name}-{environment}-{resource_type}-{purpose}
例:
resource "aws_instance" "web_server" {
# ...
tags = {
Name = "my-project-prod-ec2-web"
}
}
resource "aws_s3_bucket" "app_logs" {
bucket = "my-project-prod-s3-logs"
# ...
}
3.3 タグ戦略
必須タグ
すべてのリソースに以下のタグを付与:
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.team_name
CostCenter = var.cost_center
CreatedAt = formatdate("YYYY-MM-DD", timestamp())
}
}
タグの自動適用
プロバイダーレベルでデフォルトタグを設定:
provider "aws" {
region = var.region
default_tags {
tags = local.common_tags
}
}
4. 状態管理のベストプラクティス
4.1 リモート状態の設定
# environments/prod/main.tf
terraform {
backend "s3" {
bucket = "my-project-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
4.2 状態の分離
環境ごと、サービスごとに状態ファイルを分離し、変更の影響範囲を限定します。
5. CI/CD統合の考慮事項
5.1 ディレクトリ構成とパイプライン
# .github/workflows/terraform.yml の例
name: Terraform
on:
push:
paths:
- 'environments/**'
- 'modules/**'
jobs:
terraform:
runs-on: ubuntu-latest
strategy:
matrix:
environment: [dev, staging, prod]
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Plan
run: |
cd environments/${{ matrix.environment }}
terraform plan
まとめ
効果的なTerraformプロジェクトの構築には、以下の要素が不可欠です:
| 要素 | 目的 | 効果 |
|---|---|---|
| 構造化されたディレクトリ構成 | コードの整理と再利用性の向上 | 開発効率の向上、保守性の確保 |
| 一貫した命名規則 | 可読性とチーム協業の促進 | コミュニケーションコストの削減 |
| 適切なタグ戦略 | リソース管理とコスト最適化 | 運用コストの可視化と制御 |
| セキュリティ考慮 | 機密情報の適切な管理 | セキュリティリスクの軽減 |
これらのベストプラクティスを段階的に導入することで、Terraformをより安全かつ効率的にチーム運用できるようになります。
次回は、Terraformのテスト戦略とCI/CDパイプラインへの統合について詳しく解説します。