terraform について
terraform は、Terraform と OpenTofu のインフラコード作業向けスキルです。設定構成、状態の保護、リファクタリング、テストに関する計画と検証の手順を扱います。
主な機能
- 01設定ファイルとモジュールの整理
- 02状態と認証情報の保護
- 03リソースアドレスの安全な変更
- 04リソース反復とバージョン制約の計画
- 05設定検証とテスト実行
活用シーン
- 01インフラモジュールの整理
- 02リソース変更計画のレビュー
- 03状態ドリフトやロックの調査
- 04実インフラを作成せずに設定をテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ iliaal/ ai-skills --skill terraformインストール後、Claude Code で/terraformを入力して読み込まれたか確認します。
同梱ファイルreferences/native-test-patterns.mdSPEC.md
詳細
Terraform と OpenTofu
作業ルール
- リファクタリング中は状態とリソースアドレスを維持し、意図しない置き換えがないかプランを確認します。
- ローカルまたはモックによる影響を検証済みのテストと、実サービスにアクセスする、または課金対象のインフラストラクチャを作成するテストを分けます。プランモードでも実際のデータソースを読み取る場合がありますが、モックを使った適用はインフラストラクチャを作成せずに実行できます。
ファイル構成と命名
| ファイル | 用途 |
|---|---|
terraform.tf | Terraform とプロバイダーのバージョン要件 |
providers.tf | プロバイダーの設定 |
main.tf | 主要なリソースとデータソース |
variables.tf | 入力変数(アルファベット順) |
outputs.tf | 出力値(アルファベット順) |
locals.tf | ローカル値 |
- 小文字とアンダースコアを使用します。
webAPIやweb-apiではなくweb_apiのようにします。 - リソースタイプ名を含めず、内容を表す名詞を使います。
aws_instance.web_api_instanceではなくaws_instance.web_apiのようにします。 - 複数形ではなく単数形を使います。
- モジュール内に同じタイプのリソースが1つだけある場合は、シングルトンリソースに
thisを使います。 - 文脈に沿った変数の接頭辞を使います。
cidrではなくvpc_cidr_blockのようにします。
ブロックの順序
リソース: count/for_each(後に空行)→ 引数 → ネストされたブロック → tags → depends_on → lifecycle(最後)
変数: description → type → default → validation → nullable
すべての変数に type と description が必要です。すべての出力に description が必要です。シークレットには sensitive = true を指定します。
モジュール構成
| 種類 | 範囲 | 例 |
|---|---|---|
| リソースモジュール | 単一の論理グループ | VPC とサブネット、SG とルール |
| インフラストラクチャモジュール | リソースモジュールの集合 | 1つのリージョンのネットワーキングとコンピュート |
| コンポジション | 完全なインフラストラクチャ | 複数のリージョン/アカウントにまたがる |
module-name/
├── main.tf, variables.tf, outputs.tf, versions.tf
├── examples/
│ ├── minimal/
│ └── complete/
└── tests/
└── defaults.tftest.hcl
モジュールは小さく保ちます(単一責任)。examples/ はドキュメントと統合テスト用のフィクスチャを兼ねます。公開するすべてのモジュールにセマンティックバージョニングを適用します。
count と for_each
| シナリオ | 使用方法 |
|---|---|
| ブール値の切り替え(作成またはスキップ) | count = condition ? 1 : 0 |
| 順序が変わる可能性のある名前付き/キー付き項目 | for_each = toset(list) または map |
| 固定数の同一レプリカ | count = N |
デフォルトでは for_each を使います。count のリストから途中の項目を削除すると、それ以降のすべてのリソースが再作成されるためです。count はブール値の条件分岐、または本当に同一のレプリカに限って使います。
バージョンの固定
| コンポーネント | 方針 | 例 |
|---|---|---|
| Terraform | マイナーバージョンを固定 | required_version = "~> 1.9.0" |
| プロバイダー | メジャーバージョンを固定 | version = "~> 5.0" |
| モジュール(本番環境) | 完全一致で固定 | version = "5.1.2" |
| モジュール(開発環境) | パッチバージョンを許可 | version = "~> 5.1.0" |
主な最新機能:moved ブロック(1.1+)、デフォルト値を指定した optional()(1.3+)、ネイティブテスト(1.6+)、モックプロバイダー(1.7+)、変数間の検証(1.9+)、書き込み専用引数(1.11+)。
Stacks(HCP。現在のリリース状況を確認してください):複数の設定を単一のデプロイ単位としてオーケストレーションします。複数環境のパターンでの利用を検討してください。
状態とセキュリティ
- ロック機能付きのリモートバックエンドを使用します。S3 の場合は
use_lockfile = true(1.10+)、Azure Blob、GCS、または Terraform Cloud を使用します。共有インフラストラクチャでは、ローカル状態を決して使用しないでください。DynamoDB ベースの S3 ロック(dynamodb_table)は非推奨で、削除が予定されています。use_lockfileを優先してください。既存のテーブルから移行する間は、両方を同時に設定できます。 - OpenTofu 専用:
terraform { encryption { key_provider "pbkdf2" "k" {...} method "aes_gcm" "m" { keys = key_provider.pbkdf2.k } state { method = method.aes_gcm.m } plan { method = method.aes_gcm.m } } }を設定すると、状態ファイルとプランファイルがクライアント側で暗号化されます(またはTF_ENCRYPTIONを使用します)。既存の平文状態を引き続き読み込めるよう、fallback { method = method.unencrypted.x }を設定して段階的に導入してください。また、fallbackブロックなしでキープロバイダーやメソッドの名前を変更しないでください。OpenTofu ではbackend {}の引数やモジュールのsource/versionでもvar.*/local.*を使用できます(init時に解決されます。状態やプロバイダー関数への参照は使用できません)。同じ HCL は Terraform ではエラーになります(「A backend block cannot refer to named values」)。 - 保存時の状態を暗号化してください。
.tfstate、.terraform/、*.tfplanは決してコミットしないでください。.terraform.lock.hclは必ずコミットしてください。 - リソースのタグ付けを統一するため、プロバイダーに
default_tagsを設定してください。 - すべてのストレージで保存時の暗号化を有効にしてください。デフォルトではプライベートネットワークを使用し、パブリックアクセスは明示的に許可した場合のみ有効にしてください。
- 最小権限のセキュリティグループを使用してください。明確な理由がない限り、
0.0.0.0/0からのインバウンドを許可しないでください。 - 認証情報をハードコードしないでください。assume_role、OIDC、またはシークレットマネージャーを使用してください。
- プリコミットでは、まず自動フォーマット(
terraform fmt -recursiveはファイルを書き換えます)を実行し、その後に検証(terraform validate && tflint && trivy config .)を実行してください。 - リソース名やモジュール名のリファクタリングで破棄と再作成を行わないよう、
fromとtoのアドレスを指定したmovedブロックを使用してください。下流でのアップグレードに備え、過去の移動定義は保持してください。影響を受けるすべての状態が移行した後、または明示的に破壊的変更を伴うモジュールリリースとしてのみ削除してください。 lifecycle { ignore_changes = [attr] }は 更新のみ を抑制し、設定変更後の 最初の プラン時に、以前の状態の値で置き換えます。「最初の適用」の例外はありません。レビュー担当者が誤解しやすい結果が 2 つあります。(1)すでにプロビジョニング済みのリソースでは、設定内のリテラル値は書き込まれません。また、ignore_changesが置換の評価前に差分を消去するため、ForceNewも発動しません。そのため、コミット済みの値をプレースホルダーに置き換える変更はリポジトリから値を消去する一方、リモートの値はそのまま残ります。(2)ignore_changesは作成時には適用されないため、その後の-replace、taint、state rmによる削除と再追加、または手動削除の際には、別の手段で設定された値の上にプレースホルダーが再設定されます。設定にはコンテナリソースのみを残し、値はすべて別の手段でプロビジョニングしてください。または、あらゆる置換手順について、復元手順をランブックに明記してください。
トラブルシューティング
- 状態ロックが解除されない場合:他の操作が実行中でないことを確認してから、
terraform force-unlock <ID>を実行します。 - リソースのドリフト:検出には
terraform plan -refresh-only、反映にはterraform apply -refresh-onlyを使用します。 - tainted リソースを置換する場合:
terraform taint(非推奨)ではなく、terraform apply -replace=ADDRを使用します。 - 既存リソースをインポートする場合:宣言的なインポートには
importブロック(1.5+)、またはterraform import ADDR IDを使用します。
依存関係の管理
明示的な depends_on を使わずに削除順序を制御するには、try() と locals を使用します。
locals {
vpc_id = try(aws_vpc_ipv4_cidr_block_association.this[0].vpc_id, aws_vpc.this.id, "")
}
これにより、Terraform は CIDR 関連付けより先にサブネットを破棄し、削除エラーを防止します。
- サブネット CIDR の計算には
cidrsubnet(var.vpc_cidr, 8, count.index)を使用してください。サブネットをハードコードしないでください。 - マルチリージョンでは、
provider "aws" { alias = "eu_west_1" }と、モジュールブロック内のproviders = { aws = aws.eu_west_1 }を使用します。
検証
完了を宣言する前に実行してください。
terraform fmt -check && terraform validate && tflint && trivy config .
すべてのコマンドがエラーなしで成功する必要があります。テストファイルを選ぶ前に、プロバイダー、データソース、プロビジョナー、外部コマンドを確認してください。完了チェックには、安全なプランテストと完全にモック化した適用テストを含めてください。実サービスやインフラストラクチャを使うテストは、タスクの承認範囲とコスト上限の範囲内で実行してください。terraform test -filter=tests/vpc_unit_test.tftest.hcl のように、リポジトリルートからの相対パスでテストファイル名を指定し、対象のファイルが少なくとも1回実行されたことを確認してください。終了コードだけでは、テストが選択されず、未知のフィルターが受け入れられる場合があります。native-test-patterns.md に記載された、検証済みの選択手順を使用してください。
タスク固有のリファレンス
該当する動作を実装またはレビューする前に、関連するリファレンスを読んでください。
- ネイティブのプラン/適用テスト、フィクスチャの順序、または CI のテスト選択については、native-test-patterns.md を参照してください。