terraform-iac-expert について
terraform-iac-expertは、TerraformとOpenTofuを使うインフラエンジニア向けのスキルです。モジュール設計、状態管理、マルチクラウド環境に加え、環境構成、セキュリティ、CI/CD連携を扱います。
主な機能
- 01再利用可能なTerraformモジュールの設計
- 02リモート状態とロックの設定
- 03複数環境のインフラ管理
- 04状態の移行とドリフトへの対応
- 05シークレットとIAMのセキュリティ設計
- 06CI/CDワークフローとの連携
活用シーン
- 01Terraformプロジェクト構成の作成
- 02開発・ステージング・本番環境の設定
- 03既存リソースの状態へのインポート
- 04状態ドリフトやプラン失敗の調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill terraform-iac-expertインストール後、Claude Code で/terraform-iac-expertを入力して読み込まれたか確認します。
同梱ファイル.claude-plugin/plugin.json
詳細
Terraform IaC エキスパート
概要
Terraform と OpenTofu を使用した Infrastructure as Code のエキスパートです。モジュール設計、状態管理、マルチクラウドへのデプロイ、CI/CD 連携を専門としています。複数環境のセットアップ、リモート状態バックエンド、セキュアなシークレット管理など、複雑なインフラストラクチャパターンに対応します。
使用する場面
- 新しい Terraform プロジェクトやワークスペースのセットアップ
- 再利用可能な Terraform モジュールの設計
- 状態ファイルとリモートバックエンドの管理
- 複数環境(dev/staging/prod)のインフラストラクチャの実装
- 既存のインフラストラクチャの Terraform への移行
- 状態のドリフトや plan の失敗のトラブルシューティング
- Terraform と CI/CD パイプラインの連携
- セキュリティのベストプラクティス(シークレット、IAM、ポリシー)の実装
機能
プロジェクト構成
- モジュールベースのアーキテクチャ設計
- ワークスペースとディレクトリ構成の戦略
- 変数と出力の整理
- プロバイダーの設定とバージョン制約
- リモート状態用のバックエンド設定
モジュール開発
- 再利用可能なモジュールパターン
- 入力の検証と型制約
- モジュールの組み合わせを考慮した出力設計
- ローカルモジュールとレジストリモジュール
- モジュールのバージョン管理と公開
状態管理
- リモート状態バックエンド(S3、GCS、Azure Blob、Terraform Cloud)
- 状態ロックの仕組み
- 状態の移行と操作
- 既存リソースのインポート
- 状態のドリフトへの対処
複数環境のパターン
- ワークスペースベースの環境
- ディレクトリベースの環境
- DRY なインフラストラクチャを実現する Terragrunt
- 環境固有の変数
- 環境昇格のワークフロー
セキュリティ
- 機密変数の取り扱い
- Terraform 用 IAM ロールの設計
- Policy as Code(Sentinel、OPA)
- シークレット管理との連携(Vault、AWS Secrets Manager)
- 最小権限の原則
CI/CD 連携
- Terraform 用 GitHub Actions
- PR ベースのワークフロー向け Atlantis
- Terraform Cloud/Enterprise
- Plan/Apply の自動化
- コスト見積もりとの連携
依存関係
以下と相性よく連携します:
aws-solutions-architect- AWS リソースのパターンkubernetes-orchestrator- K8s インフラストラクチャgithub-actions-pipeline-builder- CI/CD の自動化site-reliability-engineer- 本番環境のインフラストラクチャ
例
プロジェクト構成
terraform/
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ ├── eks/
│ └── rds/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── prod/
└── shared/
└── provider.tf
Locals を使ったルートモジュール
# environments/prod/main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "mycompany-terraform-state"
key = "prod/terraform.tfstate"
region = "us-west-2"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
locals {
environment = "prod"
project = "myapp"
common_tags = {
Environment = local.environment
Project = local.project
ManagedBy = "terraform"
}
}
module "vpc" {
source = "../../modules/vpc"
environment = local.environment
cidr_block = "10.0.0.0/16"
tags = local.common_tags
}
module "eks" {
source = "../../modules/eks"
environment = local.environment
vpc_id = module.vpc.vpc_id
private_subnet_ids = module.vpc.private_subnet_ids
cluster_version = "1.29"
tags = local.common_tags
}
入力検証を備えた再利用可能なモジュール
# modules/vpc/variables.tf
variable "environment" {
type = string
description = "Environment name (dev, staging, prod)"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "cidr_block" {
type = string
description = "VPC CIDR block"
validation {
condition = can(cidrhost(var.cidr_block, 0))
error_message = "Must be a valid CIDR block."
}
}
variable "availability_zones" {
type = list(string)
description = "List of AZs to use"
default = ["us-west-2a", "us-west-2b", "us-west-2c"]
}
variable "enable_nat_gateway" {
type = bool
description = "Enable NAT Gateway for private subnets"
default = true
}
variable "tags" {
type = map(string)
description = "Tags to apply to all resources"
default = {}
}
動的ブロックを使ったモジュール
# modules/security-group/main.tf
resource "aws_security_group" "this" {
name = var.name
description = var.description
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(var.tags, {
Name = var.name
})
}
リモート状態のデータソース
# Reference another environment's state
data "terraform_remote_state" "shared" {
backend = "s3"
config = {
bucket = "mycompany-terraform-state"
key = "shared/terraform.tfstate"
region = "us-west-2"
}
}
# Use outputs from shared state
resource "aws_instance" "app" {
ami = data.terraform_remote_state.shared.outputs.base_ami_id
instance_type = "t3.medium"
subnet_id = data.terraform_remote_state.shared.outputs.private_subnet_id
}
GitHub Actions による CI/CD
# .github/workflows/terraform.yml
name: Terraform
on:
pull_request:
paths:
- 'terraform/**'
push:
branches: [main]
paths:
- 'terraform/**'
env:
TF_VERSION: 1.6.0
AWS_REGION: us-west-2
jobs:
plan:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write # For OIDC
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/terraform-github-actions
aws-region: ${{ env.AWS_REGION }}
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Init
working-directory: terraform/environments/prod
run: terraform init
- name: Terraform Plan
working-directory: terraform/environments/prod
run: terraform plan -out=tfplan
- name: Upload Plan
uses: actions/upload-artifact@v4
with:
name: tfplan
path: terraform/environments/prod/tfplan
apply:
needs: plan
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment: production
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/terraform-github-actions
aws-region: ${{ env.AWS_REGION }}
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Download Plan
uses: actions/download-artifact@v4
with:
name: tfplan
path: terraform/environments/prod
- name: Terraform Apply
working-directory: terraform/environments/prod
run: terraform apply -auto-approve tfplan
既存リソースのインポート
# Import existing AWS resource into state
terraform import aws_s3_bucket.existing my-existing-bucket
# Import using for_each key
terraform import 'aws_iam_user.users["alice"]' alice
# Generate configuration from import (Terraform 1.5+)
terraform plan -generate-config-out=generated.tf
機密値の取り扱い
# Reference secrets from AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/password"
}
resource "aws_db_instance" "main" {
# ... other config ...
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
# Mark outputs as sensitive
output "db_connection_string" {
value = "postgres://admin:${aws_db_instance.main.password}@${aws_db_instance.main.endpoint}"
sensitive = true
}
ベストプラクティス
- リモート状態を使用する - チームで使うプロジェクトでは、状態をローカルに保存しないでください
- 状態ロックを有効にする - 同時変更を防止します
- プロバイダーのバージョンを固定する -
>=ではなく~>制約を使用します - 環境を分離する - ブランチではなく、ディレクトリまたはワークスペースを使用します
- 再利用可能なものはモジュール化する - ただし、過度な抽象化は避けます
- 入力を検証する - 変数の検証ブロックを使用します
- データソースを使用する - ハードコードする代わりに既存のリソースを参照します
- すべてのリソースにタグを付ける - コスト追跡のため、一貫したタグを適用します
- プランを慎重に確認する - 特に破棄操作を確認します
よくある落とし穴
- 状態ファイルの競合 - 複数の人が同時に terraform を実行する
- 値のハードコード - 環境ごとの違いに変数を使用しない
- 循環依存 - リソース同士が相互に依存する
- 依存関係の指定漏れ - 暗黙の依存関係だけでは不十分な場合に
depends_onを使用しない - 状態ファイルの肥大化 - 大規模なインフラストラクチャを分割しない
- 状態内のシークレット - 状態に機密値が含まれるため、保存時に暗号化する
- プロバイダーバージョンの不一致 - チームメンバーごとに異なるバージョンを使用する
- -target の不注意な使用 - ドリフトの原因になる可能性があるため、必要な場合に限って使用する
コメント
Markdown 対応