Giao diện
💾 Module Design
Level: Core Solves: Thiết kế Terraform modules tái sử dụng, maintainable, và scalable cho enterprise
🎯 Mục tiêu (Outcomes)
Sau khi áp dụng kiến thức trong trang này, bạn sẽ có khả năng:
- Thiết kế Module Interface với variables và outputs rõ ràng
- Áp dụng Module Composition đúng cách
- Sử dụng Semantic Versioning cho modules
- Viết Documentation chuẩn cho modules
- Tránh Anti-patterns thường gặp
- Test Modules trước khi deploy
✅ Khi nào dùng
| Pattern | Use Case | Lý do |
|---|---|---|
| Shared modules | Reusable infra | DRY, consistency |
| Composition | Complex systems | Separation of concerns |
| Registry modules | Team-wide standards | Central management |
| Local modules | Project-specific | Quick iteration |
❌ Khi nào KHÔNG dùng
| Pattern | Vấn đề | Thay thế |
|---|---|---|
| Module cho 1 resource | Overkill | Inline resource |
| "God" module | Too complex | Split modules |
| No versioning | Breaking changes | SemVer |
| Copy-paste modules | Drift | Registry/Git source |
⚠️ Cảnh báo từ Raizo
"Team có 100 projects, mỗi project copy-paste VPC code. Security fix cần update tất cả manually. 3 tuần để patch. Modules + versioning giải quyết trong 1 ngày."
Tại sao cần Modules?
💡 Giáo sư Tom
Copy-paste Terraform code là technical debt. Modules là cách DRY (Don't Repeat Yourself) trong IaC. Một module tốt giống như một function tốt - single responsibility, clear interface, và well-tested.
Vấn đề với Copy-Paste
┌─────────────────────────────────────────────────────────────────┐
│ COPY-PASTE CHAOS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Project A Project B Project C │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ vpc.tf │ │ vpc.tf │ │ vpc.tf │ │
│ │ (v1) │ │ (v1.1) │ │ (v1.2) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ PROBLEMS: │
│ ❌ Security fix needed? Update ALL projects manually │
│ ❌ Different versions = different behaviors │
│ ❌ No single source of truth │
│ ❌ Knowledge scattered across projects │
│ │
└─────────────────────────────────────────────────────────────────┘Module Solution
┌─────────────────────────────────────────────────────────────────┐
│ MODULE SOLUTION │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ VPC Module │ │
│ │ (versioned) │ │
│ │ v2.1.0 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Project A │ │ Project B │ │ Project C │ │
│ │ uses v2.1 │ │ uses v2.1 │ │ uses v2.0 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ BENEFITS: │
│ ✅ Single source of truth │
│ ✅ Version control for infrastructure │
│ ✅ Security fixes propagate via version bumps │
│ ✅ Consistent patterns across organization │
│ │
└─────────────────────────────────────────────────────────────────┘Module Structure
Standard Module Layout
modules/vpc/
├── main.tf # Primary resources
├── variables.tf # Input variables (interface)
├── outputs.tf # Output values (interface)
├── versions.tf # Provider requirements
├── locals.tf # Local values
├── README.md # Documentation
├── examples/ # Usage examples
│ ├── simple/
│ └── complete/
└── tests/ # Module tests
└── vpc_test.goModule Interface Design
hcl
# variables.tf - Module inputs (the "API")
variable "name" {
description = "Name prefix for all resources"
type = string
validation {
condition = length(var.name) <= 20
error_message = "Name must be 20 characters or less."
}
}
variable "cidr_block" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
validation {
condition = can(cidrhost(var.cidr_block, 0))
error_message = "Must be a valid CIDR block."
}
}
variable "availability_zones" {
description = "List of AZs to use"
type = list(string)
default = []
}
variable "enable_nat_gateway" {
description = "Enable NAT Gateway for private subnets"
type = bool
default = true
}
variable "single_nat_gateway" {
description = "Use single NAT Gateway (cost saving for non-prod)"
type = bool
default = false
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}hcl
# outputs.tf - Module outputs (the "return values")
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "nat_gateway_ips" {
description = "Public IPs of NAT Gateways"
value = aws_eip.nat[*].public_ip
}
output "vpc_cidr_block" {
description = "CIDR block of the VPC"
value = aws_vpc.main.cidr_block
}Module Composition
Root Module Calling Child Modules
hcl
# environments/prod/main.tf
module "vpc" {
source = "../../modules/vpc"
name = "prod"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b", "us-east-1c"]
enable_nat_gateway = true
single_nat_gateway = false # HA for production
tags = local.common_tags
}
module "eks" {
source = "../../modules/eks"
cluster_name = "prod-cluster"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
tags = local.common_tags
}
module "rds" {
source = "../../modules/rds"
identifier = "prod-db"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
tags = local.common_tags
}Module Composition Pattern
Module Versioning
Semantic Versioning
MAJOR.MINOR.PATCH
v2.1.3
│ │ │
│ │ └── Patch: Bug fixes, no interface changes
│ └──── Minor: New features, backward compatible
└────── Major: Breaking changesVersion Constraints
hcl
# Exact version (most restrictive)
module "vpc" {
source = "company/vpc/aws"
version = "2.1.3"
}
# Pessimistic constraint (recommended)
module "vpc" {
source = "company/vpc/aws"
version = "~> 2.1" # >= 2.1.0, < 3.0.0
}
# Range constraint
module "vpc" {
source = "company/vpc/aws"
version = ">= 2.0, < 3.0"
}Module Sources
hcl
# Local path
module "vpc" {
source = "../../modules/vpc"
}
# Terraform Registry
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
}
# GitHub
module "vpc" {
source = "github.com/company/terraform-modules//vpc?ref=v2.1.0"
}
# S3 (private modules)
module "vpc" {
source = "s3::https://s3-us-east-1.amazonaws.com/company-modules/vpc.zip"
}
# Git over SSH
module "vpc" {
source = "git@github.com:company/terraform-modules.git//vpc?ref=v2.1.0"
}Module Best Practices
1. Single Responsibility
hcl
# ❌ BAD: Module does too much
module "infrastructure" {
source = "./modules/everything"
# Creates VPC, EKS, RDS, S3, IAM, CloudWatch...
}
# ✅ GOOD: Focused modules
module "vpc" {
source = "./modules/vpc"
}
module "eks" {
source = "./modules/eks"
vpc_id = module.vpc.vpc_id
}2. Sensible Defaults
hcl
# ✅ GOOD: Secure defaults, override when needed
variable "enable_encryption" {
description = "Enable encryption at rest"
type = bool
default = true # Secure by default
}
variable "public_access" {
description = "Allow public access"
type = bool
default = false # Private by default
}3. Input Validation
hcl
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_type" {
description = "EC2 instance type"
type = string
validation {
condition = can(regex("^t3\\.", var.instance_type))
error_message = "Only t3 instance types are allowed."
}
}4. Conditional Resources
hcl
# Create NAT Gateway only if enabled
resource "aws_nat_gateway" "main" {
count = var.enable_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.availability_zones)) : 0
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
}5. Dynamic Blocks
hcl
resource "aws_security_group" "main" {
name = var.name
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}Module Documentation
README Template
markdown
# VPC Module
Creates a VPC with public and private subnets across multiple AZs.
## Usage
```hcl
module "vpc" {
source = "company/vpc/aws"
version = "2.1.0"
name = "production"
cidr_block = "10.0.0.0/16"
availability_zones = ["us-east-1a", "us-east-1b"]
enable_nat_gateway = true
}Requirements
| Name | Version |
|---|---|
| terraform | >= 1.0 |
| aws | >= 5.0 |
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| name | Name prefix | string | - | yes |
| cidr_block | VPC CIDR | string | "10.0.0.0/16" | no |
Outputs
| Name | Description |
|---|---|
| vpc_id | ID of the VPC |
| public_subnet_ids | IDs of public subnets |
## Anti-Patterns to Avoid
### ❌ Hardcoded Values
```hcl
# BAD
resource "aws_instance" "web" {
ami = "ami-0123456789" # Hardcoded AMI
instance_type = "t3.micro" # Hardcoded type
subnet_id = "subnet-abc123" # Hardcoded subnet
}
# GOOD
resource "aws_instance" "web" {
ami = var.ami_id
instance_type = var.instance_type
subnet_id = var.subnet_id
}❌ Provider in Module
hcl
# BAD - Provider in module
# modules/vpc/main.tf
provider "aws" {
region = "us-east-1"
}
# GOOD - Provider in root module only
# environments/prod/providers.tf
provider "aws" {
region = "us-east-1"
}❌ Overly Generic Modules
hcl
# BAD - Too many conditionals
variable "resource_type" {
description = "Type of resource to create"
# Creates different resources based on this...
}
## Best Practices Checklist
- [ ] Single responsibility per module
- [ ] Clear variable descriptions
- [ ] Input validation
- [ ] Sensible secure defaults
- [ ] Version constraints (SemVer)
- [ ] README with examples
- [ ] No providers in modules
- [ ] Outputs for composition
## ⚖️ Trade-offs
### Trade-off 1: Module Granularity
| Granularity | Reusability | Complexity |
|-------------|-------------|------------|
| **Fine** (1-3 resources) | High | Low per module, high overall |
| **Medium** (5-10 resources) | Balanced | Balanced |
| **Coarse** (20+ resources) | Low | High per module |
**Khuyến nghị**: Medium granularity - một module = một logical component.
---
### Trade-off 2: Module Sources
| Source | Speed | Security | Versioning |
|--------|-------|----------|------------|
| **Local path** | Fast | N/A | Git |
| **Git** | Medium | SSH keys | Tags |
| **Registry (public)** | Fast | Vetted | SemVer |
| **Registry (private)** | Fast | Controlled | SemVer |
---
### Trade-off 3: Flexibility vs Simplicity
| Approach | Flexibility | Complexity |
|----------|-------------|------------|
| **Many variables** | High | High |
| **Few variables** | Low | Low |
| **Object variables** | High | Medium |
## 🚨 Failure Modes
### Failure Mode 1: Breaking Change in Module
::: danger 🔥 Incident thực tế
*Module maintainer rename variable. 50 projects bị break. Rollback module version, fix all consumers, release properly.*
:::
| Cách phát hiện | Cách phòng tránh |
|----------------|------------------|
| Plan failures | SemVer đúng |
| Consumer errors | Deprecation warnings |
| Breaking at runtime | Test before release |
---
### Failure Mode 2: Unpinned Module Version
| Cách phát hiện | Cách phòng tránh |
|----------------|------------------|
| Random failures | Pin versions |
| Different behavior | Lock file |
| Init pulls new version | ≫ version constraint |
---
### Failure Mode 3: Module Drift
| Cách phát hiện | Cách phòng tránh |
|----------------|------------------|
| Copy-paste diverges | Single source |
| Different configs | Regular audit |
| Security not applied | Centralized modules |
## 🔐 Security Baseline
### Module Security Requirements
| Requirement | Implementation | Verification |
|-------------|----------------|---------------|
| **Secure defaults** | encryption=true, public=false | Code review |
| **No hardcoded secrets** | Variables only | Pre-commit scan |
| **Least privilege** | Minimal IAM | Policy review |
| **Validated inputs** | validation blocks | Module tests |
### Module Security Checklist
| Item | Status |
|------|--------|
| Secure default values | ☑ Required |
| No secrets in code | ☑ Required |
| Input validation | ☑ Required |
| Version pinned | ☑ Required |
| Source trusted | ☑ Required |
## 📊 Ops Readiness
### Metrics cần Monitoring
| Metric | Source | Alert Threshold |
|--------|--------|-----------------|
| Module version spread | Registry | > 3 major versions |
| Deprecated module usage | Audit | Any |
| Module test failures | CI | Any |
| Consumer count | Registry | Tracking only |
### Runbook Entry Points
| Tình huống | Runbook |
|------------|---------|
| Breaking change released | `runbook/module-rollback.md` |
| Module deprecation | `runbook/module-migration.md` |
| Security vuln in module | `runbook/module-security-patch.md` |
| Version conflict | `runbook/module-version-resolution.md` |
## ✅ Design Review Checklist
### Interface
- [ ] Variables well-typed
- [ ] Descriptions complete
- [ ] Validation in place
- [ ] Outputs documented
### Structure
- [ ] Single responsibility
- [ ] Standard layout
- [ ] README with examples
- [ ] Tests included
### Versioning
- [ ] SemVer followed
- [ ] Changelog maintained
- [ ] Breaking changes documented
- [ ] Migration guide for major versions
### Security
- [ ] Secure defaults
- [ ] No hardcoded values
- [ ] Input validation
- [ ] Minimal permissions
## 📎 Liên kết
- 📎 [IaC Fundamentals](/terraform/foundation/fundamentals) - Terraform basics
- 📎 [State Management](/terraform/foundation/state) - Module state considerations
- 📎 [Testing & CI/CD](/terraform/advanced/testing) - Testing modules
- 📎 [Multi-Cloud Patterns](/terraform/patterns/multi-cloud) - Cross-cloud module design
- 📎 [AWS VPC](/aws/core/networking) - AWS networking patterns
- 📎 [GCP VPC](/gcp/core/networking) - GCP networking patterns
- 📎 [Terraform Security](/terraform/security/security) - Security patterns