writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
awsUse this skill when the user says 'AWS', 'EC2', 'S3', 'RDS', 'Lambda', 'VPC', 'IAM', 'Well-Architected', 'cost optimization', 'CloudFormation', 'Terraform AWS', 'EKS', 'ECS', 'ELB', 'Route53', 'CloudFront', 'WAF', 'Auto Scaling', 'Security Group', 'NACL', 'AWS CLI', 'AWS SDK', 'boto3', 'aws-vault', 'SSM', 'Secrets Manager', 'KMS', 'CloudWatch'. Covers: comp…
Independent GitHub snapshot · Automated classification · Original documentation governs
Design, deploy, and manage AWS infrastructure following the Well-Architected Framework with service decision trees, networking patterns, security best practices, and cost optimization.
Exact user phrases: "AWS", "EC2", "S3", "RDS", "Lambda", "VPC", "IAM", "Well-Architected", "cost optimization", "CloudFormation", "Terraform AWS", "EKS", "ECS", "ELB", "Security Group", "AWS CLI", "boto3", "SSM", "CloudWatch".
Before activating, verify:
Writes to Terraform HCL, CloudFormation YAML, AWS CLI commands, IAM policy JSON, and/or CDK TypeScript/Python.
HCL, YAML, JSON, or CLI commands with no extraneous explanation.
No preamble. No postamble. No explanations. No filler/hedging/transitions.
Direct file write. No response text.
| Workload Type | Recommended | When | Cost Model |
|---|---|---|---|
| Stateful VM, custom OS, legacy | EC2 | Need full OS control, GPU, bare metal | Per-hour instance |
| Short-lived, event-driven | Lambda | <15min execution, event sources | Per-invocation + duration |
| Container, orchestrated | ECS on Fargate | No K8s complexity, containers only | Per-task vCPU/memory |
| Container, full K8s | EKS (managed node groups) | Need K8s ecosystem, multi-service | Per-cluster + node costs |
| Container, serverless K8s | EKS with Fargate profiles | No node management | Per-pod (Fargate pricing) |
| Web app, simple deploy | App Runner | From source/GitHub, no infra config | Per-instance + traffic |
| Batch, flexible | AWS Batch | Job scheduling, queue-based | Per-job compute |
| Data Model | Recommended | Best For | HA Model |
|---|---|---|---|
| Relational (SQL) | RDS Multi-AZ | Standard OLTP, <16TB | Synchronous standby |
| Relational (high perf) | Aurora | >16TB, 6-way replication | 6 copies across 3 AZs |
| Key-Value / Document | DynamoDB | Serverless, auto-scaling, single-digit ms | Auto-replicated 3 AZs |
| Cache | ElastiCache (Redis) | Sub-millisecond reads, session store | Multi-AZ with replicas |
| Graph | Neptune | Social graphs, fraud detection | Multi-AZ |
| Time series | Timestream | IoT, DevOps metrics | Auto-tiered storage |
| Ledger | QLDB | Immutable audit trail | Auto-replicated |
| Use Case | Recommended | Performance | Max Size |
|---|---|---|---|
| Object storage, static files | S3 | 3500 PUT/5500 GET per prefix per sec | Unlimited |
| Block storage for EC2 | EBS (gp3/io2) | 16000 IOPS per volume (io2) | 64TiB |
| Shared file system (NFS) | EFS | 10GB/s burst | Unlimited |
| Windows file server | FSx for Windows | SMB protocol | 64TiB |
| Lustre HPC | FSx for Lustre | 100s GB/s throughput | Unlimited |
| Archive, long-term | S3 Glacier | Minutes to hours retrieval | Unlimited |
| Scenario | Solution | Cost | Throughput |
|---|---|---|---|
| Public web services | ALB + CloudFront | ~$0.0225/LCU-hour | Up to 1M+ QPS |
| Outbound from private subnet | NAT Gateway | ~$0.045/hr + $0.045/GB | Up to 45 Gbps |
| VPC-to-AWS service (private) | VPC Endpoint (Gateway) | Free | Up to 10 Gbps |
| VPC-to-AWS service (private) | VPC Endpoint (Interface) | ~$0.01/hr + $0.01/GB | Up to 10 Gbps |
| VPC-to-VPC peering | VPC Peering | Free (data transfer cost) | Up to 25 Gbps |
| On-prem to AWS | Direct Connect | ~$0.02/hr + port fees | 50M-100Gbps |
| Site-to-site VPN | VPN Connection | ~$0.05/hr | Up to 1.25 Gbps |
VPC with public/private subnets across 3 AZs → IAM role with least-privilege policy → EC2/ECS service in private subnets → S3 with versioning/encryption → CloudWatch alarms → Cost tags.
# Terraform: VPC with public + private subnets across 3 AZs
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = { Name = "main", Environment = "production" }
}
resource "aws_subnet" "public" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = { Name = "public-${count.index}", Tier = "public" }
}
resource "aws_subnet" "private" {
count = 3
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 100}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = { Name = "private-${count.index}", Tier = "private" }
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
}
resource "aws_eip" "nat" {
count = 3
domain = "vpc"
}
resource "aws_nat_gateway" "main" {
count = 3
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
}
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.${var.aws_region}.s3"
route_table_ids = aws_subnet.private[*].route_table_id
}
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3ReadAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-app-assets",
"arn:aws:s3:::my-app-assets/*"
],
"Condition": {
"StringEquals": {
"aws:SourceAccount": "123456789012"
},
"IpAddress": {
"aws:SourceIp": "10.0.0.0/16"
}
}
},
{
"Sid": "KMSDecrypt",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:us-east-1:123456789012:key/abc123",
"Condition": {
"ForAnyValue:StringLike": {
"kms:ViaService": "s3.us-east-1.amazonaws.com"
}
}
},
{
"Sid": "DenyResourceDeletion",
"Effect": "Deny",
"Action": [
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteVpc"
],
"Resource": "*"
}
]
}
resource "aws_ecs_cluster" "main" {
name = "main-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
}
resource "aws_ecs_task_definition" "app" {
family = "app-task"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = "512" # 0.5 vCPU
memory = "1024" # 1 GB
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.app_task.arn
container_definitions = jsonencode([
{
name = "app"
image = "${aws_ecr_repository.app.repository_url}:latest"
essential = true
portMappings = [
{ containerPort = 8080, protocol = "tcp" }
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/app"
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
}
}
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
}
])
}
resource "aws_ecs_service" "app" {
name = "app-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.private[*].id
security_groups = [aws_security_group.app.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
deployment_circuit_breaker {
enable = true
rollback = true
}
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_http_namespace.main.arn
}
}
resource "aws_s3_bucket" "assets" {
bucket = "my-app-assets-production"
force_destroy = false
}
resource "aws_s3_bucket_versioning" "assets" {
bucket = aws_s3_bucket.assets.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "assets" {
bucket = aws_s3_bucket.assets.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.key_id
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_public_access_block" "assets" {
bucket = aws_s3_bucket.assets.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_lifecycle_configuration" "assets" {
bucket = aws_s3_bucket.assets.id
rule {
id = "transition-to-glacier"
status = "Enabled"
filter {
prefix = "logs/"
}
transition {
days = 90
storage_class = "GLACIER_IR"
}
transition {
days = 365
storage_class = "DEEP_ARCHIVE"
}
expiration {
days = 730
}
}
}
resource "aws_s3_bucket_replication_configuration" "assets" {
bucket = aws_s3_bucket.assets.id
role = aws_iam_role.replication.arn
rule {
id = "cross-region-replication"
status = "Enabled"
destination {
bucket = aws_s3_bucket.dr.arn
storage_class = "STANDARD_IA"
}
}
}
resource "aws_cloudwatch_log_group" "app" {
name = "/ecs/app"
retention_in_days = 30
kms_key_id = aws_kms_key.cloudwatch.arn
}
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
alarm_name = "app-high-cpu"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "CPUUtilization"
namespace = "AWS/ECS"
period = 300
statistic = "Average"
threshold = 80
alarm_description = "ECS CPU > 80% for 10 minutes"
alarm_actions = [aws_sns_topic.ops_alerts.arn]
ok_actions = [aws_sns_topic.ops_alerts.arn]
dimensions = {
ClusterName = aws_ecs_cluster.main.name
ServiceName = aws_ecs_service.app.name
}
tags = { Environment = "production" }
}
resource "aws_cloudwatch_composite_alarm" "app_health" {
alarm_name = "app-composite-health"
alarm_rule = "ALARM(\"${aws_cloudwatch_metric_alarm.high_cpu.alarm_name}\") || ALARM(\"${aws_cloudwatch_metric_alarm.high_error_rate.alarm_name}\")"
alarm_actions = [aws_sns_topic.ops_alerts.arn]
}
# Enforce cost tags via AWS Config
resource "aws_config_config_rule" "required_tags" {
name = "required-tags"
source {
owner = "AWS"
source_identifier = "REQUIRED_TAGS"
}
input_parameters = jsonencode({
tag1Key = "Environment"
tag2Key = "Project"
tag3Key = "Owner"
tag4Key = "CostCenter"
})
}
# Cost allocation tags (created once)
# aws cloudformation create-stack --stack-name cost-tags --template-body file://cost-tags.yaml
# Resource tagging example
resource "aws_ecs_service" "app" {
tags = {
Environment = "production"
Project = "my-app"
Owner = "platform-team"
CostCenter = "CC-12345"
Terraform = "true"
}
}
| Service | Unit | Price | Best For |
|---|---|---|---|
| EC2 t3.medium (2 vCPU, 4GB) | On-Demand/hour | $0.0416 | Steady-state apps |
| EC2 t3.medium (1yr reserved) | /hour | $0.0250 | Predictable workloads |
| Lambda 1GB, 128ms | Per 1M invocations | $0.26 | Low-volume, bursty |
| ECS Fargate 0.5 vCPU, 1GB | Per hour | $0.056 | Container apps |
| EKS cluster (control plane) | Per hour | $0.10 | K8s workloads |
| App Runner (1 vCPU, 2GB) | Per hour | $0.102 | Simple web apps |
Deploying all resources in one Availability Zone. AWS services like EBS, RDS, and EC2 can all fail when an AZ goes down. Always deploy across >= 2 AZs for production.
Using "Effect": "Allow", "Action": "*", "Resource": "*". Follow least privilege with resource-level constraints and condition keys.
Putting databases and application servers in public subnets. Use private subnets with NAT Gateway or VPC endpoints for all private resources.
Storing all data in S3 Standard indefinitely. Use lifecycle policies to transition to Glacier/DEEP_ARCHIVE based on access patterns.
Hitting default limits (500 VPCs per region, 20 ALBs, etc.) during production incidents. Request quota increases early.
Storing AWS access keys in code, config files, or environment variables. Use IAM roles (EC2/ECS/EKS) or STS for temporary credentials.
| Issue | Likely Cause | Solution |
|---|---|---|
| EC2 can't reach internet | No public IP and no NAT | Add EIP or NAT Gateway to VPC |
| S3 access denied | Bucket policy or IAM incorrect | Check policy evaluation; use IAM policy simulator |
| RDS connection refused | Security group not allowing traffic | Add app security group to RDS ingress rule |
| Lambda timeout | Function duration > configured timeout | Increase timeout; optimize function code |
| ECS task failing | Task role missing permissions | Check task execution role policies; verify image |
| CloudFormation rollback | Resource limit or IAM permission | Check CloudTrail for specific error |
Terraform HCL, CloudFormation YAML, AWS CLI commands, or IAM policy JSON.
After completing this skill: