mcpField
O
opentofu-coder

opentofu-coder

github.com/majesticlabs-devクラウド・DevOps5069 回インストール7 週間前に更新
リポジトリ

opentofu-coder について

OpenTofu のインフラ構成を書く開発者向けのリファレンススキルです。HCL のパターンや状態管理、CLI のワークフローを扱い、必要がない限りフラットな構成を推奨します。

主な機能

  1. 01フラットなファイル構成を優先
  2. 02HCL 構文とリソースの例
  3. 03モジュールと状態管理の説明
  4. 04状態暗号化と CLI ワークフロー
  5. 05インフラ構成のベストプラクティス

活用シーン

  1. 01小規模インフラの構成を計画
  2. 02リソース、変数、出力ブロックを作成
  3. 03インフラ状態の管理と暗号化
  4. 04不要な抽象化がないか確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/majesticlabs-dev/majestic-marketplace --skill opentofu-coder

インストール後、Claude Code で/opentofu-coderを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
environment-strategies.md
hcl-patterns.md
makefile-automation.md
post-provisioning.md
project-scaffolding.md
provider-config.md
provider-examples.md
state-management.md

詳細

OpenTofu コーダー

⚠️ シンプルさを最優先 — デフォルトはフラットファイル

常に最もシンプルな方法から始めてください。明示的に求められた場合にのみ、複雑さを加えてください。

シンプル(デフォルト)と過剰設計の比較
項目✅ シンプル(デフォルト)❌ 過剰設計
構成1つのディレクトリにフラットな .tf ファイルを配置ネストされた modules/ + environments/ ディレクトリ
モジュールなし、またはリモートレジストリモジュールのみシンプルなリソースにカスタムのローカルモジュールを使用
環境ワークスペース、または単一の tfvars環境ごとにディレクトリを複製
変数インラインのデフォルト値、最小限の tfvars複雑な変数階層
ファイル数.tf ファイルは合計 3~5 個ネストされたディレクトリ全体で 15 個以上
シンプルな方法を使うケース(90%)
  • 各タイプのリソースを 1~5 個管理する
  • 単一のプロバイダー、単一のリージョン
  • 小規模なチーム、または個人開発者
  • 標準的なインフラストラクチャのパターン
複雑さが妥当なケース(10%)
  • エンタープライズ向けのマルチリージョン、マルチアカウント
  • チーム間で共有する再利用可能なモジュール
  • 複雑な依存関係
  • ユーザーが明示的にモジュール構造を求めている

ルール:すべてをフラットな .tf ファイル 5 個以内で定義できるなら、そうしてください。

シンプルなプロジェクト構成(デフォルト)
infra/
├── main.tf           # All resources
├── variables.tf      # Input variables
├── outputs.tf        # Outputs
├── versions.tf       # Provider versions
└── terraform.tfvars  # Variable values (gitignored)

概要

OpenTofu は、Linux Foundation が保守する、MPL-2.0 ライセンスのコミュニティ主導のオープンソースな Terraform フォークです。HashiCorp Configuration Language(HCL)を使用して、クラウドプロバイダー全体で宣言的なインフラストラクチャ管理を行います。

基本理念

以下を優先してください。

  • 命令型より宣言型:手順ではなく、望ましい状態を記述します
  • 冪等性:複数回適用しても、同じ結果を安全に得られます
  • モジュール性:再利用可能なモジュールからインフラストラクチャを構成します
  • 状態を真実の基準とする:状態ファイルは、管理対象リソースの信頼できる情報源です
  • イミュータブルなインフラストラクチャ:リソースをその場で変更するのではなく、置き換えます

HCL 構文の基本

リソースブロック
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type

  tags = {
    Name        = "${var.project}-web"
    Environment = var.environment
  }
}
データソース
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  owners = ["099720109477"]  # Canonical
}
変数
variable "environment" {
  description = "Deployment environment (dev, staging, prod)"
  type        = string
  default     = "dev"

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Environment must be dev, staging, or prod."
  }
}

variable "instance_types" {
  description = "Map of environment to instance type"
  type        = map(string)
  default = {
    dev     = "t3.micro"
    staging = "t3.small"
    prod    = "t3.medium"
  }
}
出力
output "instance_ip" {
  description = "Public IP of the web instance"
  value       = aws_instance.web.public_ip
  sensitive   = false
}

output "database_password" {
  description = "Generated database password"
  value       = random_password.db.result
  sensitive   = true
}
ローカル値
locals {
  common_tags = {
    Project     = var.project
    Environment = var.environment
    ManagedBy   = "OpenTofu"
  }

  name_prefix = "${var.project}-${var.environment}"
}

メタ引数

count — 複数のインスタンスを作成
resource "aws_instance" "server" {
  count = var.server_count

  ami           = var.ami_id
  instance_type = var.instance_type

  tags = {
    Name = "${local.name_prefix}-server-${count.index}"
  }
}
for_each — マップまたはセットから作成
resource "aws_iam_user" "users" {
  for_each = toset(var.user_names)

  name = each.value
  path = "/users/"
}

resource "aws_security_group_rule" "ingress" {
  for_each = var.ingress_rules

  type              = "ingress"
  from_port         = each.value.port
  to_port           = each.value.port
  protocol          = each.value.protocol
  cidr_blocks       = each.value.cidr_blocks
  security_group_id = aws_security_group.main.id
}
depends_on — 依存関係を明示
resource "aws_instance" "app" {
  ami           = var.ami_id
  instance_type = var.instance_type

  depends_on = [
    aws_db_instance.database,
    aws_elasticache_cluster.cache
  ]
}
lifecycle — リソースの動作を制御
resource "aws_instance" "critical" {
  ami           = var.ami_id
  instance_type = var.instance_type

  lifecycle {
    prevent_destroy = true
    create_before_destroy = true
    ignore_changes = [
      tags["LastUpdated"],
      user_data
    ]
  }
}

# Replace when AMI changes
resource "aws_instance" "immutable" {
  ami           = var.ami_id
  instance_type = var.instance_type

  lifecycle {
    replace_triggered_by = [
      null_resource.ami_trigger
    ]
  }
}

モジュール設計

モジュールの構成
modules/
└── vpc/
    ├── main.tf          # Primary resources
    ├── variables.tf     # Input variables
    ├── outputs.tf       # Output values
    ├── versions.tf      # Required providers
    └── README.md        # Documentation
モジュールの呼び出し
module "vpc" {
  source = "./modules/vpc"

  cidr_block  = "10.0.0.0/16"
  environment = var.environment

  azs             = ["us-east-1a", "us-east-1b"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]
}

# Remote module with version
module "eks" {
  source  = "terraform-aws-modules/eks/aws"
  version = "~> 20.0"

  cluster_name    = local.cluster_name
  cluster_version = "1.29"

  vpc_id     = module.vpc.vpc_id
  subnet_ids = module.vpc.private_subnet_ids
}
モジュールのベストプラクティス
  • 変数のインターフェースは最小限で明確にする
  • 可能な場合は適切なデフォルト値を設定する
  • すべての変数と出力を文書化する
  • 汎用性を高めすぎた「万能」モジュールは避ける
  • 設定フラグよりも組み合わせを優先する
  • リモートモジュールのバージョンを固定する

状態管理

リモートバックエンド(S3)
terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/network/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}
OpenTofu の状態暗号化(独自機能)
terraform {
  encryption {
    key_provider "pbkdf2" "main" {
      passphrase = var.state_encryption_passphrase
    }

    method "aes_gcm" "encrypt" {
      keys = key_provider.pbkdf2.main
    }

    state {
      method   = method.aes_gcm.encrypt
      enforced = true
    }

    plan {
      method   = method.aes_gcm.encrypt
      enforced = true
    }
  }
}
状態コマンド
# List resources in state
tofu state list

# Show specific resource
tofu state show aws_instance.web

# Move resource (refactoring)
tofu state mv aws_instance.old aws_instance.new

# Remove from state (without destroying)
tofu state rm aws_instance.imported

# Import existing resource
tofu import aws_instance.web i-1234567890abcdef0

AWS プロバイダーの設定、認証方法、複数プロバイダーのパターンについては、プロバイダーの設定を参照してください。

ワークスペースやディレクトリベースの環境管理については、環境戦略を参照してください。

CLI ワークフロー

# Initialize working directory
tofu init

# Validate configuration
tofu validate

# Format code
tofu fmt -recursive

# Preview changes
tofu plan -out=plan.tfplan

# Apply changes
tofu apply plan.tfplan

# Destroy infrastructure
tofu destroy

# Show current state
tofu show

# Refresh state from actual infrastructure
tofu refresh

ベストプラクティスのチェックリスト

OpenTofu/Terraform コードを記述する際は、以下を確認してください。

  • チームで使用する場合は、ロック機能付きのリモートバックエンドを使用する
  • 状態の暗号化を有効にする(OpenTofu の機能)
  • .tfstate や、シークレットを含む .tfvars を VCS にコミットしない
  • プロバイダーとモジュールのバージョンを固定する
  • apply の前に毎回 tofu plan を実行する
  • 重要なリソースには lifecycle.prevent_destroy を使用する
  • すべての変数と出力を文書化する
  • 計算値やタグには locals を使用する
  • 名前付きリソースには count より for_each を優先する
  • 変数の制約には検証ブロックを使用する
  • シークレットはコードではなく、シークレットマネージャーに保存する

よくあるパターン

条件付きリソース
resource "aws_eip" "static" {
  count = var.create_elastic_ip ? 1 : 0

  instance = aws_instance.web.id
}
動的ブロック
resource "aws_security_group" "main" {
  name = "${local.name_prefix}-sg"

  dynamic "ingress" {
    for_each = var.ingress_rules
    content {
      from_port   = ingress.value.port
      to_port     = ingress.value.port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
    }
  }
}

参考資料

詳しいパターンと例については、以下をご覧ください。

コメント