terraform-module-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTerraform Module Design Skill
Terraform模块设计技能
Table of Contents
目录
How to Implement → Step-by-Step | Examples
Help → Requirements | See Also
Purpose
目标
Master module design for creating reusable, testable, well-documented infrastructure components. Learn when to modularize, module structure patterns, input/output design, and composition strategies.
掌握模块设计方法,以创建可复用、可测试、文档完善的基础设施组件。学习何时进行模块化、模块结构模式、输入/输出设计以及组合策略。
When to Use
适用场景
Use this skill when you need to:
- Create reusable infrastructure patterns - Build modules for repeated resource groups
- Encapsulate complex configurations - Simplify multi-resource setups
- Standardize across projects - Ensure consistency in infrastructure
- Organize code for maintainability - Structure large Terraform projects
- Build composable systems - Combine modules to create larger architectures
- Version infrastructure components - Publish and version modules
When NOT to use:
- Single one-off resources
- Resources used only once
- No variation between uses
Trigger Phrases:
- "Create reusable Terraform module"
- "Design module structure"
- "Define module inputs and outputs"
- "Compose multiple modules"
- "Version Terraform module"
在以下场景中使用本技能:
- 创建可复用的基础设施模式 - 为重复出现的资源组构建模块
- 封装复杂配置 - 简化多资源部署配置
- 跨项目标准化 - 确保基础设施的一致性
- 整理代码提升可维护性 - 构建大型Terraform项目的结构
- 构建可组合系统 - 组合多个模块以创建更大型的架构
- 为基础设施组件版本化 - 发布并管理模块版本
不适用场景:
- 单一一次性资源
- 仅使用一次的资源
- 不同使用场景无差异的资源
触发关键词:
- "创建可复用Terraform模块"
- "设计模块结构"
- "定义模块输入和输出"
- "组合多个模块"
- "为Terraform模块版本化"
Quick Start
快速入门
Create a reusable Pub/Sub module in 5 minutes:
bash
undefined5分钟创建一个可复用的Pub/Sub模块:
bash
undefined1. Create module structure
1. Create module structure
mkdir -p modules/pubsub-topic
cd modules/pubsub-topic
mkdir -p modules/pubsub-topic
cd modules/pubsub-topic
2. Create files
2. Create files
cat > main.tf << 'EOF'
resource "google_pubsub_topic" "topic" {
name = var.topic_name
message_retention_duration = "${var.retention_days * 86400}s"
labels = var.labels
}
resource "google_pubsub_subscription" "subscription" {
name = "${var.topic_name}-sub"
topic = google_pubsub_topic.topic.name
dead_letter_policy {
dead_letter_topic = google_pubsub_topic.dlq.id
max_delivery_attempts = var.max_delivery_attempts
}
}
resource "google_pubsub_topic" "dlq" {
name = "${var.topic_name}-dlq"
}
EOF
cat > variables.tf << 'EOF'
variable "topic_name" {
type = string
description = "Name of the Pub/Sub topic"
}
variable "retention_days" {
type = number
default = 7
description = "Message retention in days"
}
variable "max_delivery_attempts" {
type = number
default = 5
description = "Max delivery attempts before DLQ"
}
variable "labels" {
type = map(string)
default = {}
description = "Resource labels"
}
EOF
cat > outputs.tf << 'EOF'
output "topic_name" {
value = google_pubsub_topic.topic.name
description = "Name of the Pub/Sub topic"
}
output "subscription_name" {
value = google_pubsub_subscription.subscription.name
description = "Name of the subscription"
}
EOF
cat > main.tf << 'EOF'
resource "google_pubsub_topic" "topic" {
name = var.topic_name
message_retention_duration = "${var.retention_days * 86400}s"
labels = var.labels
}
resource "google_pubsub_subscription" "subscription" {
name = "${var.topic_name}-sub"
topic = google_pubsub_topic.topic.name
dead_letter_policy {
dead_letter_topic = google_pubsub_topic.dlq.id
max_delivery_attempts = var.max_delivery_attempts
}
}
resource "google_pubsub_topic" "dlq" {
name = "${var.topic_name}-dlq"
}
EOF
cat > variables.tf << 'EOF'
variable "topic_name" {
type = string
description = "Name of the Pub/Sub topic"
}
variable "retention_days" {
type = number
default = 7
description = "Message retention in days"
}
variable "max_delivery_attempts" {
type = number
default = 5
description = "Max delivery attempts before DLQ"
}
variable "labels" {
type = map(string)
default = {}
description = "Resource labels"
}
EOF
cat > outputs.tf << 'EOF'
output "topic_name" {
value = google_pubsub_topic.topic.name
description = "Name of the Pub/Sub topic"
}
output "subscription_name" {
value = google_pubsub_subscription.subscription.name
description = "Name of the subscription"
}
EOF
3. Use module
3. Use module
cd ../..
cat > main.tf << 'EOF'
module "incoming_charges" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-incoming"
retention_days = 7
labels = {
environment = "production"
}
}
EOF
cd ../..
cat > main.tf << 'EOF'
module "incoming_charges" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-incoming"
retention_days = 7
labels = {
environment = "production"
}
}
EOF
4. Deploy
4. Deploy
terraform init
terraform apply
undefinedterraform init
terraform apply
undefinedInstructions
操作步骤
Step 1: Decide When to Create a Module
步骤1:确定是否需要创建模块
Create a Module When:
- ✅ Pattern repeats multiple times in your code
- ✅ Encapsulates complex resource group (5+ related resources)
- ✅ Has configurable inputs that vary per use
- ✅ Produces clear outputs for other modules to consume
Don't Create a Module For:
- ❌ Single one-off resources
- ❌ Resources used only once
- ❌ No variation between uses
- ❌ Over-engineering simple setup
Example: Pub/Sub topic + subscription + DLQ + IAM (4 resources, repeats twice)
→ Perfect candidate for a module!
应创建模块的场景:
- ✅ 模式在代码中多次重复出现
- ✅ 封装了复杂的资源组(5个及以上相关资源)
- ✅ 具有可配置的输入,且不同使用场景有差异
- ✅ 能生成清晰的输出供其他模块调用
不应创建模块的场景:
- ❌ 单一一次性资源
- ❌ 仅使用一次的资源
- ❌ 不同使用场景无差异的资源
- ❌ 对简单配置过度设计
示例: Pub/Sub主题 + 订阅 + DLQ + IAM(4个资源,重复出现两次)
→ 非常适合创建模块!
Step 2: Understand Module Structure
步骤2:理解模块结构
Standard Module Layout:
modules/pubsub-topic/
├── main.tf # Resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Provider requirements
└── README.md # DocumentationRoot Module (your main configuration):
terraform/
├── main.tf # Provider, variables
├── iam.tf # IAM resources
├── pubsub.tf # Pub/Sub resources
├── modules/ # Local modules
│ └── pubsub-topic/
├── .terraform.lock.hcl
└── terraform.tfvarsModule Paths:
hcl
undefined标准模块布局:
modules/pubsub-topic/
├── main.tf # Resources
├── variables.tf # Input variables
├── outputs.tf # Output values
├── versions.tf # Provider requirements
└── README.md # Documentation根模块(主配置文件):
terraform/
├── main.tf # Provider, variables
├── iam.tf # IAM resources
├── pubsub.tf # Pub/Sub resources
├── modules/ # Local modules
│ └── pubsub-topic/
├── .terraform.lock.hcl
└── terraform.tfvars模块路径:
hcl
undefinedLocal module
Local module
module "incoming" {
source = "./modules/pubsub-topic"
}
module "incoming" {
source = "./modules/pubsub-topic"
}
Remote module (GitHub)
Remote module (GitHub)
module "incoming" {
source = "github.com/org/terraform-modules/pubsub-topic"
version = "~> 1.0"
}
module "incoming" {
source = "github.com/org/terraform-modules/pubsub-topic"
version = "~> 1.0"
}
Terraform Registry
Terraform Registry
module "incoming" {
source = "hashicorp/vault/aws"
version = "~> 0.2"
}
undefinedmodule "incoming" {
source = "hashicorp/vault/aws"
version = "~> 0.2"
}
undefinedStep 3: Design Module Inputs (Variables)
步骤3:设计模块输入(变量)
Principles:
- Keep inputs simple and intuitive
- Use descriptive names
- Provide sensible defaults
- Validate constraints
Example - Pub/Sub Module:
hcl
undefined设计原则:
- 保持输入简单直观
- 使用描述性名称
- 提供合理的默认值
- 验证输入约束
示例 - Pub/Sub模块:
hcl
undefinedvariables.tf
variables.tf
variable "topic_name" {
description = "Name of the Pub/Sub topic"
type = string
validation {
condition = length(var.topic_name) > 0 && length(var.topic_name) <= 255
error_message = "Topic name must be 1-255 characters"
}
}
variable "retention_days" {
description = "Days to retain messages (0 = unlimited)"
type = number
default = 7
validation {
condition = var.retention_days >= 0 && var.retention_days <= 365
error_message = "Retention must be 0-365 days"
}
}
variable "labels" {
description = "Resource labels"
type = map(string)
default = {}
validation {
condition = alltrue([for k, v in var.labels : length(k) > 0 && length(v) > 0])
error_message = "Labels must have non-empty keys and values"
}
}
variable "enable_dlq" {
description = "Enable Dead Letter Queue"
type = bool
default = true
}
**Input Design Patterns**:
```hclvariable "topic_name" {
description = "Name of the Pub/Sub topic"
type = string
validation {
condition = length(var.topic_name) > 0 && length(var.topic_name) <= 255
error_message = "Topic name must be 1-255 characters"
}
}
variable "retention_days" {
description = "Days to retain messages (0 = unlimited)"
type = number
default = 7
validation {
condition = var.retention_days >= 0 && var.retention_days <= 365
error_message = "Retention must be 0-365 days"
}
}
variable "labels" {
description = "Resource labels"
type = map(string)
default = {}
validation {
condition = alltrue([for k, v in var.labels : length(k) > 0 && length(v) > 0])
error_message = "Labels must have non-empty keys and values"
}
}
variable "enable_dlq" {
description = "Enable Dead Letter Queue"
type = bool
default = true
}
**输入设计模式**:
```hclSimple inputs
Simple inputs
variable "name" { type = string }
variable "name" { type = string }
With defaults
With defaults
variable "replica_count" { type = number; default = 3 }
variable "replica_count" { type = number; default = 3 }
Lists
Lists
variable "allowed_ips" { type = list(string); default = [] }
variable "allowed_ips" { type = list(string); default = [] }
Maps (configuration objects)
Maps (configuration objects)
variable "config" {
type = map(object({
retention_days = number
dlq_enabled = bool
}))
}
variable "config" {
type = map(object({
retention_days = number
dlq_enabled = bool
}))
}
Flexible object
Flexible object
variable "topic_config" {
type = object({
retention_days = number
enable_dlq = bool
max_retries = number
})
default = {
retention_days = 7
enable_dlq = true
max_retries = 5
}
}
undefinedvariable "topic_config" {
type = object({
retention_days = number
enable_dlq = bool
max_retries = number
})
default = {
retention_days = 7
enable_dlq = true
max_retries = 5
}
}
undefinedStep 4: Design Module Outputs
步骤4:设计模块输出
Principles:
- Export only necessary values
- Use descriptive names
- Document what each output is
- Mark sensitive outputs
Example - Pub/Sub Module:
hcl
undefined设计原则:
- 仅导出必要的值
- 使用描述性名称
- 文档化每个输出的含义
- 标记敏感输出
示例 - Pub/Sub模块:
hcl
undefinedoutputs.tf
outputs.tf
output "topic_id" {
description = "Topic resource ID"
value = google_pubsub_topic.topic.id
}
output "topic_name" {
description = "Topic name"
value = google_pubsub_topic.topic.name
}
output "subscription_name" {
description = "Subscription name"
value = google_pubsub_subscription.subscription.name
}
output "dlq_topic_name" {
description = "Dead Letter Queue topic name"
value = google_pubsub_topic.dlq.name
}
output "topic_id" {
description = "Topic resource ID"
value = google_pubsub_topic.topic.id
}
output "topic_name" {
description = "Topic name"
value = google_pubsub_topic.topic.name
}
output "subscription_name" {
description = "Subscription name"
value = google_pubsub_subscription.subscription.name
}
output "dlq_topic_name" {
description = "Dead Letter Queue topic name"
value = google_pubsub_topic.dlq.name
}
Sensitive output
Sensitive output
output "configuration" {
description = "Complete module configuration"
value = {
topic_name = google_pubsub_topic.topic.name
dlq_name = google_pubsub_topic.dlq.name
}
sensitive = true
}
**Output Best Practices**:
```hcloutput "configuration" {
description = "Complete module configuration"
value = {
topic_name = google_pubsub_topic.topic.name
dlq_name = google_pubsub_topic.dlq.name
}
sensitive = true
}
**输出最佳实践**:
```hcl✅ GOOD: Specific, documented
✅ GOOD: Specific, documented
output "topic_id" {
description = "Google resource ID of the topic"
value = google_pubsub_topic.topic.id
}
output "topic_id" {
description = "Google resource ID of the topic"
value = google_pubsub_topic.topic.id
}
❌ BAD: Vague, undocumented
❌ BAD: Vague, undocumented
output "id" {
value = google_pubsub_topic.topic.id
}
output "id" {
value = google_pubsub_topic.topic.id
}
✅ GOOD: Sensible defaults to avoid null values
✅ GOOD: Sensible defaults to avoid null values
output "labels" {
description = "Applied labels"
value = merge(var.labels, { module = "pubsub" })
}
undefinedoutput "labels" {
description = "Applied labels"
value = merge(var.labels, { module = "pubsub" })
}
undefinedStep 5: Create Module Documentation
步骤5:创建模块文档
README.md Format:
markdown
undefinedREADME.md格式:
markdown
undefinedPubSub Topic Module
PubSub Topic Module
Creates a Pub/Sub topic with optional Dead Letter Queue.
Creates a Pub/Sub topic with optional Dead Letter Queue.
Usage
Usage
hcl
module "incoming_charges" {
source = "./modules/pubsub-topic"
topic_name = "charges-incoming"
retention_days = 7
enable_dlq = true
}hcl
module "incoming_charges" {
source = "./modules/pubsub-topic"
topic_name = "charges-incoming"
retention_days = 7
enable_dlq = true
}Arguments
Arguments
- (required): Name of the topic
topic_name - (optional): Message retention in days (default: 7)
retention_days - (optional): Enable Dead Letter Queue (default: true)
enable_dlq - (optional): Resource labels (default: {})
labels
- (required): Name of the topic
topic_name - (optional): Message retention in days (default: 7)
retention_days - (optional): Enable Dead Letter Queue (default: true)
enable_dlq - (optional): Resource labels (default: {})
labels
Outputs
Outputs
- : Topic resource ID
topic_id - : Topic name
topic_name - : Subscription name
subscription_name - : Dead Letter Queue topic name
dlq_topic_name
- : Topic resource ID
topic_id - : Topic name
topic_name - : Subscription name
subscription_name - : Dead Letter Queue topic name
dlq_topic_name
Example with Custom Configuration
Example with Custom Configuration
hcl
module "replies" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-replies"
retention_days = 3
enable_dlq = true
labels = {
environment = "production"
team = "charges"
}
}
output "replies_topic" {
value = module.replies.topic_name
}undefinedhcl
module "replies" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-replies"
retention_days = 3
enable_dlq = true
labels = {
environment = "production"
team = "charges"
}
}
output "replies_topic" {
value = module.replies.topic_name
}undefinedStep 6: Compose Modules
步骤6:组合模块
Module Composition: Combining modules to build larger systems.
hcl
undefined模块组合: 结合多个模块构建更大型的系统。
hcl
undefinedmain.tf - Compose multiple modules
main.tf - Compose multiple modules
module "incoming_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-incoming"
retention_days = 7
}
module "replies_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-replies"
retention_days = 3
}
module "dlq_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-dlq"
enable_dlq = false # Don't need DLQ for DLQ!
}
module "incoming_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-incoming"
retention_days = 7
}
module "replies_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-replies"
retention_days = 3
}
module "dlq_pubsub" {
source = "./modules/pubsub-topic"
topic_name = "supplier-charges-dlq"
enable_dlq = false # Don't need DLQ for DLQ!
}
Use outputs from one module as inputs to another
Use outputs from one module as inputs to another
module "iam_bindings" {
source = "./modules/pubsub-iam"
topics = [
module.incoming_pubsub.topic_name,
module.replies_pubsub.topic_name,
]
}
module "iam_bindings" {
source = "./modules/pubsub-iam"
topics = [
module.incoming_pubsub.topic_name,
module.replies_pubsub.topic_name,
]
}
Export composed outputs
Export composed outputs
output "topics" {
value = {
incoming = module.incoming_pubsub.topic_name
replies = module.replies_pubsub.topic_name
}
}
undefinedoutput "topics" {
value = {
incoming = module.incoming_pubsub.topic_name
replies = module.replies_pubsub.topic_name
}
}
undefinedStep 7: Version and Maintain Modules
步骤7:版本化与维护模块
Module Versioning (for published modules):
bash
undefined模块版本化(针对已发布的模块):
bash
undefinedgit tag for versions
git tag for versions
git tag v1.0.0
git push origin v1.0.0
git tag v1.0.0
git push origin v1.0.0
In code
In code
module "pubsub" {
source = "github.com/org/terraform-pubsub-module"
version = "~> 1.0"
}
**Semantic Versioning**:
- `1.0.0` - MAJOR.MINOR.PATCH
- `~> 1.0` - Allows 1.0.x, not 1.1.0
- `>= 1.0, < 2.0` - Allows any 1.x version
**Maintenance Checklist**:
- Update provider versions regularly
- Test module with new Terraform versions
- Document all breaking changes
- Provide migration guidesmodule "pubsub" {
source = "github.com/org/terraform-pubsub-module"
version = "~> 1.0"
}
**语义化版本控制**:
- `1.0.0` - 主版本.次版本.修订版本
- `~> 1.0` - 允许1.0.x版本,不允许1.1.0及以上
- `>= 1.0, < 2.0` - 允许任何1.x版本
**维护清单**:
- 定期更新Provider版本
- 使用新版本Terraform测试模块
- 文档化所有破坏性变更
- 提供迁移指南Examples
示例
Example 1: Simple Service Module
示例1:简单服务模块
hcl
undefinedhcl
undefinedmodules/gke-service/main.tf
modules/gke-service/main.tf
resource "kubernetes_namespace" "service" {
metadata {
name = var.namespace
}
}
resource "kubernetes_deployment" "service" {
metadata {
namespace = kubernetes_namespace.service.metadata[0].name
name = var.service_name
}
spec {
replicas = var.replicas
selector {
match_labels = {
app = var.service_name
}
}
template {
metadata {
labels = {
app = var.service_name
}
}
spec {
container {
name = var.service_name
image = var.image
env {
name = "PUBSUB_TOPIC"
value = var.pubsub_topic
}
}
}
}}
}
resource "kubernetes_namespace" "service" {
metadata {
name = var.namespace
}
}
resource "kubernetes_deployment" "service" {
metadata {
namespace = kubernetes_namespace.service.metadata[0].name
name = var.service_name
}
spec {
replicas = var.replicas
selector {
match_labels = {
app = var.service_name
}
}
template {
metadata {
labels = {
app = var.service_name
}
}
spec {
container {
name = var.service_name
image = var.image
env {
name = "PUBSUB_TOPIC"
value = var.pubsub_topic
}
}
}
}}
}
modules/gke-service/variables.tf
modules/gke-service/variables.tf
variable "namespace" { type = string }
variable "service_name" { type = string }
variable "image" { type = string }
variable "replicas" { type = number; default = 3 }
variable "pubsub_topic" { type = string }
variable "namespace" { type = string }
variable "service_name" { type = string }
variable "image" { type = string }
variable "replicas" { type = number; default = 3 }
variable "pubsub_topic" { type = string }
modules/gke-service/outputs.tf
modules/gke-service/outputs.tf
output "namespace" { value = kubernetes_namespace.service.metadata[0].name }
output "deployment_name" { value = kubernetes_deployment.service.metadata[0].name }
output "namespace" { value = kubernetes_namespace.service.metadata[0].name }
output "deployment_name" { value = kubernetes_deployment.service.metadata[0].name }
Usage
Usage
module "charges_service" {
source = "./modules/gke-service"
namespace = "production"
service_name = "supplier-charges"
image = "gcr.io/project/charges:1.0.0"
replicas = 3
pubsub_topic = module.pubsub.topic_name
}
undefinedmodule "charges_service" {
source = "./modules/gke-service"
namespace = "production"
service_name = "supplier-charges"
image = "gcr.io/project/charges:1.0.0"
replicas = 3
pubsub_topic = module.pubsub.topic_name
}
undefinedExample 2: Composition Pattern
示例2:组合模式
hcl
undefinedhcl
undefinedmain.tf - Compose multiple specialized modules
main.tf - Compose multiple specialized modules
module "pubsub" {
source = "./modules/pubsub-topic"
topic_name = "charges"
retention_days = 7
}
module "database" {
source = "./modules/cloud-sql"
instance_name = "charges-db"
database_name = "charges"
}
module "gke_service" {
source = "./modules/gke-service"
service_name = "charges-processor"
image = "gcr.io/project/processor:1.0"
pubsub_topic = module.pubsub.topic_name
db_connection = module.database.connection_string
}
module "pubsub" {
source = "./modules/pubsub-topic"
topic_name = "charges"
retention_days = 7
}
module "database" {
source = "./modules/cloud-sql"
instance_name = "charges-db"
database_name = "charges"
}
module "gke_service" {
source = "./modules/gke-service"
service_name = "charges-processor"
image = "gcr.io/project/processor:1.0"
pubsub_topic = module.pubsub.topic_name
db_connection = module.database.connection_string
}
Export all outputs
Export all outputs
output "infrastructure" {
value = {
pubsub_topic = module.pubsub.topic_name
database_host = module.database.host
service_name = module.gke_service.deployment_name
}
}
undefinedoutput "infrastructure" {
value = {
pubsub_topic = module.pubsub.topic_name
database_host = module.database.host
service_name = module.gke_service.deployment_name
}
}
undefinedExample 3: Dynamic Module Usage with for_each
示例3:使用for_each动态调用模块
hcl
undefinedhcl
undefinedmain.tf
main.tf
locals {
pubsub_topics = {
"incoming" = {
retention_days = 7
}
"replies" = {
retention_days = 3
}
"events" = {
retention_days = 14
}
}
}
module "topics" {
for_each = local.pubsub_topics
source = "./modules/pubsub-topic"
topic_name = each.key
retention_days = each.value.retention_days
}
locals {
pubsub_topics = {
"incoming" = {
retention_days = 7
}
"replies" = {
retention_days = 3
}
"events" = {
retention_days = 14
}
}
}
module "topics" {
for_each = local.pubsub_topics
source = "./modules/pubsub-topic"
topic_name = each.key
retention_days = each.value.retention_days
}
Access module outputs
Access module outputs
output "topic_names" {
value = {
for name, module in module.topics :
name => module.topic_name
}
}
undefinedoutput "topic_names" {
value = {
for name, module in module.topics :
name => module.topic_name
}
}
undefinedRequirements
要求
- Terraform 1.x+
- Module should have clear input/output contracts
- Good README documentation
- Tested with typical use cases
- Terraform 1.x+
- 模块应具备清晰的输入/输出契约
- 完善的README文档
- 已通过典型用例测试
See Also
相关链接
- terraform skill - General Terraform
- terraform-gcp-integration - GCP patterns
- terraform-state-management - State handling
- terraform skill - 通用Terraform技能
- terraform-gcp-integration - GCP集成模式
- terraform-state-management - 状态管理