takt/builtins/en/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

6.9 KiB

Terraform AWS Knowledge

Module Design

Split modules by domain (network, database, application layer). Do not create generic utility modules.

Criteria Judgment
Domain-based module splitting OK
Generic "utils" module REJECT
Unrelated resources mixed in one module REJECT
Implicit inter-module dependencies REJECT (connect explicitly via outputs→inputs)

Inter-Module Dependencies

Pass dependencies explicitly via outputs→inputs. Avoid implicit references (using data sources to look up other module resources).

# OK - Explicit dependency
module "database" {
  source     = "../../modules/database"
  vpc_id     = module.network.vpc_id
  subnet_ids = module.network.private_subnet_ids
}

# NG - Implicit dependency
module "database" {
  source = "../../modules/database"
  # vpc_id not passed; module uses data "aws_vpc" internally
}

Identification Variable Passthrough

Pass identification variables (environment, service name) explicitly from root to child modules. Do not rely on globals or hardcoding.

# OK - Explicit passthrough
module "database" {
  environment      = var.environment
  service          = var.service
  application_name = var.application_name
}

Resource Naming Convention

Compute name_prefix in locals and apply consistently to all resources. Append resource-specific suffixes.

Criteria Judgment
Unified naming with name_prefix pattern OK
Inconsistent naming across resources REJECT
Name exceeds AWS character limits REJECT
Tag names not in PascalCase Warning
# OK - Unified with name_prefix
locals {
  name_prefix = "${var.environment}-${var.service}-${var.application_name}"
}

resource "aws_ecs_cluster" "main" {
  name = "${local.name_prefix}-cluster"
}

# NG - Inconsistent naming
resource "aws_ecs_cluster" "main" {
  name = "${var.environment}-app-cluster"
}

Character Limit Handling

AWS services have name character limits. Use shortened forms when approaching limits.

Service Limit Example
Target Group 32 chars ${var.environment}-${var.service}-backend-tg
Lambda Function 64 chars Full prefix OK
S3 Bucket 63 chars Full prefix OK

Tagging Strategy

Use provider default_tags for common tags. No duplicate tagging on individual resources.

Criteria Judgment
Centralized via provider default_tags OK
Duplicate tags matching default_tags on individual resources Warning
Only Name tag added on individual resources OK
# OK - Centralized, individual gets Name only
provider "aws" {
  default_tags {
    tags = {
      Environment = var.environment
      ManagedBy   = "Terraform"
    }
  }
}

resource "aws_instance" "main" {
  tags = {
    Name = "${local.name_prefix}-instance"
  }
}

# NG - Duplicates default_tags
resource "aws_instance" "main" {
  tags = {
    Environment = var.environment
    ManagedBy   = "Terraform"
    Name        = "${local.name_prefix}-instance"
  }
}

File Organization Patterns

Environment Directory Structure

Separate environments into directories, each with independent state management.

environments/
├── production/
│   ├── terraform.tf       # Version constraints
│   ├── providers.tf       # Provider config (default_tags)
│   ├── backend.tf         # S3 backend
│   ├── variables.tf       # Environment variables
│   ├── main.tf            # Module invocations
│   └── outputs.tf         # Outputs
└── staging/
    └── ...

Module File Structure

File Contents
main.tf locals and data sources only
variables.tf Input variable definitions only (no resources)
outputs.tf Output definitions only (no resources)
{resource_type}.tf One file per resource category
templates/ user_data scripts and other templates

Security Best Practices

EC2 Instance Security

Setting Recommended Reason
http_tokens "required" Enforce IMDSv2 (SSRF prevention)
http_put_response_hop_limit 1 Prevent container escapes
root_block_device.encrypted true Data-at-rest encryption

S3 Bucket Security

Block all public access with all four settings. Use OAC (Origin Access Control) for CloudFront distributions.

# OK - Complete block
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 Design

Pattern Recommendation
Per-service role separation Separate execution role (for ECS Agent) and task role (for app)
CI/CD authentication OIDC federation (avoid long-lived credentials)
Policy scope Specify resource ARNs explicitly (avoid "*")

Secret Management

Method Recommendation
SSM Parameter Store (SecureString) Recommended
Secrets Manager Recommended (when rotation needed)
Direct in .tfvars Conditional OK (gitignore required)
Hardcoded in .tf files REJECT

Set SSM Parameter initial values to placeholders and use lifecycle { ignore_changes = [value] } to manage outside Terraform.

Cost Optimization Patterns

Document trade-offs with inline comments for cost-impacting choices.

Choice Cost Effect Trade-off
NAT Instance vs NAT Gateway Instance ~$3-4/mo vs Gateway ~$32/mo Lower availability and throughput
Public subnet placement No VPC Endpoints needed Weaker network isolation
EC2 + EBS vs RDS EC2 ~$15-20/mo vs RDS ~$50+/mo Higher operational burden
# OK - Trade-off documented
# Using t3.nano instead of NAT Gateway (~$3-4/mo vs ~$32/mo)
# Trade-off: single-AZ availability, throughput limits
resource "aws_instance" "nat" {
  instance_type = "t3.nano"
}

Lifecycle Rule Usage

Rule Purpose Target
prevent_destroy Prevent accidental deletion Databases, EBS volumes
ignore_changes Allow external changes desired_count (Auto Scaling), SSM value
create_before_destroy Prevent downtime Load balancers, security groups
# OK - Prevent accidental database deletion
resource "aws_instance" "database" {
  lifecycle {
    prevent_destroy = true
  }
}

# OK - Let Auto Scaling manage desired_count
resource "aws_ecs_service" "main" {
  lifecycle {
    ignore_changes = [desired_count]
  }
}

Version Management

Setting Recommendation
required_version ">= 1.5.0" or higher (default_tags support)
Provider version Pin minor version with ~> (e.g., ~> 5.80)
State locking use_lockfile = true required