关于 terraform-service-scaffold
面向需要为新服务搭建基础设施的团队,这项技能按需求生成完整的 Terraform 服务级目录结构。它涵盖实时编排、可复用模块、环境配置和 CI/CD 工作流,并遵循版本固定与状态管理规则。
主要功能
- 01按服务需求生成 Terraform 结构
- 02支持 ECS 或 EKS 容器主机
- 03配置 RDS、Redis、S3、SQS
- 04生成环境变量与状态配置
- 05生成 CI/CD 工作流
- 06按文件拆分服务模块资源
适用场景
- 01为新服务仓库引入 Terraform
- 02从头搭建微服务基础设施
- 03为服务配置开发环境与云资源
- 04生成基础设施变更的 CI/CD 流程
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ c0x12c/ ai-toolkit --skill terraform-service-scaffold安装后在 Claude Code 中输入/terraform-service-scaffold确认已加载。
详细介绍
Terraform 服务脚手架
生成完整的服务级 Terraform 结构,包含实时编排、可复用模块、环境配置和 CI/CD 工作流。
适用场景
- 为新的服务代码仓库添加 Terraform
- 从头开始搭建服务基础设施
- 使用标准云资源搭建新的微服务
流程
1. 收集需求
询问用户:
- 服务名称(例如:
{service}) - 容器宿主:ECS 或 EKS
- 所需资源:RDS、Redis、S3、SQS,或组合
- 环境:要搭建哪些环境(默认:dev)
2. 读取基础设施远程状态
# Check existing shared infrastructure outputs
data "terraform_remote_state" "infra" {
backend = "s3"
config = {
bucket = "{project}-terraform-state"
key = "infra/terraform.tfstate"
region = var.region
}
}
3. 创建实时编排
位置:terraform/live/
terraform.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {}
}
provider "aws" {
region = var.region
default_tags {
tags = {
Project = var.project
Service = var.service
Environment = var.env
ManagedBy = "terraform"
}
}
}
variables.tf
variable "project" {
description = "Project identifier"
type = string
}
variable "service" {
description = "Service name"
type = string
}
variable "env" {
description = "Environment (dev, staging, prod)"
type = string
}
variable "region" {
description = "AWS region"
type = string
default = "us-east-1"
}
locals.tf
locals {
name_prefix = "${var.project}-${var.service}-${var.env}"
vpc_id = data.terraform_remote_state.infra.outputs.vpc_id
private_subnet_ids = data.terraform_remote_state.infra.outputs.private_subnet_ids
public_subnet_ids = data.terraform_remote_state.infra.outputs.public_subnet_ids
common_tags = {
Project = var.project
Service = var.service
Environment = var.env
}
}
outputs.tf
output "ecr_repository_url" {
description = "ECR repository URL for {service}"
value = module.{service}.ecr_repository_url
}
output "service_endpoint" {
description = "Service endpoint URL"
value = module.{service}.service_endpoint
}
4. 创建服务模块
位置:terraform/modules/{service}/
每个文件包含一个资源:
main.tf
# Module entry point — locals and data sources only
locals {
name_prefix = "${var.project}-${var.service}-${var.env}"
}
ecr.tf
module "ecr_backend" {
source = "git::https://github.com/{project}/terraform-modules.git//ecr?ref=v1.0.0"
name = "${local.name_prefix}-backend"
image_tag_mutability = "IMMUTABLE"
lifecycle_policy_rules = [
{
description = "Keep last 20 images"
count_number = 20
tag_status = "any"
count_type = "imageCountMoreThan"
}
]
}
module "ecr_worker" {
source = "git::https://github.com/{project}/terraform-modules.git//ecr?ref=v1.0.0"
name = "${local.name_prefix}-worker"
image_tag_mutability = "IMMUTABLE"
lifecycle_policy_rules = [
{
description = "Keep last 20 images"
count_number = 20
tag_status = "any"
count_type = "imageCountMoreThan"
}
]
}
rds.tf
module "rds" {
source = "git::https://github.com/{project}/terraform-modules.git//rds?ref=v1.0.0"
name = "${local.name_prefix}-db"
engine = "postgres"
engine_version = "15.4"
instance_class = var.rds_instance_class
allocated_storage = var.rds_allocated_storage
db_name = replace(var.service, "-", "_")
master_username = var.db_username
master_password = var.db_password
subnet_ids = var.private_subnet_ids
vpc_id = var.vpc_id
deletion_protection = var.env == "prod" ? true : false
backup_retention_period = var.env == "prod" ? 30 : 7
multi_az = var.env == "prod" ? true : false
}
redis.tf
module "redis" {
source = "git::https://github.com/{project}/terraform-modules.git//elasticache?ref=v1.0.0"
name = "${local.name_prefix}-cache"
engine = "redis"
node_type = var.redis_node_type
num_cache_nodes = 1
subnet_ids = var.private_subnet_ids
vpc_id = var.vpc_id
transit_encryption = true
at_rest_encryption = true
auth_token = var.redis_auth_token
}
s3.tf
module "s3" {
source = "git::https://github.com/{project}/terraform-modules.git//s3?ref=v1.0.0"
bucket_name = "${local.name_prefix}-assets"
versioning = true
server_side_encryption = {
sse_algorithm = "aws:kms"
}
block_public_access = {
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
}
sqs.tf
module "sqs" {
source = "git::https://github.com/{project}/terraform-modules.git//sqs?ref=v1.0.0"
name = "${local.name_prefix}-queue"
visibility_timeout_seconds = 300
message_retention_seconds = 1209600
receive_wait_time_seconds = 20
dead_letter_queue = {
enabled = true
max_receive_count = 3
retention_seconds = 1209600
}
}
ecs.tf(容器宿主为 ECS 时)
module "ecs_service" {
source = "git::https://github.com/{project}/terraform-modules.git//ecs-service?ref=v1.0.0"
name = local.name_prefix
cluster_id = var.ecs_cluster_id
task_definition = module.ecs_task.arn
desired_count = var.desired_count
subnet_ids = var.private_subnet_ids
security_groups = [aws_security_group.service.id]
target_group_arn = aws_lb_target_group.service.arn
container_name = "${local.name_prefix}-backend"
container_port = var.container_port
}
module "ecs_task" {
source = "git::https://github.com/{project}/terraform-modules.git//ecs-task?ref=v1.0.0"
family = local.name_prefix
cpu = var.task_cpu
memory = var.task_memory
execution_role = var.ecs_execution_role_arn
task_role = var.ecs_task_role_arn
image = "${module.ecr_backend.repository_url}:${var.image_tag}"
container_port = var.container_port
environment = var.container_environment
secrets = var.container_secrets
}
resource "aws_lb_target_group" "service" {
name = local.name_prefix
port = var.container_port
protocol = "HTTP"
vpc_id = var.vpc_id
target_type = "ip"
health_check {
path = "/health"
healthy_threshold = 2
unhealthy_threshold = 3
interval = 30
}
}
eks.tf(容器宿主为 EKS 时)
resource "kubernetes_namespace" "service" {
metadata {
name = var.service
labels = {
project = var.project
env = var.env
}
}
}
resource "kubernetes_service_account" "service" {
metadata {
name = var.service
namespace = kubernetes_namespace.service.metadata[0].name
annotations = {
"eks.amazonaws.com/role-arn" = module.irsa.role_arn
}
}
}
module "irsa" {
source = "git::https://github.com/{project}/terraform-modules.git//irsa?ref=v1.0.0"
name = "${local.name_prefix}-irsa"
oidc_provider_arn = var.oidc_provider_arn
namespace = var.service
service_account = var.service
policy_arns = var.irsa_policy_arns
}
resource "kubernetes_secret" "service" {
metadata {
name = "${var.service}-secrets"
namespace = kubernetes_namespace.service.metadata[0].name
}
data = var.k8s_secrets
}
resource "kubernetes_config_map" "service" {
metadata {
name = "${var.service}-config"
namespace = kubernetes_namespace.service.metadata[0].name
}
data = var.k8s_config
}
5. 创建环境配置
位置:terraform/envs/dev/
state.config
bucket = "{project}-terraform-state"
key = "{service}/dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "{project}-terraform-locks"
encrypt = true
terraform.tfvars
project = "{project}"
service = "{service}"
env = "dev"
region = "us-east-1"
# RDS
rds_instance_class = "db.t3.micro"
rds_allocated_storage = 20
db_username = "{service}_admin"
# Redis
redis_node_type = "cache.t3.micro"
# Container
desired_count = 1
task_cpu = 256
task_memory = 512
container_port = 8080
image_tag = "latest"
secrets.tfvars
# Encrypted via git-secret-protector — committed to git, decrypted at CI/CD time
db_password = ""
redis_auth_token = ""
设置 git-secret-protector
使用 git-secret-protector 在 git 中加密密钥,而不是使用 .gitignore。
# 1. Install git-secret-protector
pip install git-secret-protector
# 2. Initialize per-environment filters
git-secret-protector init --filter secrets-dev
git-secret-protector init --filter secrets-prod
# 3. Add .gitattributes rules for auto-encrypt on commit
cat >> .gitattributes <<'EOF'
terraform/live/envs/dev/secrets.tfvars filter=secrets-dev
terraform/live/envs/prod/secrets.tfvars filter=secrets-prod
EOF
# 4. Store encryption keys securely (CI/CD secrets, NOT in repo)
# GitHub Actions: store as repository secret GIT_SECRET_PROTECTOR_KEY_DEV / _PROD
密钥文件会以加密形式提交到 git。检出时,如果密钥可用,smudge 过滤器会对其解密。 在 CI/CD 中,运行 terraform plan/apply 前先解密。
6. 生成 CI/CD 工作流
# .github/workflows/terraform.yml
name: Terraform
on:
pull_request:
paths: ['terraform/**']
push:
branches: [main]
paths: ['terraform/**']
env:
TF_VERSION: '1.11'
ENVIRONMENT: dev
jobs:
plan:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Decrypt secrets
run: |
pip install git-secret-protector
git-secret-protector reveal --filter secrets-${{ env.ENVIRONMENT }}
env:
GIT_SECRET_PROTECTOR_KEY: ${{ secrets.GIT_SECRET_PROTECTOR_KEY_DEV }}
- name: Init
run: |
cd terraform/live
terraform init -backend-config=envs/${{ env.ENVIRONMENT }}/state.config
- name: Plan
run: |
cd terraform/live
terraform plan \
-var-file=envs/${{ env.ENVIRONMENT }}/terraform.tfvars \
-var-file=envs/${{ env.ENVIRONMENT }}/secrets.tfvars \
-no-color
apply:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Decrypt secrets
run: |
pip install git-secret-protector
git-secret-protector reveal --filter secrets-${{ env.ENVIRONMENT }}
env:
GIT_SECRET_PROTECTOR_KEY: ${{ secrets.GIT_SECRET_PROTECTOR_KEY_DEV }}
- name: Init
run: |
cd terraform/live
terraform init -backend-config=envs/${{ env.ENVIRONMENT }}/state.config
- name: Apply
run: |
cd terraform/live
terraform apply \
-var-file=envs/${{ env.ENVIRONMENT }}/terraform.tfvars \
-var-file=envs/${{ env.ENVIRONMENT }}/secrets.tfvars \
-auto-approve
7. 输出检查清单
生成所有文件后,输出此检查清单:
-
terraform/live/terraform.tf— 后端 + 提供程序 -
terraform/live/variables.tf— 所有输入变量 -
terraform/live/locals.tf— 计算值、远程状态 -
terraform/live/outputs.tf— 导出的值 -
terraform/modules/{service}/— 每个文件一个资源 -
terraform/envs/dev/state.config— 后端配置 -
terraform/envs/dev/terraform.tfvars— 环境值 -
terraform/envs/dev/secrets.tfvars— 敏感值(由 git 忽略) -
.github/workflows/terraform.yml— CI/CD 流水线 -
.gitignore包含*.tfvars密钥、.terraform/、*.tfstate*
交互风格
- 生成前询问服务名称、容器宿主和所需资源
- 一次性生成所有文件,不输出不完整内容
- 使用带版本固定的注册表模块,绝不内联定义资源
- 仅在
live/中保留提供程序,绝不放在模块中
规则
- 使用
{project}/terraform-modules中的注册表模块,并添加?ref=vX.Y.Z - 固定所有提供程序和模块的版本
- 使用扁平 locals,除非无法避免,否则不使用嵌套映射
- 仅在
live/中定义提供程序,绝不放在模块中 - 模块中每个文件包含一个资源
- 使用远程状态引用其他堆栈
- 敏感变量标记为
sensitive = true - 所有资源都添加 project、service、environment、ManagedBy 标签
- 将状态存储在 S3 中,并使用 DynamoDB 锁定
- 绝不提交密钥——使用
.gitignore和secrets.tfvars
输出
生成完整的目录树:
terraform/
live/
terraform.tf
variables.tf
locals.tf
outputs.tf
modules/{service}/
main.tf
ecr.tf
rds.tf
redis.tf
s3.tf
sqs.tf
ecs.tf (or eks.tf)
variables.tf
outputs.tf
envs/dev/
state.config
terraform.tfvars
secrets.tfvars
.github/workflows/terraform.yml
评论
支持 Markdown