--- 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> { 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