mcpField
O
opentofu-coder

opentofu-coder

github.com/majesticlabs-dev云与 DevOps5069 次安装7 周前更新
代码仓库

关于 opentofu-coder

面向编写 OpenTofu 配置的开发者,提供 HCL 资源、模块、状态管理和 CLI 工作流参考。强调从扁平文件结构起步,并涵盖 OpenTofu 状态加密。

主要功能

  1. 01优先采用扁平文件结构
  2. 02提供 HCL 语法与资源示例
  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 编码

⚠️ 简单至上——默认使用扁平文件结构

始终从最简单的方案开始。只有在明确要求时,才增加复杂性。

简单方案(默认)与过度设计
方面✅ 简单方案(默认)❌ 过度设计
结构一个目录中的扁平 .tf 文件嵌套的 modules/ + environments/ 目录
模块无模块,或仅使用远程注册表模块为简单资源创建自定义本地模块
环境Workspaces 或单个 tfvars每个环境复制一份目录
变量内联默认值,尽量少用 tfvars复杂的变量层级结构
文件数量总共 3-5 个 .tf 文件嵌套目录中有 15 个以上文件
适用简单方案的情况(90% 的情况)
  • 每种类型管理 1-5 个资源
  • 单个 Provider、单个区域
  • 小团队或独立开发者
  • 标准基础设施模式
适合采用复杂方案的情况(10% 的情况)
  • 企业级多区域、多账户
  • 跨团队共享的可复用模块
  • 复杂的依赖链
  • 用户明确要求采用模块化结构

规则:如果 5 个扁平 .tf 文件就能定义所有内容,那就这么做。

简单的项目结构(默认)
infra/
├── main.tf           # All resources
├── variables.tf      # Input variables
├── outputs.tf        # Outputs
├── versions.tf       # Provider versions
└── terraform.tfvars  # Variable values (gitignored)

概述

OpenTofu 是由社区推动、采用 MPL-2.0 许可证的 Terraform 开源分支,由 Linux Foundation 维护。它使用 HashiCorp Configuration Language(HCL)跨云服务商以声明式方式管理基础设施。

核心理念

优先遵循以下原则:

  • 声明式优于命令式:描述期望的状态,而不是操作步骤
  • 幂等性:多次安全地应用,并得到相同结果
  • 模块化:通过可复用模块组合基础设施
  • 以状态为准:状态文件是受管理资源的事实来源
  • 不可变基础设施:替换资源,而不是就地修改

HCL 语法基础

Resource 块
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type

  tags = {
    Name        = "${var.project}-web"
    Environment = var.environment
  }
}
Data Source
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 - 根据 Map/Set 创建实例
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 Provider 设置、身份验证方法和多 Provider 模式,请参阅Provider 配置。

有关 Workspaces 和基于目录的环境管理,请参阅环境策略。

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
  • 固定 Provider 和模块的版本
  • 每次执行 apply 前先运行 tofu plan
  • 对关键资源使用 lifecycle.prevent_destroy
  • 记录所有变量和输出
  • 使用 locals 存储计算值和标签
  • 对具名资源优先使用 for_each,而不是 count
  • 使用验证块约束变量
  • 将密钥存储在密钥管理器中,而不是代码中

常见模式

条件资源
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
    }
  }
}

参考资料

如需了解详细模式和示例,请参阅:

评论

支持 Markdown