Agent-almanac provision-infrastructure-terraform
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/ja/skills/provision-infrastructure-terraform" ~/.claude/skills/pjt222-agent-almanac-provision-infrastructure-terraform-e0cb81 && rm -rf "$T"
i18n/ja/skills/provision-infrastructure-terraform/SKILL.mdTerraformによるインフラストラクチャのプロビジョニング
Terraformを使用したInfrastructure as Codeにより、AWS、Azure、GCP、その他のプロバイダーでクラウドリソースをプロビジョニング、バージョン管理、管理します。
使用タイミング
- 新規クラウドインフラのプロビジョニング(VPC、コンピュート、ストレージ、データベース)
- ClickOpsやCloudFormationから宣言型IaCへの移行
- マルチ環境インフラの管理(dev、staging、production)
- チーム間で再現可能なインフラパターンの実装
- アプリケーションコードと並べたインフラ変更のバージョン管理
- 再利用可能なモジュールによるインフラ標準の強制
入力
- 必須: Terraform CLIがインストール済み(
)terraform --version - 必須: クラウドプロバイダー認証情報(AWS、Azure、GCPサービスアカウント)
- 必須: リモートステートバックエンド設定(S3、Azure Storage、Terraform Cloud)
- 任意: インポートまたは移行する既存インフラ
- 任意: チームコラボレーション向けTerraform Cloud/Enterprise
- 任意: 検証とフォーマット用のpre-commitフック
手順
完全な設定ファイルとテンプレートは拡張サンプルを参照してください。
ステップ1: Terraformプロジェクト構造の初期化
バックエンド設定とプロバイダーセットアップを含む整理されたディレクトリ構造を作成します。
# Create project structure mkdir -p terraform/{modules,environments/{dev,staging,prod}} cd terraform # Create backend configuration cat > backend.tf <<'EOF' terraform { required_version = ">= 1.6" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "my-terraform-state" key = "infrastructure/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-lock" # Workspace-specific state files workspace_key_prefix = "env" } } provider "aws" { region = var.aws_region default_tags { tags = { ManagedBy = "Terraform" Environment = terraform.workspace Project = var.project_name } } } EOF # Create variables file cat > variables.tf <<'EOF' variable "aws_region" { description = "AWS region for resources" type = string default = "us-east-1" } variable "project_name" { description = "Project name for resource naming and tagging" type = string validation { condition = length(var.project_name) > 0 && length(var.project_name) <= 32 error_message = "Project name must be 1-32 characters" } } variable "environment" { description = "Environment name (dev, staging, prod)" type = string validation { condition = contains(["dev", "staging", "prod"], var.environment) error_message = "Environment must be dev, staging, or prod" } } EOF # Initialize Terraform terraform init
期待結果: Terraformが正常に初期化され、プロバイダープラグインがダウンロードされ、リモートバックエンドが設定されます。プロバイダーバイナリを含む
.terraform/ ディレクトリが作成されます。ステートバックエンド接続が検証されます。
失敗時: バックエンド初期化が失敗する場合、S3バケットが存在し、IAMパーミッションが
s3:GetObject、s3:PutObject、dynamodb:GetItem、dynamodb:PutItem を許可していることを確認します。プロバイダーダウンロード失敗の場合、ネットワーク接続と社内プロキシ設定を確認します。terraform init -upgrade を実行してプロバイダーを更新してください。
ステップ2: 再利用可能なインフラモジュールの作成
入力検証を含むVPC、コンピュート、データインフラ向けの合成可能なモジュールを構築します。
# modules/vpc/main.tf variable "vpc_cidr" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } variable "availability_zones" { description = "List of AZs to use" type = list(string) } variable "project_name" { description = "Project name for resource naming" type = string } variable "environment" { description = "Environment name" type = string } locals { common_tags = { Project = var.project_name Environment = var.environment Module = "vpc" } } resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-vpc" }) } resource "aws_subnet" "public" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-public-${var.availability_zones[count.index]}" Type = "public" }) } resource "aws_subnet" "private" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 100) availability_zone = var.availability_zones[count.index] tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-private-${var.availability_zones[count.index]}" Type = "private" }) } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-igw" }) } resource "aws_eip" "nat" { count = length(var.availability_zones) domain = "vpc" tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-nat-eip-${var.availability_zones[count.index]}" }) depends_on = [aws_internet_gateway.main] } resource "aws_nat_gateway" "main" { count = length(var.availability_zones) allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id tags = merge(local.common_tags, { Name = "${var.project_name}-${var.environment}-nat-${var.availability_zones[count.index]}" }) depends_on = [aws_internet_gateway.main] } # modules/vpc/outputs.tf output "vpc_id" { description = "VPC ID" value = aws_vpc.main.id } output "public_subnet_ids" { description = "List of public subnet IDs" value = aws_subnet.public[*].id } output "private_subnet_ids" { description = "List of private subnet IDs" value = aws_subnet.private[*].id } output "nat_gateway_ips" { description = "List of NAT Gateway public IPs" value = aws_eip.nat[*].public_ip }
期待結果: モジュールが複数のAZにわたるパブリック・プライベートサブネット、インターネットゲートウェイ、EIPを持つNATゲートウェイを含むVPCを作成します。出力値がダウンストリームモジュール向けにリソースIDを公開します。
失敗時: CIDRオーバーラップエラーの場合、
cidrsubnet() 計算を調整するか、VPC CIDRが既存ネットワークと競合しないか検証します。依存関係エラーの場合、depends_on ブロックが適切なリソース作成順序を保証していることを確認します。terraform graph | dot -Tpng > graph.png で依存関係を可視化します。
ステップ3: 環境固有設定の実装
変数オーバーライドとデータソースを含む環境ワークスペースを作成します。
# environments/prod/main.tf terraform { required_version = ">= 1.6" } # Import shared backend and provider config # ... (see EXAMPLES.md for complete configuration)
期待結果: 環境固有の設定が3つのAZ、より大きなインスタンスタイプ、本番セキュリティ設定を持つ本番規模のインフラを作成します。データソースが最新のAMIを解決します。テンプレートファイルが環境変数でレンダリングされます。
失敗時: ワークスペースエラーの場合、
terraform workspace new prod でワークスペースを作成します。データソース失敗の場合、AWSクレデンシャルに ec2:DescribeImages パーミッションがあることを確認します。テンプレートレンダリングエラーの場合、変数の型がテンプレートの期待に一致することを検証します。
ステップ4: Plan・Applyワークフローの実行
Terraformプランの実行、変更の確認、承認ワークフローによる適用を行います。
# Format code terraform fmt -recursive # Validate configuration terraform validate # ... (see EXAMPLES.md for complete configuration)
CI/CD自動化の統合:
# .github/workflows/terraform.yml name: Terraform on: pull_request: paths: # ... (see EXAMPLES.md for complete configuration)
期待結果: プランがリソースの追加・変更・削除を表示します。ドリフトが検出されません。適用がエラーなくリソースを作成・更新します。出力が期待値を含みます。CIワークフローがPRにプランをコメントし、mainブランチマージ時に自動適用します。
失敗時: プラン失敗の場合、
terraform validate で構文エラーを確認します。ステートロックエラーの場合、aws dynamodb get-item --table-name terraform-lock --key '{"LockID":{"S":"terraform-state-bucket/key"}}' でロック保持者を特定し、古い場合は強制ロック解除します。適用失敗の場合、プロバイダー固有のエラーのCloudWatchログを確認します。terraform show で現在のステートを確認します。
ステップ5: ステート管理とドリフト検出の実装
ステートロック、バックアップ、自動ドリフト検出を設定します。
# Create DynamoDB table for state locking cat > state-backend.tf <<'EOF' resource "aws_dynamodb_table" "terraform_lock" { name = "terraform-lock" billing_mode = "PAY_PER_REQUEST" hash_key = "LockID" # ... (see EXAMPLES.md for complete configuration)
自動ドリフト検出:
# Create drift detection script cat > scripts/detect-drift.sh <<'EOF' #!/bin/bash set -euo pipefail cd terraform # ... (see EXAMPLES.md for complete configuration)
期待結果: バージョン管理と暗号化を備えたステートバックエンドが設定されます。ドリフト検出が帯域外変更を特定します。ステート操作(list、show、mv、import)がエラーなく実行されます。自動ドリフトチェックがスケジュールで実行され、アラートを送信します。
失敗時: ステートロックタイムアウトの場合、DynamoDBテーブルが存在し正しいキースキーマを持つことを確認します。バージョン管理の問題は、
aws s3api get-bucket-versioning --bucket bucket-name でS3バケットのバージョン管理状態を確認します。インポート失敗の場合、リソースが存在し、Terraform設定が実際のリソース属性と一致することを確認します。
ステップ6: モジュールテストとドキュメントの実装
Terratestによる自動テストとドキュメント生成を追加します。
// test/vpc_test.go package test import ( "testing" # ... (see EXAMPLES.md for complete configuration)
ドキュメントの生成:
# Install terraform-docs go install github.com/terraform-docs/terraform-docs@latest # Generate module documentation terraform-docs markdown table modules/vpc > modules/vpc/README.md # ... (see EXAMPLES.md for complete configuration)
期待結果: Terratestがモジュールが正しい設定で期待されるリソースを作成することを検証します。変数の説明と出力定義からドキュメントが自動生成されます。pre-commitフックがコミット前にフォーマットと検証を強制します。
失敗時: Terratest失敗の場合、AWSクレデンシャルとクォータを確認します。長時間実行のテストには
t.Parallel() で並列実行を実装します。ドキュメント生成エラーの場合、すべての変数に description 属性があることを確認します。pre-commitが失敗した場合、手動で terraform fmt を実行し検証エラーを修正します。
バリデーション
- バックエンドが暗号化、バージョン管理、ステートロックで設定されている
- すべてのモジュールに入力検証と出力値がある
- ワークスペースが環境固有のステートを分離している
-
が適用後に予期しない変更を表示しないterraform plan - ドリフト検出が自動的に実行され変更をアラートする
- モジュールがTerratestまたは類似フレームワークでテストされている
- ドキュメントが自動生成され最新の状態に保たれている
- シークレットがAWS Secrets Managerで管理され、ハードコードされていない
- コスト見積もりが統合されている(InfracostまたはSimilar)
- 爆発半径が環境ごとに分離されたステートで最小化されている
よくある落とし穴
-
ハードコードされた値: AMI ID、AZ、またはアカウント固有の値をハードコードしないでください。データソースと変数を使用してください。
-
lifecycleブロックの欠如: リソースが予期せず再作成されます。更新中のダウンタイムを防ぐために
を追加してください。lifecycle { create_before_destroy = true } -
ステートロックなし: 並行適用がステートを破損します。S3バックエンドには必ずDynamoDBテーブルをロックに使用してください。
-
過度に広いIAM: TerraformサービスアカウントがフルAdmin権限を持っています。管理リソースにスコープを絞った最小権限ポリシーを実装してください。
-
バージョン制約なし: プロバイダー更新がインフラを破壊します。
制約でプロバイダーバージョンを固定してください。version = "~> 5.0" -
ステート内のシークレット: 機密値がプレーンテキストのステートファイルに保存されます。出力に
を使用し、シークレットをAWS Secrets Managerに保存し、データソース経由で参照してください。sensitive = true -
バックアップ戦略なし: ステートファイルが失われまたは破損し、復旧計画がありません。S3バージョン管理を有効にし、定期的なステートバックアップを実装し、復旧手順をテストしてください。
-
単一形の設定: 単一ステートファイルがインフラ全体を管理しています。爆発半径を減らすために論理的な境界(ネットワーク、コンピュート、データ)に分割してください。
関連スキル
- Terraformコードのバージョン管理configure-git-repository
- GitHub Actionsによる自動化されたTerraformワークフローbuild-ci-cd-pipeline
- TerraformとArgoCD/Fluxの統合implement-gitops-workflow
- Terraformでプロビジョニングされたクラスターでのシークレット管理manage-kubernetes-secrets
- Terraform Kubernetesプロバイダーの使用deploy-to-kubernetes