From ai-toolkit
Generates complete service-level Terraform infrastructure with modules, environments, and CI/CD for AWS services like ECS/EKS, RDS, Redis, S3. Use when adding Terraform to a new service or bootstrapping from scratch.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ai-toolkit:terraform-service-scaffoldThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generates a complete service-level Terraform structure with live orchestration, reusable modules, environment configs, and CI/CD workflow.
Generates a complete service-level Terraform structure with live orchestration, reusable modules, environment configs, and CI/CD workflow.
Ask the user:
{service})# Check existing shared infrastructure outputs
data "terraform_remote_state" "infra" {
backend = "s3"
config = {
bucket = "{project}-terraform-state"
key = "infra/terraform.tfstate"
region = var.region
}
}
Location: terraform/live/
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"
}
}
}
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 {
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
}
}
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
}
Location: terraform/modules/{service}/
One resource per file:
# Module entry point — locals and data sources only
locals {
name_prefix = "${var.project}-${var.service}-${var.env}"
}
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"
}
]
}
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
}
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
}
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
}
}
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
}
}
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
}
}
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
}
Location: terraform/envs/dev/
bucket = "{project}-terraform-state"
key = "{service}/dev/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "{project}-terraform-locks"
encrypt = true
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"
# Encrypted via git-secret-protector — committed to git, decrypted at CI/CD time
db_password = ""
redis_auth_token = ""
Secrets are encrypted in git using git-secret-protector, NOT .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
The secrets file IS committed to git (encrypted). On checkout, the smudge filter decrypts it if the key is available. In CI/CD, decrypt before terraform plan/apply.
# .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
After generating all files, produce this checklist:
terraform/live/terraform.tf — backend + providerterraform/live/variables.tf — all input variablesterraform/live/locals.tf — computed values, remote stateterraform/live/outputs.tf — exported valuesterraform/modules/{service}/ — resource-per-fileterraform/envs/dev/state.config — backend configterraform/envs/dev/terraform.tfvars — environment valuesterraform/envs/dev/secrets.tfvars — sensitive values (gitignored).github/workflows/terraform.yml — CI/CD pipeline.gitignore includes *.tfvars secrets, .terraform/, *.tfstate*live/ only, never in modules{project}/terraform-modules with ?ref=vX.Y.Zlive/ only, never in modulessensitive = true.gitignore and secrets.tfvarsProduces a complete directory tree:
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
npx claudepluginhub c0x12c/ai-toolkit --plugin ai-toolkitProvides a structured workflow for Terraform infrastructure as code, covering resource provisioning, module creation, state management, multi-environment deployments, and CI/CD integration.
Structures service repositories with Terraform IaC directories, separates shared platform from service-owned resources, designs module patterns, and eliminates central deployment bottlenecks.
Researches infrastructure best practices and generates Terraform modules, Dockerfiles, Kubernetes manifests, Pulumi programs, and CI/CD pipelines for GCP, AWS, Azure deployments.