.claude/skills/terraform-aws/SKILL.md
Comprehensive AWS infrastructure management with Terraform. Covers provider configuration, state management (S3 backend with native locking in Terraform 1.11+), common resource patterns (VPC, IAM, S3, RDS, EKS), module usage, and production best practices. Use when provisioning AWS infrastructure, managing Terraform state, creating VPCs, configuring IAM roles/policies, deploying databases, or troubleshooting Terraform/AWS issues.
npx skillsauth add adaptationio/skrillz terraform-awsInstall 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.
Complete guide for managing AWS infrastructure as code using Terraform. This skill provides production-ready patterns for VPC networking, IAM security, state management, and common AWS resources with Terraform 1.11+ features and AWS Provider 6.x.
Keywords: Terraform, AWS, infrastructure as code, IaC, VPC, IAM, S3 backend, state locking, modules, EC2, RDS, EKS, security groups, CloudWatch
Terraform Version: 1.11+ (with S3 native locking) AWS Provider: 6.x
terraform {
required_version = ">= 1.11.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "production/terraform.tfstate"
region = "us-east-1"
# Native S3 locking (Terraform 1.11+)
use_lockfile = true
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
}
}
}
variable "aws_region" {
description = "AWS region for all resources"
type = string
default = "us-east-1"
}
variable "environment" {
description = "Environment name (dev, staging, production)"
type = string
validation {
condition = contains(["dev", "staging", "production"], var.environment)
error_message = "Environment must be dev, staging, or production."
}
}
variable "project_name" {
description = "Project name for resource naming and tagging"
type = string
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "${var.project_name}-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b", "us-east-1c"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]
enable_nat_gateway = true
single_nat_gateway = var.environment != "production" # Cost optimization for non-prod
enable_dns_hostnames = true
enable_dns_support = true
# VPC Flow Logs
enable_flow_log = true
create_flow_log_cloudwatch_iam_role = true
create_flow_log_cloudwatch_log_group = true
tags = {
Environment = var.environment
}
}
resource "aws_s3_bucket" "app_data" {
bucket = "${var.project_name}-app-data-${var.environment}"
tags = {
Name = "${var.project_name}-app-data"
Environment = var.environment
}
}
resource "aws_s3_bucket_versioning" "app_data" {
bucket = aws_s3_bucket.app_data.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "app_data" {
bucket = aws_s3_bucket.app_data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "app_data" {
bucket = aws_s3_bucket.app_data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# IAM role for EC2 instances
resource "aws_iam_role" "ec2_app_role" {
name = "${var.project_name}-ec2-app-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
}
# Attach AWS managed policy for SSM
resource "aws_iam_role_policy_attachment" "ec2_ssm" {
role = aws_iam_role.ec2_app_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# Custom policy for S3 access
resource "aws_iam_role_policy" "ec2_s3_access" {
name = "${var.project_name}-ec2-s3-access"
role = aws_iam_role.ec2_app_role.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
]
Resource = "${aws_s3_bucket.app_data.arn}/*"
},
{
Effect = "Allow"
Action = [
"s3:ListBucket"
]
Resource = aws_s3_bucket.app_data.arn
}
]
})
}
# Instance profile for EC2
resource "aws_iam_instance_profile" "ec2_app_profile" {
name = "${var.project_name}-ec2-app-profile"
role = aws_iam_role.ec2_app_role.name
}
resource "aws_db_subnet_group" "main" {
name = "${var.project_name}-db-subnet-group"
subnet_ids = module.vpc.private_subnets
tags = {
Name = "${var.project_name}-db-subnet-group"
}
}
resource "aws_security_group" "rds" {
name = "${var.project_name}-rds-sg"
description = "Security group for RDS database"
vpc_id = module.vpc.vpc_id
ingress {
description = "PostgreSQL from VPC"
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = [module.vpc.vpc_cidr_block]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_db_instance" "main" {
identifier = "${var.project_name}-db-${var.environment}"
engine = "postgres"
engine_version = "16.3"
instance_class = var.environment == "production" ? "db.t3.medium" : "db.t3.micro"
allocated_storage = var.environment == "production" ? 100 : 20
storage_type = "gp3"
db_name = var.db_name
username = var.db_username
password = var.db_password # Use AWS Secrets Manager in production!
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.rds.id]
backup_retention_period = var.environment == "production" ? 30 : 7
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
deletion_protection = var.environment == "production"
skip_final_snapshot = var.environment != "production"
final_snapshot_identifier = var.environment == "production" ? "${var.project_name}-db-final-snapshot-${formatdate("YYYY-MM-DD-hhmm", timestamp())}" : null
tags = {
Name = "${var.project_name}-db"
Environment = var.environment
}
}
Recommended approach - No DynamoDB required!
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "production/terraform.tfstate"
region = "us-east-1"
# Enable S3 native locking (Terraform 1.11+)
use_lockfile = true
# Encryption
encrypt = true
# Optional: Use KMS for encryption
# kms_key_id = "arn:aws:kms:us-east-1:123456789012:key/abcd1234-..."
}
}
For Terraform < 1.11:
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "production/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
See references/state-management.md for complete state management patterns.
# VPC Module
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = "${var.project_name}-vpc"
cidr = var.vpc_cidr
azs = var.availability_zones
private_subnets = var.private_subnet_cidrs
public_subnets = var.public_subnet_cidrs
enable_nat_gateway = true
enable_vpn_gateway = false
}
# EKS Module
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "${var.project_name}-eks"
cluster_version = "1.30"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
main = {
min_size = 2
max_size = 10
desired_size = 3
instance_types = ["t3.medium"]
capacity_type = "ON_DEMAND"
}
}
}
modules/
└── application/
├── main.tf
├── variables.tf
├── outputs.tf
└── README.md
# Using custom module
module "application" {
source = "./modules/application"
name = "${var.project_name}-app"
environment = var.environment
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
}
# Initialize backend and download providers
terraform init
# Migrate state to new backend
terraform init -migrate-state
# Reconfigure backend
terraform init -reconfigure
# See what will change
terraform plan -out=tfplan
# Apply changes
terraform apply tfplan
# Auto-approve (use with caution!)
terraform apply -auto-approve
# Target specific resource
terraform apply -target=aws_instance.web
# Import VPC
terraform import aws_vpc.main vpc-12345678
# Import S3 bucket
terraform import aws_s3_bucket.app_data my-bucket-name
# Import RDS instance
terraform import aws_db_instance.main my-db-instance
# List workspaces
terraform workspace list
# Create workspace
terraform workspace new staging
# Switch workspace
terraform workspace select production
# Show current workspace
terraform workspace show
For comprehensive guides on specific topics:
VPC Networking: references/vpc-networking.md
IAM Security: references/iam-security.md
State Management: references/state-management.md
| Issue | Cause | Fix |
|-------|-------|-----|
| State lock timeout | Orphaned lock file | terraform force-unlock <lock-id> |
| Provider version conflict | Incompatible versions | Update required_providers block |
| Cycle in resource dependencies | Circular reference | Use depends_on or split resources |
| Resource already exists | Resource created outside Terraform | Use terraform import |
| Insufficient permissions | Missing IAM permissions | Add required IAM policies |
| State drift detected | Manual changes in AWS | Review with terraform plan, then apply or import |
# Use consistent naming pattern
resource "aws_instance" "web" {
# Format: {project}-{resource}-{environment}
tags = {
Name = "${var.project_name}-web-${var.environment}"
}
}
# Use conditionals for environment differences
resource "aws_db_instance" "main" {
instance_class = var.environment == "production" ? "db.r5.large" : "db.t3.micro"
backup_retention_period = var.environment == "production" ? 30 : 7
deletion_protection = var.environment == "production"
}
# Never hardcode secrets!
variable "db_password" {
description = "Database master password"
type = string
sensitive = true
}
# Use AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "production/db/password"
}
resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
provider "aws" {
region = var.aws_region
# All resources get these tags automatically
default_tags {
tags = {
Environment = var.environment
ManagedBy = "Terraform"
Project = var.project_name
CostCenter = var.cost_center
}
}
}
# Initialization
terraform init # Initialize working directory
terraform init -upgrade # Upgrade providers to latest version
# Planning
terraform plan # Show execution plan
terraform plan -out=tfplan # Save plan to file
terraform plan -destroy # Plan resource destruction
# Applying
terraform apply # Apply changes
terraform apply tfplan # Apply saved plan
terraform destroy # Destroy all resources
# State Management
terraform state list # List resources in state
terraform state show <resource> # Show resource details
terraform state rm <resource> # Remove resource from state
terraform state mv <src> <dest> # Move/rename resource in state
# Validation
terraform fmt # Format HCL files
terraform fmt -recursive # Format all .tf files
terraform validate # Validate configuration
terraform validate -json # JSON output for CI/CD
# Outputs
terraform output # Show all outputs
terraform output <name> # Show specific output
terraform output -json # JSON output
# Workspaces
terraform workspace list # List workspaces
terraform workspace new <name> # Create workspace
terraform workspace select <name> # Switch workspace
terraform workspace delete <name> # Delete workspace
required_providerssensitive = trueterraform fmt and terraform validate pass.terraform/ and *.tfstate in .gitignoreTerraform Version: 1.11+ AWS Provider: 6.x Best Practices: Following AWS Well-Architected Framework Last Updated: November 2025
development
Setup secure web-based terminal access to WSL2 from mobile/tablet via ttyd + ngrok/Cloudflare/Tailscale. One-command install, start, stop, status. Use when you need remote terminal access, web terminal, browser-based shell, or mobile access to WSL2 environment.
development
Complete development workflows where Claude writes the code while Gemini and Codex provide research, planning, reviews, and different perspectives. Claude remains the main developer. Use for complex projects requiring expert planning and multi-perspective reviews.
development
Systematic progress tracking for skill development. Manages task states (pending/in_progress/completed), updates in real-time, reports progress, identifies blockers, and maintains momentum. Use when tracking skill development, coordinating work, or reporting progress.
testing
Comprehensive testing workflow orchestrating functional testing, example validation, integration testing, and usability assessment. Sequential workflow for complete skill testing from examples through scenarios to integration validation. Use when conducting thorough testing, pre-deployment validation, ensuring skill functionality, or comprehensive quality checks.