infrastructure/terraform-starter/SKILL.md
Scaffold Terraform 1.9+ infrastructure with provider configuration (AWS/GCP/Azure), modular structure, remote state, variables, outputs, workspaces, and common resource patterns.
npx skillsauth add achreftlili/deep-dev-skills terraform-starterInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scaffold Terraform 1.9+ infrastructure with provider configuration (AWS/GCP/Azure), modular structure, remote state, variables, outputs, workspaces, and common resource patterns.
mkdir -p infra/{modules,environments/dev,environments/staging,environments/production}
# Root module
touch infra/main.tf infra/variables.tf infra/outputs.tf infra/providers.tf infra/backend.tf infra/versions.tf
# Module scaffolds
mkdir -p infra/modules/{networking,compute,database,storage}
touch infra/modules/networking/{main.tf,variables.tf,outputs.tf}
touch infra/modules/compute/{main.tf,variables.tf,outputs.tf}
touch infra/modules/database/{main.tf,variables.tf,outputs.tf}
# Environment tfvars
touch infra/environments/dev/terraform.tfvars
touch infra/environments/staging/terraform.tfvars
touch infra/environments/production/terraform.tfvars
# Gitignore for Terraform
cat <<'EOF' > infra/.gitignore
.terraform/
*.tfstate
*.tfstate.backup
*.tfplan
.terraform.lock.hcl
*.auto.tfvars
EOF
infra/
main.tf # Root module — composes child modules
variables.tf # Input variables
outputs.tf # Outputs from root module
providers.tf # Provider configuration
backend.tf # Remote state backend
versions.tf # Required providers and Terraform version
modules/
networking/
main.tf # VPC, subnets, NAT gateway, security groups
variables.tf
outputs.tf
compute/
main.tf # ECS/EC2/GKE/AKS clusters
variables.tf
outputs.tf
database/
main.tf # RDS/CloudSQL/CosmosDB
variables.tf
outputs.tf
storage/
main.tf # S3/GCS buckets
variables.tf
outputs.tf
environments/
dev/
terraform.tfvars # Dev-specific variable values
staging/
terraform.tfvars
production/
terraform.tfvars
terraform.tfvars files per environment, selected via -var-file.versions.tf.locals for computed values. Keep variables.tf for user-supplied inputs.${var.project}-${var.environment}-<resource>.Project, Environment, ManagedBy=terraform..tfvars files. Use sensitive = true variables or data sources from secret managers.terraform fmt before every commit.versions.tf)terraform {
required_version = ">= 1.9"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
backend.tf)terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "myapp/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-locks"
encrypt = true
}
}
backend.tf)terraform {
backend "gcs" {
bucket = "mycompany-terraform-state"
prefix = "myapp"
}
}
providers.tf)provider "aws" {
region = var.aws_region
default_tags {
tags = {
Project = var.project
Environment = var.environment
ManagedBy = "terraform"
}
}
}
variables.tf)variable "project" {
description = "Project name used as resource prefix"
type = string
}
variable "environment" {
description = "Environment name (dev, staging, production)"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be one of: dev, staging, production."
}
}
variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
main.tf)locals {
name_prefix = "${var.project}-${var.environment}"
}
module "networking" {
source = "./modules/networking"
name_prefix = local.name_prefix
vpc_cidr = var.vpc_cidr
environment = var.environment
}
module "database" {
source = "./modules/database"
name_prefix = local.name_prefix
subnet_ids = module.networking.private_subnet_ids
security_group_ids = [module.networking.db_security_group_id]
environment = var.environment
db_instance_class = var.db_instance_class
}
module "compute" {
source = "./modules/compute"
name_prefix = local.name_prefix
subnet_ids = module.networking.private_subnet_ids
security_group_ids = [module.networking.app_security_group_id]
database_url = module.database.connection_url
environment = var.environment
}
modules/networking/main.tf)data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = { Name = "${var.name_prefix}-vpc" }
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = { Name = "${var.name_prefix}-public-${count.index}" }
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + 10)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = { Name = "${var.name_prefix}-private-${count.index}" }
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = { Name = "${var.name_prefix}-igw" }
}
resource "aws_eip" "nat" {
domain = "vpc"
tags = { Name = "${var.name_prefix}-nat-eip" }
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
tags = { Name = "${var.name_prefix}-nat" }
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = { Name = "${var.name_prefix}-public-rt" }
}
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = { Name = "${var.name_prefix}-private-rt" }
}
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
modules/database/main.tf)resource "aws_db_subnet_group" "main" {
name = "${var.name_prefix}-db-subnet"
subnet_ids = var.subnet_ids
tags = { Name = "${var.name_prefix}-db-subnet" }
}
resource "aws_db_instance" "main" {
identifier = "${var.name_prefix}-db"
engine = "postgres"
engine_version = "16.4"
instance_class = var.db_instance_class
allocated_storage = 20
max_allocated_storage = 100
storage_encrypted = true
db_name = var.db_name
username = var.db_username
password = var.db_password
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = var.security_group_ids
multi_az = var.environment == "production"
skip_final_snapshot = var.environment != "production"
backup_retention_period = var.environment == "production" ? 7 : 1
tags = { Name = "${var.name_prefix}-db" }
}
modules/storage/main.tf)resource "aws_s3_bucket" "main" {
bucket = "${var.name_prefix}-${var.bucket_suffix}"
tags = { Name = "${var.name_prefix}-${var.bucket_suffix}" }
}
resource "aws_s3_bucket_versioning" "main" {
bucket = aws_s3_bucket.main.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "main" {
bucket = aws_s3_bucket.main.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
}
}
}
resource "aws_s3_bucket_public_access_block" "main" {
bucket = aws_s3_bucket.main.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
environments/dev/terraform.tfvars)project = "myapp"
environment = "dev"
aws_region = "us-east-1"
vpc_cidr = "10.0.0.0/16"
db_instance_class = "db.t4g.micro"
outputs.tf)output "vpc_id" {
description = "VPC ID"
value = module.networking.vpc_id
}
output "database_endpoint" {
description = "RDS endpoint"
value = module.database.endpoint
sensitive = true
}
# Initialize (download providers, configure backend)
terraform -chdir=infra init
# Format all files
terraform -chdir=infra fmt -recursive
# Validate configuration
terraform -chdir=infra validate
# Plan with environment-specific vars
terraform -chdir=infra plan -var-file=environments/dev/terraform.tfvars -out=dev.tfplan
# Apply a saved plan
terraform -chdir=infra apply dev.tfplan
# Destroy (dev only)
terraform -chdir=infra destroy -var-file=environments/dev/terraform.tfvars
# Import existing resource
terraform -chdir=infra import module.networking.aws_vpc.main vpc-abc123
# Show current state
terraform -chdir=infra show
# List resources in state
terraform -chdir=infra state list
# Upgrade providers
terraform -chdir=infra init -upgrade
kubernetes-manifests skill. Terraform provisions the cluster (EKS/GKE/AKS); kubectl/kustomize manages workloads.github-actions-ci skill. Run terraform plan on PR, terraform apply on merge. Use OIDC for cloud auth.aws_secretsmanager_secret / google_secret_manager_secret for runtime secrets. Reference them in compute modules via IAM roles, not hardcoded values.testing
Set up Vitest 2.x with TypeScript for unit and component testing using test/describe/it, vi.fn/vi.mock/vi.spyOn, component testing with Testing Library, coverage (v8/istanbul), workspace config, and snapshot testing.
testing
Set up pytest 8.x with Python for unit and integration testing using fixtures (scope, autouse, parametrize), async tests (pytest-asyncio), mocking (unittest.mock, pytest-mock), coverage (pytest-cov), conftest.py patterns, and markers.
testing
Set up Playwright 1.49+ with TypeScript for E2E testing using page object model, fixtures, test.describe/test blocks, assertions, selectors, network mocking, CI configuration, and trace viewer.
testing
Set up Jest 30+ with TypeScript for unit tests, integration tests, mocking (jest.fn, jest.mock, jest.spyOn), coverage configuration, custom matchers, snapshot testing, and setup/teardown patterns.