基本機能
ベストプラクティス
Terraformを効果的に使用するためのベストプラクティスを紹介します。これらの推奨事項に従うことで、保守性が高く、安全で、スケーラブルなインフラストラクチャコードを作成できます。
最終更新: 2026/2/17
ベストプラクティス
Terraformを効果的に使用するためのベストプラクティスを紹介します。これらの推奨事項に従うことで、保守性が高く、安全で、スケーラブルなインフラストラクチャコードを作成できます。
コード構成
1. ファイルの分割
関連するリソースをファイルごとに分割します。
project/
├── main.tf # プロバイダーとメインリソース
├── variables.tf # 変数の定義
├── outputs.tf # 出力の定義
├── versions.tf # Terraformとプロバイダーのバージョン
├── network.tf # ネットワーク関連リソース
├── compute.tf # コンピュートリソース
├── database.tf # データベースリソース
└── security.tf # セキュリティ関連リソース
2. 環境ごとにディレクトリを分離
terraform/
├── modules/ # 再利用可能なモジュール
│ ├── vpc/
│ ├── ec2/
│ └── rds/
├── environments/ # 環境ごとの設定
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ ├── staging/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── terraform.tfvars
│ └── prod/
│ ├── main.tf
│ ├── variables.tf
│ └── terraform.tfvars
└── global/ # 全環境共通のリソース
└── iam/
命名規則
1. リソース名
明確で一貫性のある命名規則を使用します。
# 良い例
resource "aws_instance" "web_server" {
# ...
}
resource "aws_s3_bucket" "application_logs" {
# ...
}
# 悪い例
resource "aws_instance" "i1" {
# ...
}
resource "aws_s3_bucket" "bucket" {
# ...
}
2. タグの標準化
すべてのリソースに一貫したタグを付けます。
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.owner
CostCenter = var.cost_center
}
}
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-web-${var.environment}"
Role = "WebServer"
}
)
}
変数の管理
1. 変数の型を明示
# 良い例
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t2.micro"
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
}
variable "tags" {
description = "Tags to apply to resources"
type = map(string)
default = {}
}
# 悪い例(型指定なし)
variable "instance_type" {
default = "t2.micro"
}
2. 変数の検証
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_type" {
description = "EC2 instance type"
type = string
validation {
condition = can(regex("^t[2-3]\\.", var.instance_type))
error_message = "Instance type must be a t2 or t3 instance."
}
}
3. 機密情報の扱い
variable "db_password" {
description = "Database password"
type = string
sensitive = true
}
# terraform.tfvarsには含めない
# 環境変数で渡す
# export TF_VAR_db_password="secure-password"
状態管理
1. リモートバックエンドの使用
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "prod/terraform.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-lock"
}
}
2. 状態ファイルの保護
# S3バケットの設定例
resource "aws_s3_bucket" "terraform_state" {
bucket = "my-terraform-state"
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
セキュリティ
1. 最小権限の原則
# IAMポリシーの例
resource "aws_iam_role_policy" "ec2_policy" {
name = "ec2-policy"
role = aws_iam_role.ec2_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket"
]
Resource = [
aws_s3_bucket.app_bucket.arn,
"${aws_s3_bucket.app_bucket.arn}/*"
]
}
]
})
}
2. セキュリティグループの制限
# 良い例:特定のIPからのみアクセスを許可
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for web servers"
vpc_id = aws_vpc.main.id
ingress {
description = "HTTPS from office"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["203.0.113.0/24"] # オフィスのIPアドレス
}
}
# 悪い例:すべてのポートを開放
resource "aws_security_group" "bad" {
name = "bad-sg"
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 避けるべき
}
}
3. 機密データの暗号化
resource "aws_db_instance" "main" {
identifier = "mydb"
engine = "postgres"
instance_class = "db.t3.micro"
allocated_storage = 20
# 暗号化を有効化
storage_encrypted = true
kms_key_id = aws_kms_key.db.arn
# パブリックアクセスを無効化
publicly_accessible = false
}
モジュールの使用
1. DRY原則(Don't Repeat Yourself)
# 悪い例:同じコードを繰り返す
resource "aws_instance" "web1" {
ami = "ami-xxxxx"
instance_type = "t2.micro"
# ... 多くの設定 ...
}
resource "aws_instance" "web2" {
ami = "ami-xxxxx"
instance_type = "t2.micro"
# ... 同じ設定を繰り返す ...
}
# 良い例:モジュールを使用
module "web_servers" {
source = "./modules/ec2"
count = 2
ami = "ami-xxxxx"
instance_type = "t2.micro"
}
2. モジュールのバージョン管理
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0" # メジャーバージョンを固定
name = "my-vpc"
cidr = "10.0.0.0/16"
}
コードの品質
1. フォーマットの統一
# コードのフォーマット
terraform fmt -recursive
# CI/CDでチェック
terraform fmt -check -recursive
2. 検証の実行
# 構文チェック
terraform validate
# セキュリティスキャン(tfsec)
tfsec .
# リンティング(tflint)
tflint
3. ドキュメントの作成
# variables.tf
variable "instance_type" {
description = "EC2 instance type. Recommended: t2.micro for dev, t3.medium for prod"
type = string
default = "t2.micro"
}
# outputs.tf
output "instance_id" {
description = "ID of the EC2 instance"
value = aws_instance.web.id
}
依存関係の管理
1. 明示的な依存関係
resource "aws_instance" "web" {
ami = "ami-xxxxx"
instance_type = "t2.micro"
# 明示的な依存関係
depends_on = [aws_s3_bucket.logs]
}
2. データソースの活用
# 既存のリソースを参照
data "aws_vpc" "existing" {
id = var.vpc_id
}
resource "aws_subnet" "new" {
vpc_id = data.aws_vpc.existing.id
cidr_block = "10.0.1.0/24"
}
ライフサイクル管理
1. 破壊的な変更の防止
resource "aws_db_instance" "main" {
identifier = "mydb"
# ... その他の設定 ...
lifecycle {
prevent_destroy = true
}
}
2. ダウンタイムの最小化
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
lifecycle {
create_before_destroy = true
}
}
3. 特定の変更を無視
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
tags = {
Name = "WebServer"
}
lifecycle {
ignore_changes = [
tags, # タグの変更を無視
user_data, # User Dataの変更を無視
]
}
}
CI/CDとの統合
1. 自動化されたワークフロー
# GitHub Actions の例
name: Terraform
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
- name: Terraform Format
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve
2. プルリクエストでのプラン確認
- name: Terraform Plan
run: terraform plan -no-color
continue-on-error: true
- name: Comment PR
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Terraform Plan:\n```\n${{ steps.plan.outputs.stdout }}\n```'
})
パフォーマンスの最適化
1. ターゲットの使用
# 特定のリソースのみ更新
terraform apply -target=aws_instance.web
# 複数のターゲットを指定
terraform apply -target=aws_instance.web -target=aws_security_group.web
2. 並列処理の調整
# 並列処理数を増やす
terraform apply -parallelism=20
# 並列処理数を減らす(APIレート制限対策)
terraform apply -parallelism=5
エラーハンドリング
1. 条件付きリソース作成
resource "aws_instance" "web" {
count = var.create_instance ? 1 : 0
ami = var.ami_id
instance_type = var.instance_type
}
2. デフォルト値の設定
locals {
instance_type = var.instance_type != "" ? var.instance_type : "t2.micro"
}
次のステップ
ベストプラクティスを理解したら、トラブルシューティングについて学びましょう。