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>
This commit is contained in:
423
.claude/skills/infrastructure/aws/SKILL.md
Normal file
423
.claude/skills/infrastructure/aws/SKILL.md
Normal file
@@ -0,0 +1,423 @@
|
||||
---
|
||||
name: aws-services
|
||||
description: 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
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": "ecs-tasks.amazonaws.com"
|
||||
},
|
||||
"Action": "sts:AssumeRole"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Cross-Account Access
|
||||
```json
|
||||
{
|
||||
"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
|
||||
# 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
|
||||
// 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
|
||||
```json
|
||||
// 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)
|
||||
```json
|
||||
{
|
||||
"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
|
||||
```python
|
||||
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
|
||||
```python
|
||||
# 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
|
||||
```python
|
||||
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
|
||||
```python
|
||||
# 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
|
||||
```python
|
||||
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
|
||||
```python
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
Reference in New Issue
Block a user