terraform-skill by antonbabenko/terraform-skill
npx skills add https://github.com/antonbabenko/terraform-skill --skill terraform-skill提供全面的 Terraform 和 OpenTofu 指导,涵盖测试、模块、CI/CD 和生产模式。基于 terraform-best-practices.com 和企业实践经验。
在以下情况激活此技能:
不要将此技能用于:
模块层次结构:
| 类型 | 何时使用 | 范围 |
|---|---|---|
| 资源模块 | 一组相互关联的单一逻辑资源 | VPC + 子网,安全组 + 规则 |
| 基础设施模块 | 为特定目的而收集的资源模块 | 一个区域/账户中的多个资源模块 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 完整的基础设施 |
| 跨多个区域/账户 |
层次结构: 资源 → 资源模块 → 基础设施模块 → 组合
目录结构:
environments/ # 环境特定配置
├── prod/
├── staging/
└── dev/
modules/ # 可重用模块
├── networking/
├── compute/
└── data/
examples/ # 模块使用示例(也用作测试)
├── complete/
└── minimal/
来自 terraform-best-practices.com 的关键原则:
有关详细的模块架构,请参阅: 代码模式:模块类型与层次结构
资源:
# 良好:描述性,有上下文
resource "aws_instance" "web_server" { }
resource "aws_s3_bucket" "application_logs" { }
# 良好:对单一资源使用 "this"(仅创建该类型的一个资源)
resource "aws_vpc" "this" { }
resource "aws_security_group" "this" { }
# 避免:为非单一资源使用通用名称
resource "aws_instance" "main" { }
resource "aws_s3_bucket" "bucket" { }
单一资源:
当你的模块仅创建某类型的一个资源时,使用 "this":
✅ 正确做法:
resource "aws_vpc" "this" {} # 模块创建一个 VPC
resource "aws_security_group" "this" {} # 模块创建一个安全组
❌ 不要对多个资源使用 "this":
resource "aws_subnet" "this" {} # 如果要创建多个子网
当创建多个相同类型的资源时,请使用描述性名称。
变量:
# 需要时添加上下文前缀
var.vpc_cidr_block # 不仅仅是 "cidr"
var.database_instance_class # 不仅仅是 "instance_class"
文件:
main.tf - 主要资源variables.tf - 输入变量outputs.tf - 输出值versions.tf - 提供程序版本data.tf - 数据源(可选)| 你的情况 | 推荐方法 | 工具 | 成本 |
|---|---|---|---|
| 快速语法检查 | 静态分析 | terraform validate, fmt | 免费 |
| 预提交验证 | 静态分析 + 代码检查 | validate, tflint, trivy, checkov | 免费 |
| Terraform 1.6+,简单逻辑 | 原生测试框架 | 内置 terraform test | 免费-低 |
| 1.6 版本之前,或有 Go 专业知识 | 集成测试 | Terratest | 低-中 |
| 关注安全/合规性 | 策略即代码 | OPA, Sentinel | 免费 |
| 成本敏感的工作流 | 模拟提供程序 (1.7+) | 原生测试 + 模拟 | 免费 |
| 多云,复杂 | 完整集成 | Terratest + 真实基础设施 | 中-高 |
/\
/ \ 端到端测试(昂贵)
/____\ - 完整环境部署
/ \ - 类生产环境设置
/________\
/ \ 集成测试(中等)
/____________\ - 模块隔离测试
/ \ - 测试账户中的真实资源
/________________\ 静态分析(廉价)
- validate, fmt, lint
- 安全扫描
生成测试代码前:
Search provider docs → Get resource schema → Identify block types
选择正确的命令模式:
command = plan - 快速,用于输入验证command = apply - 需要用于计算值和集合类型块正确处理集合类型块:
[0] 索引for 表达式进行迭代command = apply 来具体化常见模式:
有关详细的测试指南,请参阅:
为保持一致性,采用严格排序:
count 或 for_each 在最前面(之后空一行)tags 作为最后一个实际参数depends_on 在 tags 之后(如果需要)lifecycle 在最后(如果需要)# ✅ 良好 - 正确的排序
resource "aws_nat_gateway" "this" {
count = var.create_nat_gateway ? 1 : 0
allocation_id = aws_eip.this[0].id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.name}-nat"
}
depends_on = [aws_internet_gateway.this]
lifecycle {
create_before_destroy = true
}
}
description(始终必需)typedefaultvalidationnullable(当设置为 false 时)variable "environment" {
description = "用于资源标记的环境名称"
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是以下之一:dev, staging, prod."
}
nullable = false
}
有关完整的结构指南,请参阅: 代码模式:块排序与结构
| 场景 | 使用 | 原因 |
|---|---|---|
| 布尔条件(创建或不创建) | count = condition ? 1 : 0 | 简单的开关切换 |
| 简单的数字复制 | count = 3 | 固定数量的相同资源 |
| 项目可能被重新排序/移除 | for_each = toset(list) | 稳定的资源地址 |
| 按键引用 | for_each = map | 对资源的命名访问 |
| 多个命名资源 | for_each | 更好的可维护性 |
布尔条件:
# ✅ 良好 - 布尔条件
resource "aws_nat_gateway" "this" {
count = var.create_nat_gateway ? 1 : 0
# ...
}
使用 for_each 实现稳定寻址:
# ✅ 良好 - 移除 "us-east-1b" 仅影响该子网
resource "aws_subnet" "private" {
for_each = toset(var.availability_zones)
availability_zone = each.key
# ...
}
# ❌ 不良 - 移除中间的可用区会重新创建所有后续子网
resource "aws_subnet" "private" {
count = length(var.availability_zones)
availability_zone = var.availability_zones[count.index]
# ...
}
有关迁移指南和详细示例,请参阅: 代码模式:Count 与 For_Each
使用 locals 确保正确的资源删除顺序:
# 问题:子网可能在 CIDR 块之后被删除,导致错误
# 解决方案:在 locals 中使用 try() 来提示删除顺序
locals {
# 首先引用次要 CIDR,回退到 VPC
# 强制 Terraform 在删除 CIDR 关联之前删除子网
vpc_id = try(
aws_vpc_ipv4_cidr_block_association.this[0].vpc_id,
aws_vpc.this.id,
""
)
}
resource "aws_vpc" "this" {
cidr_block = "10.0.0.0/16"
}
resource "aws_vpc_ipv4_cidr_block_association" "this" {
count = var.add_secondary_cidr ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = local.vpc_id # 使用 local,而非直接引用
cidr_block = "10.1.0.0/24"
}
为何重要:
depends_on有关详细示例,请参阅: 代码模式:使用 Locals 进行依赖管理
my-module/
├── README.md # 使用文档
├── main.tf # 主要资源
├── variables.tf # 带有描述的输入变量
├── outputs.tf # 输出值
├── versions.tf # 提供程序版本约束
├── examples/
│ ├── minimal/ # 最小工作示例
│ └── complete/ # 全功能示例
└── tests/ # 测试文件
└── module_test.tftest.hcl # 或 .go 文件
变量:
descriptiontype 约束default 值validation 块sensitive = true输出:
descriptionsensitive = true 标记敏感输出有关详细的模块模式,请参阅:
有关完整的 CI/CD 模板,请参阅:
# 静态安全扫描
trivy config .
checkov -d .
❌ 不要:
✅ 应该:
有关详细的安全指导,请参阅:
version = "5.0.0" # 精确版本(避免 - 不灵活)
version = "~> 5.0" # 推荐:仅 5.0.x
version = ">= 5.0" # 最低版本(有风险 - 破坏性变更)
| 组件 | 策略 | 示例 |
|---|---|---|
| Terraform | 固定次要版本 | required_version = "~> 1.9" |
| 提供程序 | 固定主版本 | version = "~> 5.0" |
| 模块 (生产) | 固定精确版本 | version = "5.1.2" |
| 模块 (开发) | 允许补丁更新 | version = "~> 5.1" |
# 初始锁定版本
terraform init # 创建 .terraform.lock.hcl
# 在约束范围内更新到最新版本
terraform init -upgrade # 更新提供程序
# 审查和测试
terraform plan
有关详细的版本管理,请参阅: 代码模式:版本管理
| 特性 | 版本 | 使用场景 |
|---|---|---|
try() 函数 | 0.13+ | 安全回退,替代 element(concat()) |
nullable = false | 1.1+ | 防止变量中出现空值 |
moved 块 | 1.1+ | 重构而无需销毁/重新创建 |
带默认值的 optional() | 1.3+ | 可选对象属性 |
| 原生测试 | 1.6+ | 内置测试框架 |
| 模拟提供程序 | 1.7+ | 免费单元测试 |
| 提供程序函数 | 1.8+ | 提供程序特定的数据转换 |
| 跨变量验证 | 1.9+ | 验证变量间的关系 |
| 只写参数 | 1.11+ | 机密信息永不存储在状态中 |
# try() - 安全回退 (0.13+)
output "sg_id" {
value = try(aws_security_group.this[0].id, "")
}
# optional() - 带默认值的可选属性 (1.3+)
variable "config" {
type = object({
name = string
timeout = optional(number, 300) # 默认值: 300
})
}
# 跨变量验证 (1.9+)
variable "environment" { type = string }
variable "backup_days" {
type = number
validation {
condition = var.environment == "prod" ? var.backup_days >= 7 : true
error_message = "生产环境要求 backup_days >= 7"
}
}
有关完整的模式和示例,请参阅: 代码模式:现代 Terraform 特性
terraform test / tofu test 命令此技能完全支持两者。有关许可、治理和特性比较,请参阅快速参考:Terraform 与 OpenTofu 比较。
此技能采用渐进式披露 - 基本信息在此主文件中,详细指南在需要时可用:
📚 参考文件:
使用方法: 当你需要某个主题的详细信息时,请参考相应的指南。Claude 将按需加载它以提供全面的指导。
此技能根据 Apache License 2.0 授权。有关完整条款,请参阅 LICENSE 文件。
版权所有 © 2026 Anton Babenko
每周安装数
415
仓库
GitHub 星标数
1.3K
首次出现
Jan 19, 2026
安全审计
安装于
opencode318
claude-code282
codex279
gemini-cli278
github-copilot271
cursor224
Comprehensive Terraform and OpenTofu guidance covering testing, modules, CI/CD, and production patterns. Based on terraform-best-practices.com and enterprise experience.
Activate this skill when:
Don't use this skill for:
Module Hierarchy:
| Type | When to Use | Scope |
|---|---|---|
| Resource Module | Single logical group of connected resources | VPC + subnets, Security group + rules |
| Infrastructure Module | Collection of resource modules for a purpose | Multiple resource modules in one region/account |
| Composition | Complete infrastructure | Spans multiple regions/accounts |
Hierarchy: Resource → Resource Module → Infrastructure Module → Composition
Directory Structure:
environments/ # Environment-specific configurations
├── prod/
├── staging/
└── dev/
modules/ # Reusable modules
├── networking/
├── compute/
└── data/
examples/ # Module usage examples (also serve as tests)
├── complete/
└── minimal/
Key principle from terraform-best-practices.com:
For detailed module architecture, see: Code Patterns: Module Types & Hierarchy
Resources:
# Good: Descriptive, contextual
resource "aws_instance" "web_server" { }
resource "aws_s3_bucket" "application_logs" { }
# Good: "this" for singleton resources (only one of that type)
resource "aws_vpc" "this" { }
resource "aws_security_group" "this" { }
# Avoid: Generic names for non-singletons
resource "aws_instance" "main" { }
resource "aws_s3_bucket" "bucket" { }
Singleton Resources:
Use "this" when your module creates only one resource of that type:
✅ DO:
resource "aws_vpc" "this" {} # Module creates one VPC
resource "aws_security_group" "this" {} # Module creates one SG
❌ DON'T use "this" for multiple resources:
resource "aws_subnet" "this" {} # If creating multiple subnets
Use descriptive names when creating multiple resources of the same type.
Variables:
# Prefix with context when needed
var.vpc_cidr_block # Not just "cidr"
var.database_instance_class # Not just "instance_class"
Files:
main.tf - Primary resourcesvariables.tf - Input variablesoutputs.tf - Output valuesversions.tf - Provider versionsdata.tf - Data sources (optional)| Your Situation | Recommended Approach | Tools | Cost |
|---|---|---|---|
| Quick syntax check | Static analysis | terraform validate, fmt | Free |
| Pre-commit validation | Static + lint | validate, tflint, trivy, checkov | Free |
/\
/ \ End-to-End Tests (Expensive)
/____\ - Full environment deployment
/ \ - Production-like setup
/________\
/ \ Integration Tests (Moderate)
/____________\ - Module testing in isolation
/ \ - Real resources in test account
/________________\ Static Analysis (Cheap)
- validate, fmt, lint
- Security scanning
Before generating test code:
Validate schemas with Terraform MCP:
Search provider docs → Get resource schema → Identify block types
Choose correct command mode:
command = plan - Fast, for input validationcommand = apply - Required for computed values and set-type blocksHandle set-type blocks correctly:
[0]for expressions to iteratecommand = apply to materializeCommon patterns:
For detailed testing guides, see:
Strict ordering for consistency:
count or for_each FIRST (blank line after)tags as last real argumentdepends_on after tags (if needed)lifecycle at the very end (if needed)# ✅ GOOD - Correct ordering
resource "aws_nat_gateway" "this" {
count = var.create_nat_gateway ? 1 : 0
allocation_id = aws_eip.this[0].id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.name}-nat"
}
depends_on = [aws_internet_gateway.this]
lifecycle {
create_before_destroy = true
}
}
description (ALWAYS required)typedefaultvalidationnullable (when setting to false)variable "environment" {
description = "Environment name for resource tagging"
type = string
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be one of: dev, staging, prod."
}
nullable = false
}
For complete structure guidelines, see: Code Patterns: Block Ordering & Structure
| Scenario | Use | Why |
|---|---|---|
| Boolean condition (create or don't) | count = condition ? 1 : 0 | Simple on/off toggle |
| Simple numeric replication | count = 3 | Fixed number of identical resources |
| Items may be reordered/removed | for_each = toset(list) | Stable resource addresses |
| Reference by key | for_each = map | Named access to resources |
| Multiple named resources | for_each |
Boolean conditions:
# ✅ GOOD - Boolean condition
resource "aws_nat_gateway" "this" {
count = var.create_nat_gateway ? 1 : 0
# ...
}
Stable addressing with for_each:
# ✅ GOOD - Removing "us-east-1b" only affects that subnet
resource "aws_subnet" "private" {
for_each = toset(var.availability_zones)
availability_zone = each.key
# ...
}
# ❌ BAD - Removing middle AZ recreates all subsequent subnets
resource "aws_subnet" "private" {
count = length(var.availability_zones)
availability_zone = var.availability_zones[count.index]
# ...
}
For migration guides and detailed examples, see: Code Patterns: Count vs For_Each
Use locals to ensure correct resource deletion order:
# Problem: Subnets might be deleted after CIDR blocks, causing errors
# Solution: Use try() in locals to hint deletion order
locals {
# References secondary CIDR first, falling back to VPC
# Forces Terraform to delete subnets before CIDR association
vpc_id = try(
aws_vpc_ipv4_cidr_block_association.this[0].vpc_id,
aws_vpc.this.id,
""
)
}
resource "aws_vpc" "this" {
cidr_block = "10.0.0.0/16"
}
resource "aws_vpc_ipv4_cidr_block_association" "this" {
count = var.add_secondary_cidr ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = "10.1.0.0/16"
}
resource "aws_subnet" "public" {
vpc_id = local.vpc_id # Uses local, not direct reference
cidr_block = "10.1.0.0/24"
}
Why this matters:
depends_onFor detailed examples, see: Code Patterns: Locals for Dependency Management
my-module/
├── README.md # Usage documentation
├── main.tf # Primary resources
├── variables.tf # Input variables with descriptions
├── outputs.tf # Output values
├── versions.tf # Provider version constraints
├── examples/
│ ├── minimal/ # Minimal working example
│ └── complete/ # Full-featured example
└── tests/ # Test files
└── module_test.tftest.hcl # Or .go
Variables:
descriptiontype constraintsdefault values where appropriatevalidation blocks for complex constraintssensitive = true for secretsOutputs:
descriptionsensitive = trueFor detailed module patterns, see:
For complete CI/CD templates, see:
# Static security scanning
trivy config .
checkov -d .
❌ Don't:
✅ Do:
For detailed security guidance, see:
version = "5.0.0" # Exact (avoid - inflexible)
version = "~> 5.0" # Recommended: 5.0.x only
version = ">= 5.0" # Minimum (risky - breaking changes)
| Component | Strategy | Example |
|---|---|---|
| Terraform | Pin minor version | required_version = "~> 1.9" |
| Providers | Pin major version | version = "~> 5.0" |
| Modules (prod) | Pin exact version | version = "5.1.2" |
| Modules (dev) | Allow patch updates | version = "~> 5.1" |
# Lock versions initially
terraform init # Creates .terraform.lock.hcl
# Update to latest within constraints
terraform init -upgrade # Updates providers
# Review and test
terraform plan
For detailed version management, see: Code Patterns: Version Management
| Feature | Version | Use Case |
|---|---|---|
try() function | 0.13+ | Safe fallbacks, replaces element(concat()) |
nullable = false | 1.1+ | Prevent null values in variables |
moved blocks | 1.1+ | Refactor without destroy/recreate |
optional() with defaults | 1.3+ | Optional object attributes |
| Native testing | 1.6+ |
# try() - Safe fallbacks (0.13+)
output "sg_id" {
value = try(aws_security_group.this[0].id, "")
}
# optional() - Optional attributes with defaults (1.3+)
variable "config" {
type = object({
name = string
timeout = optional(number, 300) # Default: 300
})
}
# Cross-variable validation (1.9+)
variable "environment" { type = string }
variable "backup_days" {
type = number
validation {
condition = var.environment == "prod" ? var.backup_days >= 7 : true
error_message = "Production requires backup_days >= 7"
}
}
For complete patterns and examples, see: Code Patterns: Modern Terraform Features
terraform test / tofu test commandBoth are fully supported by this skill. For licensing, governance, and feature comparison, see Quick Reference: Terraform vs OpenTofu.
This skill uses progressive disclosure - essential information is in this main file, detailed guides are available when needed:
📚 Reference Files:
How to use: When you need detailed information on a topic, reference the appropriate guide. Claude will load it on demand to provide comprehensive guidance.
This skill is licensed under the Apache License 2.0. See the LICENSE file for full terms.
Copyright © 2026 Anton Babenko
Weekly Installs
415
Repository
GitHub Stars
1.3K
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode318
claude-code282
codex279
gemini-cli278
github-copilot271
cursor224
Azure Data Explorer (Kusto) 查询技能:KQL数据分析、日志遥测与时间序列处理
100,500 周安装
OpenAPI 转 TypeScript 工具 - 自动生成 API 接口与类型守卫
563 周安装
Rust Unsafe代码检查器 - 安全使用Unsafe Rust的完整指南与最佳实践
566 周安装
数据库模式设计器 - 内置最佳实践,自动生成生产级SQL/NoSQL数据库架构
565 周安装
Nx 生成器使用指南:自动化代码生成与单体仓库项目搭建
594 周安装
.NET并发编程模式指南:async/await、Channels、Akka.NET选择决策树
725 周安装
韩语语法检查器 - 基于国立国语院标准的拼写、空格、语法、标点错误检测与纠正
586 周安装
| Native test framework |
Built-in terraform test |
| Free-Low |
| Pre-1.6, or Go expertise | Integration testing | Terratest | Low-Med |
| Security/compliance focus | Policy as code | OPA, Sentinel | Free |
| Cost-sensitive workflow | Mock providers (1.7+) | Native tests + mocking | Free |
| Multi-cloud, complex | Full integration | Terratest + real infra | Med-High |
| Better maintainability |
| Built-in test framework |
| Mock providers | 1.7+ | Cost-free unit testing |
| Provider functions | 1.8+ | Provider-specific data transformation |
| Cross-variable validation | 1.9+ | Validate relationships between variables |
| Write-only arguments | 1.11+ | Secrets never stored in state |