takt/builtins/ja/facets/knowledge/terraform-aws.md
nrslib 1cd063680c feat: Terraform/AWS ピースとファセット一式を追加
hoicil-spot-tf の規約を分析し、専用のピース・ファセットを作成。
plan → implement → 3並列レビュー → fix → COMPLETE のワークフロー。
カテゴリに「インフラストラクチャ」を追加。
2026-02-25 23:50:52 +09:00

242 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Terraform AWS 知識
## モジュール設計
モジュールはドメイン(ネットワーク、データベース、アプリケーション層)単位で分割する。汎用ユーティリティモジュールは作らない。
| 基準 | 判定 |
|------|------|
| ドメイン単位のモジュール分割 | OK |
| 汎用 "utils" モジュール | REJECT |
| 1モジュールに無関係なリソースが混在 | REJECT |
| モジュール間の暗黙的依存 | REJECT出力→入力で明示的に接続 |
### モジュール間の依存
モジュール間の依存は出力→入力で明示的に渡す。暗黙的な参照(`data` ソースで他モジュールのリソースを引く)は避ける。
```hcl
# OK - 明示的な依存
module "database" {
source = "../../modules/database"
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
}
# NG - 暗黙的な依存
module "database" {
source = "../../modules/database"
# vpc_id を渡さず、module 内で data "aws_vpc" で引いている
}
```
### 識別変数のパススルー
環境名・サービス名などの識別変数は、ルートモジュールから子モジュールへ明示的に渡す。グローバル変数やハードコードに頼らない。
```hcl
# OK - 明示的なパススルー
module "database" {
environment = var.environment
service = var.service
application_name = var.application_name
}
```
## リソース命名規約
`locals``name_prefix` を計算し、全リソースに一貫して適用する。リソース固有のサフィックスを付加する。
| 基準 | 判定 |
|------|------|
| `name_prefix` パターンで統一命名 | OK |
| 各リソースでバラバラに命名 | REJECT |
| AWS 文字数制限を超える名前 | REJECT |
| タグ名が PascalCase でない | 警告 |
```hcl
# OK - name_prefix で統一
locals {
name_prefix = "${var.environment}-${var.service}-${var.application_name}"
}
resource "aws_ecs_cluster" "main" {
name = "${local.name_prefix}-cluster"
}
# NG - 各リソースでバラバラに命名
resource "aws_ecs_cluster" "main" {
name = "${var.environment}-app-cluster"
}
```
### 文字数制限への対応
AWS サービスには名前の文字数制限がある。制限に近い場合は短縮形を使う。
| サービス | 制限 | 例 |
|---------|------|-----|
| Target Group | 32文字 | `${var.environment}-${var.service}-backend-tg` |
| Lambda 関数 | 64文字 | フルプレフィックス可 |
| S3 バケット | 63文字 | フルプレフィックス可 |
## タグ戦略
provider の `default_tags` で共通タグを一括設定する。個別リソースでの重複タグ付けは不要。
| 基準 | 判定 |
|------|------|
| provider `default_tags` で一括設定 | OK |
| 個別リソースで `default_tags` と同じタグを重複設定 | 警告 |
| 個別リソースで `Name` タグのみ追加 | OK |
```hcl
# OK - provider で一括、個別は Name のみ
provider "aws" {
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
}
}
}
resource "aws_instance" "main" {
tags = {
Name = "${local.name_prefix}-instance"
}
}
# NG - default_tags と重複
resource "aws_instance" "main" {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Name = "${local.name_prefix}-instance"
}
}
```
## ファイル構成パターン
### 環境ディレクトリ構造
環境ごとにディレクトリを分離し、各環境が独立した状態管理を持つ。
```
environments/
├── production/
│ ├── terraform.tf # バージョン制約
│ ├── providers.tf # プロバイダ設定default_tags
│ ├── backend.tf # S3 バックエンド
│ ├── variables.tf # 環境変数
│ ├── main.tf # モジュール呼び出し
│ └── outputs.tf # 出力
└── staging/
└── ...
```
### モジュール内ファイル構成
| ファイル | 内容 |
|---------|------|
| `main.tf` | `locals``data` ソースのみ |
| `variables.tf` | 入力変数定義のみ(リソースなし) |
| `outputs.tf` | 出力定義のみ(リソースなし) |
| `{resource_type}.tf` | リソースカテゴリごとに1ファイル |
| `templates/` | user_data スクリプト等のテンプレート |
## セキュリティベストプラクティス
### EC2 インスタンスセキュリティ
| 設定 | 推奨値 | 理由 |
|------|--------|------|
| `http_tokens` | `"required"` | IMDSv2 強制SSRF 防止) |
| `http_put_response_hop_limit` | `1` | コンテナエスケープ防止 |
| `root_block_device.encrypted` | `true` | 保存データ暗号化 |
### S3 バケットセキュリティ
パブリックアクセスは4項目すべてブロックする。CloudFront 経由の場合は OACOrigin Access Controlを使用する。
```hcl
# OK - 完全ブロック
resource "aws_s3_bucket_public_access_block" "this" {
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
```
### IAM 設計
| パターン | 推奨 |
|---------|------|
| 用途別ロール分離 | 実行ロールECS Agent 用)とタスクロール(アプリ用)を分ける |
| CI/CD 認証 | OIDC フェデレーション(長期認証情報を使わない) |
| ポリシースコープ | リソース ARN を明示的に指定(`"*"` を避ける) |
### 機密情報管理
| 方法 | 推奨度 |
|------|--------|
| SSM Parameter StoreSecureString | 推奨 |
| Secrets Manager | 推奨(ローテーション必要時) |
| `.tfvars` に直接記載 | 条件付きOKgitignore 必須) |
| `.tf` ファイルにハードコード | REJECT |
SSM Parameter の初期値はプレースホルダーにし、`lifecycle { ignore_changes = [value] }` で Terraform 管理外にする。
## コスト最適化パターン
コスト影響のある選択にはインラインコメントでトレードオフを文書化する。
| 選択 | コスト効果 | トレードオフ |
|------|-----------|------------|
| NAT Instance vs NAT Gateway | NAT Instance は月額 ~$3-4 vs Gateway ~$32 | 可用性・スループットが劣る |
| パブリックサブネット配置 | VPC Endpoint 不要 | ネットワーク分離が弱まる |
| EC2 + EBS vs RDS | EC2 は月額 ~$15-20 vs RDS ~$50+ | 運用負荷が増える |
```hcl
# OK - トレードオフを文書化
# NAT Gateway の代わりに t3.nano を使用(約 $3-4/月 vs $32/月)
# トレードオフ: 可用性は単一AZ、スループット上限あり
resource "aws_instance" "nat" {
instance_type = "t3.nano"
}
```
## Lifecycle ルールの使い分け
| ルール | 用途 | 適用対象 |
|--------|------|---------|
| `prevent_destroy` | 誤削除防止 | データベース、EBS ボリューム |
| `ignore_changes` | 外部変更を許容 | `desired_count`Auto Scaling、SSM の `value` |
| `create_before_destroy` | ダウンタイム防止 | ロードバランサー、セキュリティグループ |
```hcl
# OK - データベースの誤削除防止
resource "aws_instance" "database" {
lifecycle {
prevent_destroy = true
}
}
# OK - Auto Scaling の desired_count を Terraform 管理外にする
resource "aws_ecs_service" "main" {
lifecycle {
ignore_changes = [desired_count]
}
}
```
## バージョン管理
| 設定 | 推奨 |
|------|------|
| `required_version` | `">= 1.5.0"` 以上(`default_tags` サポート) |
| プロバイダバージョン | `~>` でマイナーバージョン固定(例: `~> 5.80` |
| 状態ロック | `use_lockfile = true` 必須 |