hoicil-spot-tf の規約を分析し、専用のピース・ファセットを作成。 plan → implement → 3並列レビュー → fix → COMPLETE のワークフロー。 カテゴリに「インフラストラクチャ」を追加。
242 lines
6.9 KiB
Markdown
242 lines
6.9 KiB
Markdown
# 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).
|
|
|
|
```hcl
|
|
# 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.
|
|
|
|
```hcl
|
|
# 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 |
|
|
|
|
```hcl
|
|
# 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 |
|
|
|
|
```hcl
|
|
# 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.
|
|
|
|
```hcl
|
|
# 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 |
|
|
|
|
```hcl
|
|
# 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 |
|
|
|
|
```hcl
|
|
# 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 |
|