关于 opentofu-coder
面向编写 OpenTofu 配置的开发者,提供 HCL 资源、模块、状态管理和 CLI 工作流参考。强调从扁平文件结构起步,并涵盖 OpenTofu 状态加密。
主要功能
- 01优先采用扁平文件结构
- 02提供 HCL 语法与资源示例
- 03说明模块设计和状态管理
- 04介绍状态加密与 CLI 工作流
- 05包含基础设施最佳实践
适用场景
- 01为小型基础设施项目规划配置结构
- 02编写资源、变量和输出配置
- 03管理状态及其加密
- 04检查配置是否存在过度抽象
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ majesticlabs-dev/ majestic-marketplace --skill opentofu-coder安装后在 Claude Code 中输入/opentofu-coder确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/environment-strategies.mdhcl-patterns.mdmakefile-automation.mdpost-provisioning.mdproject-scaffolding.mdprovider-config.mdprovider-examples.mdstate-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
}
}
}
参考资料
如需了解详细模式和示例,请参阅:
- references/hcl-patterns.md - 高级 HCL 模式
- references/project-scaffolding.md - 目录结构、.gitignore、next_steps 输出、安全优先的变量
- references/post-provisioning.md - 用于基础设施部署后、部署前设置的 bin/setup-server 脚本
- references/state-management.md - 状态操作与加密
- references/provider-examples.md - 多云服务提供商配置
- references/makefile-automation.md - 用于 plan/apply/destroy 的 Makefile 工作流
评论
支持 Markdown