
Terraform Aws
- 118 installs
- 44 repo stars
- Updated May 22, 2026
- bagelhole/devops-security-agent-skills
Terraform-aws is a Claude skill that provisions AWS infrastructure with Terraform, covering provider config, S3 remote state, resource and module examples.
About
Provisions AWS infrastructure with Terraform. A developer uses it to configure the AWS provider, manage remote state in S3, and declare resources like VPCs and EC2 instances. It also lists Infrastructure-as-Code best practices such as remote state locking and provider version pinning. The SKILL.md is brief, covering provider config, example resources, modules, and commands.
- Configures the AWS provider with S3 remote state backend
- Shows VPC, EC2, and community VPC module examples
- Lists IaC best practices: remote state locking, version pinning
Terraform Aws by the numbers
- 118 all-time installs (skills.sh)
- Ranked #539 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
terraform-aws capabilities & compatibility
- Capabilities
- infra provisioning · iac · state management
- Works with
- aws · terraform
- Use cases
- devops · ci cd
- Pricing
- Free
What terraform-aws says it does
Provision and manage AWS infrastructure with Terraform.
Use remote state with locking
npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill terraform-awsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 118 |
|---|---|
| repo stars | ★ 44 |
| Last updated | May 22, 2026 |
| Repository | bagelhole/devops-security-agent-skills ↗ |
What it does
Provision AWS VPCs and instances declaratively with Terraform and remote state.
Who is it for?
Teams provisioning AWS resources declaratively with Terraform and remote state.
Skip if: Non-AWS clouds or teams not using Terraform.
When should I use this skill?
Deploying AWS resources declaratively with Terraform.
What you get
AWS infrastructure provisioned declaratively with versioned, state-locked Terraform.
- AWS provider config
- VPC and EC2 resources
- reusable modules
By the numbers
- Pins AWS provider version ~> 5.0
Files
Terraform AWS
Provision and manage AWS infrastructure with Terraform.
Provider Configuration
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = var.region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
}
}
}Example Resources
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = { Name = "main-vpc" }
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
tags = { Name = "web-server" }
}Modules
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-east-1a", "us-east-1b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
}Commands
terraform init
terraform plan -out=plan.tfplan
terraform apply plan.tfplan
terraform destroyBest Practices
- Use remote state with locking
- Implement module structure
- Use workspaces or separate states per environment
- Pin provider versions
- Use data sources for AMIs
Related Skills
- aws-vpc - VPC networking
- aws-iam - IAM policies
# AWS VPC Module Template
# Production-ready VPC with public and private subnets
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "Availability zones"
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
variable "enable_nat_gateway" {
description = "Enable NAT Gateway for private subnets"
type = bool
default = true
}
locals {
public_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i)]
private_subnets = [for i, az in var.availability_zones : cidrsubnet(var.vpc_cidr, 8, i + 10)]
}
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-igw"
}
}
# Public Subnets
resource "aws_subnet" "public" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = local.public_subnets[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-${var.availability_zones[count.index]}"
"kubernetes.io/role/elb" = "1"
}
}
# Private Subnets
resource "aws_subnet" "private" {
count = length(var.availability_zones)
vpc_id = aws_vpc.main.id
cidr_block = local.private_subnets[count.index]
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.project_name}-private-${var.availability_zones[count.index]}"
"kubernetes.io/role/internal-elb" = "1"
}
}
# Elastic IP for NAT Gateway
resource "aws_eip" "nat" {
count = var.enable_nat_gateway ? 1 : 0
domain = "vpc"
tags = {
Name = "${var.project_name}-nat-eip"
}
}
# NAT Gateway
resource "aws_nat_gateway" "main" {
count = var.enable_nat_gateway ? 1 : 0
allocation_id = aws_eip.nat[0].id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.project_name}-nat"
}
depends_on = [aws_internet_gateway.main]
}
# Public Route Table
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.project_name}-public-rt"
}
}
# Private Route Table
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
dynamic "route" {
for_each = var.enable_nat_gateway ? [1] : []
content {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[0].id
}
}
tags = {
Name = "${var.project_name}-private-rt"
}
}
# Route Table Associations
resource "aws_route_table_association" "public" {
count = length(var.availability_zones)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "private" {
count = length(var.availability_zones)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
# Outputs
output "vpc_id" {
value = aws_vpc.main.id
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
}
Terraform AWS Best Practices
Project Structure
project/
├── main.tf # Main configuration
├── variables.tf # Input variables
├── outputs.tf # Output values
├── locals.tf # Local values
├── data.tf # Data sources
├── versions.tf # Provider versions
├── terraform.tfvars # Variable values (git-ignored)
├── modules/ # Local modules
│ └── vpc/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
└── environments/ # Environment configs
├── dev/
├── staging/
└── prod/State Management
Remote State with S3
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "project/env/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}State Locking
resource "aws_dynamodb_table" "terraform_locks" {
name = "terraform-locks"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
}Security Best Practices
Use IAM Roles, Not Credentials
provider "aws" {
region = "us-east-1"
# No access_key or secret_key - use IAM role or env vars
}Enable Encryption Everywhere
resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
bucket = aws_s3_bucket.example.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.example.arn
}
}
}Use Sensitive Variables
variable "database_password" {
type = string
sensitive = true
}Tagging Strategy
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
Owner = var.team
CostCenter = var.cost_center
}
}
resource "aws_instance" "example" {
# ... configuration ...
tags = merge(local.common_tags, {
Name = "example-instance"
Role = "web"
})
}Module Best Practices
Version Pinning
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0" # Pin specific version
# ... configuration ...
}Variable Validation
variable "environment" {
type = string
description = "Environment name"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}Workflow
Plan Before Apply
terraform plan -out=tfplan
terraform apply tfplanUse Workspaces or Directories for Environments
# Workspaces
terraform workspace new prod
terraform workspace select prod
# Or separate directories (recommended)
cd environments/prod
terraform applyCommon Patterns
Data Sources for Existing Resources
data "aws_vpc" "existing" {
filter {
name = "tag:Name"
values = ["main-vpc"]
}
}
resource "aws_subnet" "new" {
vpc_id = data.aws_vpc.existing.id
# ...
}Dynamic Blocks
resource "aws_security_group" "example" {
# ...
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
}
}
}#!/bin/bash
# Terraform AWS Project Initialization Script
# Usage: ./tf-init.sh <project-name> [region]
set -euo pipefail
PROJECT_NAME="${1:-}"
REGION="${2:-us-east-1}"
if [ -z "$PROJECT_NAME" ]; then
echo "Usage: $0 <project-name> [region]"
exit 1
fi
echo "========================================="
echo "Terraform AWS Project Setup"
echo "Project: $PROJECT_NAME"
echo "Region: $REGION"
echo "========================================="
echo ""
mkdir -p "$PROJECT_NAME"
cd "$PROJECT_NAME"
# Create main.tf
cat > main.tf << EOF
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Uncomment for remote state
# backend "s3" {
# bucket = "${PROJECT_NAME}-tfstate"
# key = "terraform.tfstate"
# region = "${REGION}"
# encrypt = true
# dynamodb_table = "${PROJECT_NAME}-tflock"
# }
}
provider "aws" {
region = var.region
default_tags {
tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
}
}
}
EOF
# Create variables.tf
cat > variables.tf << EOF
variable "project_name" {
description = "Project name for tagging"
type = string
default = "${PROJECT_NAME}"
}
variable "environment" {
description = "Environment (dev, staging, prod)"
type = string
default = "dev"
}
variable "region" {
description = "AWS region"
type = string
default = "${REGION}"
}
EOF
# Create outputs.tf
cat > outputs.tf << EOF
output "region" {
description = "AWS region"
value = var.region
}
EOF
# Create terraform.tfvars
cat > terraform.tfvars << EOF
project_name = "${PROJECT_NAME}"
environment = "dev"
region = "${REGION}"
EOF
# Create .gitignore
cat > .gitignore << EOF
# Terraform
.terraform/
*.tfstate
*.tfstate.*
*.tfvars.json
crash.log
*.tfplan
# Keep tfvars template
!terraform.tfvars.example
# IDE
.idea/
*.swp
*.swo
.vscode/
EOF
# Initialize Terraform
echo ""
echo "Initializing Terraform..."
terraform init
echo ""
echo "========================================="
echo "Project created successfully!"
echo ""
echo "Files created:"
ls -la
echo ""
echo "Next steps:"
echo " 1. cd $PROJECT_NAME"
echo " 2. Edit terraform.tfvars"
echo " 3. Add resources to main.tf"
echo " 4. terraform plan"
echo " 5. terraform apply"
echo "========================================="
Related skills
FAQ
How does it manage state?
With an S3 remote state backend and remote state locking as a best practice.
What resources are shown?
aws_vpc and aws_instance, plus the terraform-aws-modules VPC module.