Files
ai-development-scaffold/.claude/skills/infrastructure/aws/SKILL.md
James Bland befb8fbaeb feat: initial Claude Code configuration scaffold
Comprehensive Claude Code guidance system with:

- 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit
- 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#),
  infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD),
  testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability)
- 3 hooks: secret detection, auto-formatting, TDD git pre-commit
- Strict TDD enforcement with 80%+ coverage requirements
- Multi-model strategy: Opus for planning, Sonnet for execution (opusplan)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 15:47:34 -05:00

12 KiB

name, description
name description
aws-services AWS service patterns, IAM best practices, and common architectures. Use when designing or implementing AWS infrastructure.

AWS Services Skill

Common Architecture Patterns

Web Application (ECS + RDS)

┌─────────────────────────────────────────────────────────────┐
│                         VPC                                  │
│  ┌─────────────────────────────────────────────────────────┐│
│  │                   Public Subnets                         ││
│  │  ┌─────────────┐                    ┌─────────────┐     ││
│  │  │     ALB     │                    │  NAT GW     │     ││
│  │  └──────┬──────┘                    └──────┬──────┘     ││
│  └─────────┼───────────────────────────────────┼───────────┘│
│            │                                   │             │
│  ┌─────────┼───────────────────────────────────┼───────────┐│
│  │         │       Private Subnets             │           ││
│  │  ┌──────▼──────┐                    ┌───────▼─────┐     ││
│  │  │ ECS Fargate │                    │     RDS     │     ││
│  │  │  (Tasks)    │───────────────────▶│  PostgreSQL │     ││
│  │  └─────────────┘                    └─────────────┘     ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘

Serverless (Lambda + API Gateway)

┌────────────┐     ┌─────────────┐     ┌─────────────┐
│   Route53  │────▶│ API Gateway │────▶│   Lambda    │
└────────────┘     └─────────────┘     └──────┬──────┘
                                              │
                   ┌──────────────────────────┼──────────────┐
                   │                          │              │
            ┌──────▼─────┐  ┌─────────┐  ┌────▼────┐
            │  DynamoDB  │  │   S3    │  │ Secrets │
            └────────────┘  └─────────┘  │ Manager │
                                         └─────────┘

IAM Best Practices

Least Privilege Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadSpecificBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-data-bucket",
        "arn:aws:s3:::my-app-data-bucket/*"
      ]
    },
    {
      "Sid": "AllowSecretsAccess",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue"
      ],
      "Resource": [
        "arn:aws:secretsmanager:eu-west-2:123456789:secret:my-app/*"
      ]
    }
  ]
}

Trust Policy (for ECS Tasks)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Cross-Account Access

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::ACCOUNT_ID:role/CrossAccountRole"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "unique-external-id"
        }
      }
    }
  ]
}

Secrets Management

Using Secrets Manager

# Python - boto3
import boto3
import json

def get_secret(secret_name: str, region: str = "eu-west-2") -> dict:
    client = boto3.client("secretsmanager", region_name=region)
    response = client.get_secret_value(SecretId=secret_name)
    return json.loads(response["SecretString"])

# Usage
db_creds = get_secret("myapp/prod/database")
connection_string = f"postgresql://{db_creds['username']}:{db_creds['password']}@{db_creds['host']}/{db_creds['database']}"
// TypeScript - AWS SDK v3
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

async function getSecret(secretName: string): Promise<Record<string, string>> {
  const client = new SecretsManagerClient({ region: "eu-west-2" });
  const command = new GetSecretValueCommand({ SecretId: secretName });
  const response = await client.send(command);

  if (!response.SecretString) {
    throw new Error("Secret not found");
  }

  return JSON.parse(response.SecretString);
}

ECS Task with Secrets

// Task definition
{
  "containerDefinitions": [
    {
      "name": "app",
      "secrets": [
        {
          "name": "DATABASE_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:eu-west-2:123456789:secret:myapp/database:password::"
        },
        {
          "name": "API_KEY",
          "valueFrom": "arn:aws:secretsmanager:eu-west-2:123456789:secret:myapp/api-key"
        }
      ]
    }
  ]
}

S3 Patterns

Bucket Policy (Least Privilege)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowECSTaskAccess",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::123456789:role/ecs-task-role"
      },
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::my-bucket/uploads/*"
    },
    {
      "Sid": "DenyUnencryptedUploads",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "AES256"
        }
      }
    }
  ]
}

Presigned URLs

import boto3
from botocore.config import Config

def generate_presigned_url(bucket: str, key: str, expiration: int = 3600) -> str:
    """Generate a presigned URL for S3 object access."""
    s3_client = boto3.client(
        "s3",
        config=Config(signature_version="s3v4"),
        region_name="eu-west-2"
    )

    return s3_client.generate_presigned_url(
        "get_object",
        Params={"Bucket": bucket, "Key": key},
        ExpiresIn=expiration
    )

DynamoDB Patterns

Single Table Design

# Entity types in same table
ENTITY_TYPES = {
    "USER": {"PK": "USER#", "SK": "PROFILE"},
    "ORDER": {"PK": "USER#", "SK": "ORDER#"},
    "PRODUCT": {"PK": "PRODUCT#", "SK": "DETAILS"},
}

# Access patterns
def get_user(user_id: str) -> dict:
    return table.get_item(
        Key={"PK": f"USER#{user_id}", "SK": "PROFILE"}
    )["Item"]

def get_user_orders(user_id: str) -> list:
    response = table.query(
        KeyConditionExpression="PK = :pk AND begins_with(SK, :sk)",
        ExpressionAttributeValues={
            ":pk": f"USER#{user_id}",
            ":sk": "ORDER#"
        }
    )
    return response["Items"]

Lambda Patterns

Handler with Error Handling

import json
import logging
from typing import Any

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event: dict, context: Any) -> dict:
    """Lambda handler with proper error handling."""
    try:
        logger.info("Processing event", extra={"event": event})

        # Process request
        body = json.loads(event.get("body", "{}"))
        result = process_request(body)

        return {
            "statusCode": 200,
            "headers": {"Content-Type": "application/json"},
            "body": json.dumps(result)
        }

    except ValidationError as e:
        logger.warning("Validation error", extra={"error": str(e)})
        return {
            "statusCode": 400,
            "body": json.dumps({"error": str(e)})
        }

    except Exception as e:
        logger.exception("Unexpected error")
        return {
            "statusCode": 500,
            "body": json.dumps({"error": "Internal server error"})
        }

Cold Start Optimization

# Initialize outside handler (runs once per container)
import boto3

# These persist across invocations
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("my-table")
secrets_client = boto3.client("secretsmanager")

# Cache secrets
_cached_secrets = {}

def get_cached_secret(name: str) -> dict:
    if name not in _cached_secrets:
        response = secrets_client.get_secret_value(SecretId=name)
        _cached_secrets[name] = json.loads(response["SecretString"])
    return _cached_secrets[name]

def handler(event, context):
    # Use cached resources
    secret = get_cached_secret("my-secret")
    # ...

CloudWatch Patterns

Structured Logging

import json
import logging

class JsonFormatter(logging.Formatter):
    def format(self, record):
        log_record = {
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "message": record.getMessage(),
            "logger": record.name,
        }

        # Add extra fields
        if hasattr(record, "extra"):
            log_record.update(record.extra)

        return json.dumps(log_record)

# Setup
logger = logging.getLogger()
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
logger.addHandler(handler)

# Usage
logger.info("User created", extra={"user_id": "123", "email": "user@example.com"})

Custom Metrics

import boto3

cloudwatch = boto3.client("cloudwatch")

def publish_metric(name: str, value: float, unit: str = "Count"):
    cloudwatch.put_metric_data(
        Namespace="MyApp",
        MetricData=[
            {
                "MetricName": name,
                "Value": value,
                "Unit": unit,
                "Dimensions": [
                    {"Name": "Environment", "Value": "prod"},
                    {"Name": "Service", "Value": "api"},
                ]
            }
        ]
    )

# Usage
publish_metric("OrdersProcessed", 1)
publish_metric("ProcessingTime", 150, "Milliseconds")

CLI Commands

# IAM
aws iam get-role --role-name MyRole
aws iam list-attached-role-policies --role-name MyRole
aws sts get-caller-identity

# S3
aws s3 ls s3://my-bucket/
aws s3 cp file.txt s3://my-bucket/
aws s3 presign s3://my-bucket/file.txt --expires-in 3600

# Secrets Manager
aws secretsmanager get-secret-value --secret-id my-secret
aws secretsmanager list-secrets

# ECS
aws ecs list-clusters
aws ecs describe-services --cluster my-cluster --services my-service
aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment

# Lambda
aws lambda invoke --function-name my-function output.json
aws lambda list-functions
aws logs tail /aws/lambda/my-function --follow

# CloudWatch
aws logs filter-log-events --log-group-name /aws/lambda/my-function --filter-pattern "ERROR"

Security Checklist

  • All S3 buckets have versioning enabled
  • All S3 buckets block public access (unless explicitly needed)
  • Encryption at rest enabled for all data stores
  • Encryption in transit (TLS) for all connections
  • IAM roles use least privilege
  • No long-term credentials (use IAM roles/instance profiles)
  • Secrets in Secrets Manager (not env vars or code)
  • VPC endpoints for AWS services (avoid public internet)
  • Security groups follow principle of least privilege
  • CloudTrail enabled for auditing
  • GuardDuty enabled for threat detection