Awesome-omni-skill aws

AWS hosting and deployment patterns covering compute (EC2, ECS, Lambda), networking (ALB, CloudFront, Route 53, API Gateway), databases (RDS, ElastiCache), infrastructure-as-code (CDK), IAM, monitoring (CloudWatch), and cost optimization. Use when deploying applications to AWS or designing cloud architecture. Triggers on AWS, EC2, ECS, Fargate, Lambda, CloudFront, CDK, API Gateway, Route 53, RDS, IAM.

install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/devops/aws" ~/.claude/skills/diegosouzapw-awesome-omni-skill-aws && rm -rf "$T"
manifest: skills/devops/aws/SKILL.md
source content

AWS Hosting

AWS provides the broadest set of cloud services. This skill covers the most common hosting patterns for web applications, APIs, and background services, with infrastructure-as-code via AWS CDK.

Architecture Decision Tree

What are you deploying?
├─ Static site (HTML/CSS/JS) ──────────> S3 + CloudFront
├─ Server-rendered app (Next.js, etc.) ─> ECS Fargate or Lambda
├─ REST/GraphQL API ───────────────────> Lambda + API Gateway OR ECS Fargate + ALB
├─ Long-running background workers ────> ECS Fargate (always-on) or EC2
├─ Event-driven functions ─────────────> Lambda (triggered by SQS, S3, EventBridge)
├─ Containerized microservices ────────> ECS Fargate + ALB + Service Connect
└─ Full control over the VM ───────────> EC2 (last resort)

EC2 — Virtual Machines

Use EC2 only when you need full OS-level control, GPU instances, or specific hardware. For most web apps, prefer ECS Fargate or Lambda.

// CDK: EC2 instance
import * as ec2 from 'aws-cdk-lib/aws-ec2';

const vpc = new ec2.Vpc(this, 'Vpc', { maxAzs: 2 });

const instance = new ec2.Instance(this, 'WebServer', {
  vpc,
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
  machineImage: ec2.MachineImage.latestAmazonLinux2023(),
  keyPair: ec2.KeyPair.fromKeyPairName(this, 'KeyPair', 'my-key'),
});

instance.connections.allowFromAnyIpv4(ec2.Port.tcp(80));
instance.connections.allowFromAnyIpv4(ec2.Port.tcp(443));

ECS Fargate — Serverless Containers

The standard choice for containerized web applications. No servers to manage, auto-scaling built in.

import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';

const cluster = new ecs.Cluster(this, 'Cluster', { vpc });

// Web service with ALB (most common pattern)
const service = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'WebApp', {
  cluster,
  cpu: 512,
  memoryLimitMiB: 1024,
  desiredCount: 2,
  taskImageOptions: {
    image: ecs.ContainerImage.fromAsset('./'),  // Build from Dockerfile in root
    containerPort: 3000,
    environment: {
      NODE_ENV: 'production',
      DATABASE_URL: databaseUrl,
    },
  },
  publicLoadBalancer: true,
});

// Auto-scaling
const scaling = service.service.autoScaleTaskCount({ maxCapacity: 10 });
scaling.scaleOnCpuUtilization('CpuScaling', {
  targetUtilizationPercent: 70,
});
scaling.scaleOnMemoryUtilization('MemoryScaling', {
  targetUtilizationPercent: 80,
});

ECS with custom task definition

const taskDef = new ecs.FargateTaskDefinition(this, 'TaskDef', {
  cpu: 1024,
  memoryLimitMiB: 2048,
});

taskDef.addContainer('app', {
  image: ecs.ContainerImage.fromEcrRepository(repo, 'latest'),
  portMappings: [{ containerPort: 3000 }],
  logging: ecs.LogDrivers.awsLogs({ streamPrefix: 'app' }),
  healthCheck: {
    command: ['CMD-SHELL', 'curl -f http://localhost:3000/health || exit 1'],
    interval: cdk.Duration.seconds(30),
    timeout: cdk.Duration.seconds(5),
    retries: 3,
  },
});

Lambda — Serverless Functions

Best for event-driven workloads, APIs with variable traffic, and cost-sensitive projects.

import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaNode from 'aws-cdk-lib/aws-lambda-nodejs';

const handler = new lambdaNode.NodejsFunction(this, 'ApiHandler', {
  runtime: lambda.Runtime.NODEJS_20_X,
  entry: 'src/lambda/handler.ts',
  handler: 'handler',
  memorySize: 512,
  timeout: cdk.Duration.seconds(30),
  environment: {
    DATABASE_URL: databaseUrl,
  },
  bundling: {
    minify: true,
    sourceMap: true,
  },
});

Lambda handler pattern

// src/lambda/handler.ts
import type { APIGatewayProxyHandlerV2 } from 'aws-lambda';

export const handler: APIGatewayProxyHandlerV2 = async (event) => {
  const body = JSON.parse(event.body ?? '{}');

  return {
    statusCode: 200,
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ message: 'OK', data: body }),
  };
};

Lambda limitations

ConstraintLimit
Max execution time15 minutes
Max memory10,240 MB
Max payload (sync)6 MB
Max payload (async)256 KB
Cold start100ms-2s (depends on runtime/size)
Concurrent executions1,000 (default, can increase)

API Gateway

HTTP API (v2) — Recommended

import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2';
import * as integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations';

const httpApi = new apigwv2.HttpApi(this, 'Api', {
  corsPreflight: {
    allowOrigins: ['https://myapp.com'],
    allowMethods: [apigwv2.CorsHttpMethod.GET, apigwv2.CorsHttpMethod.POST],
    allowHeaders: ['Content-Type', 'Authorization'],
  },
});

httpApi.addRoutes({
  path: '/users/{id}',
  methods: [apigwv2.HttpMethod.GET],
  integration: new integrations.HttpLambdaIntegration('GetUser', handler),
});

REST API (v1) — When you need request validation, API keys, or usage plans

import * as apigw from 'aws-cdk-lib/aws-apigateway';

const api = new apigw.RestApi(this, 'RestApi', {
  restApiName: 'MyService',
  deployOptions: { stageName: 'prod' },
});

const users = api.root.addResource('users');
users.addMethod('GET', new apigw.LambdaIntegration(handler));

CloudFront CDN

import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as s3 from 'aws-cdk-lib/aws-s3';

const siteBucket = new s3.Bucket(this, 'SiteBucket', {
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

const distribution = new cloudfront.Distribution(this, 'CDN', {
  defaultBehavior: {
    origin: origins.S3BucketOrigin.withOriginAccessControl(siteBucket),
    viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
    cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
  },
  defaultRootObject: 'index.html',
  errorResponses: [
    {
      httpStatus: 404,
      responsePagePath: '/index.html',  // SPA fallback
      responseHttpStatus: 200,
    },
  ],
});

Route 53 DNS

import * as route53 from 'aws-cdk-lib/aws-route53';
import * as targets from 'aws-cdk-lib/aws-route53-targets';

const zone = route53.HostedZone.fromLookup(this, 'Zone', {
  domainName: 'example.com',
});

new route53.ARecord(this, 'SiteAlias', {
  zone,
  recordName: 'app',  // app.example.com
  target: route53.RecordTarget.fromAlias(
    new targets.CloudFrontTarget(distribution)
  ),
});

RDS — Relational Database

import * as rds from 'aws-cdk-lib/aws-rds';

const database = new rds.DatabaseInstance(this, 'Database', {
  engine: rds.DatabaseInstanceEngine.postgres({
    version: rds.PostgresEngineVersion.VER_16_4,
  }),
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
  vpc,
  vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
  databaseName: 'myapp',
  credentials: rds.Credentials.fromGeneratedSecret('dbadmin'),
  multiAz: false,                    // true for production
  allocatedStorage: 20,
  maxAllocatedStorage: 100,          // Auto-scaling storage
  backupRetention: cdk.Duration.days(7),
  deletionProtection: true,          // Prevent accidental deletion
});

// Allow ECS tasks to connect
database.connections.allowFrom(service.service, ec2.Port.tcp(5432));

ElastiCache — Redis

import * as elasticache from 'aws-cdk-lib/aws-elasticache';

const subnetGroup = new elasticache.CfnSubnetGroup(this, 'RedisSubnets', {
  description: 'Subnets for Redis',
  subnetIds: vpc.privateSubnets.map(s => s.subnetId),
});

const redis = new elasticache.CfnCacheCluster(this, 'Redis', {
  cacheNodeType: 'cache.t3.micro',
  engine: 'redis',
  numCacheNodes: 1,
  cacheSubnetGroupName: subnetGroup.ref,
  vpcSecurityGroupIds: [redisSecurityGroup.securityGroupId],
});

IAM Best Practices

import * as iam from 'aws-cdk-lib/aws-iam';

// Principle of least privilege — grant only what is needed
handler.addToRolePolicy(new iam.PolicyStatement({
  actions: ['s3:GetObject', 's3:PutObject'],
  resources: [`${bucket.bucketArn}/uploads/*`],   // Scoped to prefix
}));

// Never use wildcards for actions in production
// BAD:  actions: ['s3:*']
// GOOD: actions: ['s3:GetObject', 's3:PutObject']

// Use managed policies for common patterns
taskDef.taskRole.addManagedPolicy(
  iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess')
);

IAM Anti-Patterns

Anti-PatternRiskCorrect Approach
Action: '*'
Full account accessList specific actions
Resource: '*'
Applies to all resourcesScope to specific ARNs
Long-lived access keysKey rotation burden, leak riskUse IAM roles (EC2, ECS, Lambda get them automatically)
Root account for anythingUnrestricted, unauditableCreate IAM users/roles, enable MFA on root
Sharing credentials between servicesBlast radius, no audit trailOne role per service

AWS CDK — Infrastructure as Code

pnpm add -D aws-cdk-lib constructs
npx cdk init app --language typescript

CDK Project Structure

infra/
  bin/app.ts          # Entry point — instantiates stacks
  lib/
    network-stack.ts  # VPC, subnets, security groups
    compute-stack.ts  # ECS, Lambda
    data-stack.ts     # RDS, ElastiCache, S3
    cdn-stack.ts      # CloudFront, Route 53

CDK Commands

npx cdk synth        # Generate CloudFormation template
npx cdk diff         # Preview changes
npx cdk deploy       # Deploy all stacks
npx cdk deploy ComputeStack  # Deploy specific stack
npx cdk destroy      # Tear down

CDK Stack Pattern

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class ComputeStack extends cdk.Stack {
  public readonly serviceUrl: string;

  constructor(scope: Construct, id: string, props: cdk.StackProps & {
    vpc: ec2.IVpc;
    databaseUrl: string;
  }) {
    super(scope, id, props);

    // Resources defined here
    // Export values for cross-stack references
    this.serviceUrl = service.loadBalancer.loadBalancerDnsName;
  }
}

CloudWatch Monitoring

import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as actions from 'aws-cdk-lib/aws-cloudwatch-actions';
import * as sns from 'aws-cdk-lib/aws-sns';

const alarmTopic = new sns.Topic(this, 'AlarmTopic');

// ECS CPU alarm
new cloudwatch.Alarm(this, 'HighCpu', {
  metric: service.service.metricCpuUtilization(),
  threshold: 80,
  evaluationPeriods: 3,
  comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
}).addAlarmAction(new actions.SnsAction(alarmTopic));

// Lambda error rate alarm
new cloudwatch.Alarm(this, 'LambdaErrors', {
  metric: handler.metricErrors({ period: cdk.Duration.minutes(5) }),
  threshold: 5,
  evaluationPeriods: 1,
}).addAlarmAction(new actions.SnsAction(alarmTopic));

// Custom dashboard
const dashboard = new cloudwatch.Dashboard(this, 'AppDashboard');
dashboard.addWidgets(
  new cloudwatch.GraphWidget({
    title: 'ECS CPU & Memory',
    left: [service.service.metricCpuUtilization()],
    right: [service.service.metricMemoryUtilization()],
  }),
);

Cost Optimization

StrategySavingsHow
Right-size instances30-50%Use CloudWatch metrics to identify over-provisioned resources
Spot instances (ECS)Up to 90% on computeUse Fargate Spot for fault-tolerant workloads
Reserved instances30-60%Commit to 1-3 year terms for steady-state workloads
S3 lifecycle rulesVariableMove infrequent data to IA/Glacier automatically
Lambda right-sizing10-40%Use AWS Lambda Power Tuning to find optimal memory
NAT Gateway alternatives$30+/moUse VPC endpoints for S3/DynamoDB, reduce NAT traffic
CloudFront cachingVariableReduce origin requests with proper cache policies
Scheduled scalingVariableScale down non-prod environments nights/weekends

Fargate Spot for cost savings

const service = new ecsPatterns.ApplicationLoadBalancedFargateService(this, 'App', {
  cluster,
  capacityProviderStrategies: [
    { capacityProvider: 'FARGATE_SPOT', weight: 2 },
    { capacityProvider: 'FARGATE', weight: 1 },     // Fallback to on-demand
  ],
  taskImageOptions: { image: ecs.ContainerImage.fromAsset('./') },
});

Common Architecture Patterns

Static Site

Route 53 -> CloudFront -> S3 (static files)

API + Database

Route 53 -> CloudFront -> API Gateway -> Lambda -> RDS
                                               -> ElastiCache (caching)

Containerized Web App

Route 53 -> CloudFront -> ALB -> ECS Fargate (2+ tasks)
                                      |
                                 RDS (private subnet)

Event-Driven Processing

S3 upload -> EventBridge -> Lambda -> DynamoDB
SQS queue -> Lambda -> External API
Schedule  -> EventBridge -> Lambda -> SNS notification

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Public subnets for databasesDirect internet exposurePrivate subnets, access only via application layer
No health checks on ALB targetsTraffic routed to dead instancesConfigure
/health
endpoint, set healthy thresholds
Lambda with VPC when not neededCold start penalty (seconds)Only put Lambda in VPC if it needs private resources
Hardcoding region/account IDsBreaks multi-env deploysUse
cdk.Stack.of(this).region
and env variables
One giant CloudFormation stackSlow deploys, blast radiusSplit into network, compute, data, CDN stacks
Not enabling deletion protectionAccidental
cdk destroy
kills DB
Set
deletionProtection: true
on RDS,
removalPolicy: RETAIN
Storing secrets in environment variablesVisible in console, logsUse AWS Secrets Manager or SSM Parameter Store