All skills
zxkane avatar

/aws-cdk-development

@11d6c5b
by Mengxin Zhuzxkane/aws-skills365 stars
40

AWS Cloud Development Kit (CDK) expert for building cloud infrastructure with TypeScript/Python. Use when creating CDK stacks, defining CDK constructs, implementing infrastructure as code, or when the user mentions CDK, CloudFormation, IaC, cdk synth, cdk deploy, or wants to define AWS infrastructure programmatically. Covers CDK app structure, construct patterns, stack composition, and deployment workflows.

Use this Skill: https://skilld.dev/gh/zxkane/aws-skills/aws-cdk-development

This session only. Nothing lands on disk.

referencescdk-patterns.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

AWS CDK Patterns and Best Practices

This reference provides detailed patterns, anti-patterns, and best practices for AWS CDK development.

Table of Contents

Naming Conventions

Automatic Resource Naming (Recommended)

Let CDK and CloudFormation generate unique resource names automatically:

Benefits:

  • Enables multiple deployments in the same region/account
  • Supports parallel environments (dev, staging, prod)
  • Prevents naming conflicts
  • Allows stack cloning and testing

Example:

// ✅ GOOD - Automatic naming
const bucket = new s3.Bucket(this, 'DataBucket', {
  // No bucketName specified
  encryption: s3.BucketEncryption.S3_MANAGED,
});

When Explicit Naming is Required

Some scenarios require explicit names:

  • Resources referenced by external systems
  • Resources that must maintain consistent names across deployments
  • Cross-stack references requiring stable names

Pattern: Use logical prefixes and environment suffixes

// Only when absolutely necessary
const bucket = new s3.Bucket(this, 'DataBucket', {
  bucketName: `${props.projectName}-data-${props.environment}`,
});

Construct Patterns

L3 Constructs (Patterns)

Prefer high-level patterns that encapsulate best practices:

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

new patterns.LambdaRestApi(this, 'MyApi', {
  handler: myFunction,
  // Includes CloudWatch Logs, IAM roles, and API Gateway configuration
});

Custom Constructs

Create reusable constructs for repeated patterns:

export class ApiWithDatabase extends Construct {
  public readonly api: apigateway.RestApi;
  public readonly table: dynamodb.Table;

  constructor(scope: Construct, id: string, props: ApiWithDatabaseProps) {
    super(scope, id);

    this.table = new dynamodb.Table(this, 'Table', {
      partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });

    const handler = new NodejsFunction(this, 'Handler', {
      entry: props.handlerEntry,
      environment: {
        TABLE_NAME: this.table.tableName,
      },
    });

    this.table.grantReadWriteData(handler);

    this.api = new apigateway.LambdaRestApi(this, 'Api', {
      handler,
    });
  }
}

Security Patterns

IAM Least Privilege

Use grant methods instead of broad policies:

// ✅ GOOD - Specific grants
const table = new dynamodb.Table(this, 'Table', { /* ... */ });
const lambda = new lambda.Function(this, 'Function', { /* ... */ });

table.grantReadWriteData(lambda);

// ❌ BAD - Overly broad permissions
lambda.addToRolePolicy(new iam.PolicyStatement({
  actions: ['dynamodb:*'],
  resources: ['*'],
}));

Secrets Management

Use Secrets Manager for sensitive data:

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

const secret = new secretsmanager.Secret(this, 'DbPassword', {
  generateSecretString: {
    secretStringTemplate: JSON.stringify({ username: 'admin' }),
    generateStringKey: 'password',
    excludePunctuation: true,
  },
});

// Grant read access to Lambda
secret.grantRead(myFunction);

VPC Configuration

Follow VPC best practices:

const vpc = new ec2.Vpc(this, 'Vpc', {
  maxAzs: 2,
  natGateways: 1, // Cost optimization: use 1 for dev, 2+ for prod
  subnetConfiguration: [
    {
      name: 'Public',
      subnetType: ec2.SubnetType.PUBLIC,
      cidrMask: 24,
    },
    {
      name: 'Private',
      subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
      cidrMask: 24,
    },
    {
      name: 'Isolated',
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
      cidrMask: 24,
    },
  ],
});

Lambda Integration

NodejsFunction (TypeScript/JavaScript)

import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';

const fn = new NodejsFunction(this, 'Function', {
  entry: 'src/handlers/process.ts',
  handler: 'handler',
  runtime: lambda.Runtime.NODEJS_20_X,
  timeout: Duration.seconds(30),
  memorySize: 512,
  environment: {
    TABLE_NAME: table.tableName,
  },
  bundling: {
    minify: true,
    sourceMap: true,
    externalModules: ['@aws-sdk/*'], // Use AWS SDK from Lambda runtime
  },
});

PythonFunction

import { PythonFunction } from '@aws-cdk/aws-lambda-python-alpha';

const fn = new PythonFunction(this, 'Function', {
  entry: 'src/handlers',
  index: 'process.py',
  handler: 'handler',
  runtime: lambda.Runtime.PYTHON_3_12,
  timeout: Duration.seconds(30),
  memorySize: 512,
});

Lambda Layers

Share code across functions:

const layer = new lambda.LayerVersion(this, 'CommonLayer', {
  code: lambda.Code.fromAsset('layers/common'),
  compatibleRuntimes: [lambda.Runtime.NODEJS_20_X],
  description: 'Common utilities',
});

new NodejsFunction(this, 'Function', {
  entry: 'src/handler.ts',
  layers: [layer],
});

Testing Patterns

Snapshot Testing

import { Template } from 'aws-cdk-lib/assertions';

test('Stack creates expected resources', () => {
  const app = new cdk.App();
  const stack = new MyStack(app, 'TestStack');

  const template = Template.fromStack(stack);
  expect(template.toJSON()).toMatchSnapshot();
});

Fine-Grained Assertions

test('Lambda has correct environment', () => {
  const app = new cdk.App();
  const stack = new MyStack(app, 'TestStack');

  const template = Template.fromStack(stack);

  template.hasResourceProperties('AWS::Lambda::Function', {
    Runtime: 'nodejs20.x',
    Timeout: 30,
    Environment: {
      Variables: {
        TABLE_NAME: { Ref: Match.anyValue() },
      },
    },
  });
});

Resource Count Validation

test('Stack has correct number of functions', () => {
  const app = new cdk.App();
  const stack = new MyStack(app, 'TestStack');

  const template = Template.fromStack(stack);
  template.resourceCountIs('AWS::Lambda::Function', 3);
});

Cost Optimization

Right-Sizing Lambda

// Development
const devFunction = new NodejsFunction(this, 'DevFunction', {
  memorySize: 256, // Lower for dev
  timeout: Duration.seconds(30),
});

// Production
const prodFunction = new NodejsFunction(this, 'ProdFunction', {
  memorySize: 1024, // Higher for prod performance
  timeout: Duration.seconds(10),
  reservedConcurrentExecutions: 10, // Prevent runaway costs
});

DynamoDB Billing Modes

// Development/Low Traffic
const devTable = new dynamodb.Table(this, 'DevTable', {
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

// Production/Predictable Load
const prodTable = new dynamodb.Table(this, 'ProdTable', {
  billingMode: dynamodb.BillingMode.PROVISIONED,
  readCapacity: 5,
  writeCapacity: 5,
  autoScaling: { /* ... */ },
});

S3 Lifecycle Policies

const bucket = new s3.Bucket(this, 'DataBucket', {
  lifecycleRules: [
    {
      id: 'MoveToIA',
      transitions: [
        {
          storageClass: s3.StorageClass.INFREQUENT_ACCESS,
          transitionAfter: Duration.days(30),
        },
        {
          storageClass: s3.StorageClass.GLACIER,
          transitionAfter: Duration.days(90),
        },
      ],
    },
    {
      id: 'CleanupOldVersions',
      noncurrentVersionExpiration: Duration.days(30),
    },
  ],
});

Anti-Patterns

❌ Hardcoded Values

// BAD
new lambda.Function(this, 'Function', {
  functionName: 'my-function', // Prevents multiple deployments
  code: lambda.Code.fromAsset('lambda'),
  handler: 'index.handler',
  runtime: lambda.Runtime.NODEJS_20_X,
});

// GOOD
new NodejsFunction(this, 'Function', {
  entry: 'src/handler.ts',
  // Let CDK generate the name
});

❌ Overly Broad IAM Permissions

// BAD
function.addToRolePolicy(new iam.PolicyStatement({
  actions: ['*'],
  resources: ['*'],
}));

// GOOD
table.grantReadWriteData(function);

❌ Manual Dependency Management

// BAD - Manual bundling
new lambda.Function(this, 'Function', {
  code: lambda.Code.fromAsset('lambda.zip'), // Pre-bundled manually
  // ...
});

// GOOD - Let CDK handle it
new NodejsFunction(this, 'Function', {
  entry: 'src/handler.ts',
  // CDK handles bundling automatically
});

❌ Missing Environment Variables

// BAD
new NodejsFunction(this, 'Function', {
  entry: 'src/handler.ts',
  // Table name hardcoded in Lambda code
});

// GOOD
new NodejsFunction(this, 'Function', {
  entry: 'src/handler.ts',
  environment: {
    TABLE_NAME: table.tableName,
  },
});

❌ Ignoring Stack Outputs

// BAD - No way to reference resources
new MyStack(app, 'Stack', {});

// GOOD - Export important values
class MyStack extends Stack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    const api = new apigateway.RestApi(this, 'Api', {});

    new CfnOutput(this, 'ApiUrl', {
      value: api.url,
      description: 'API Gateway URL',
      exportName: 'MyApiUrl',
    });
  }
}

Summary

  • Always let CDK generate resource names unless explicitly required
  • Use high-level constructs (L2/L3) over low-level (L1)
  • Prefer grant methods for IAM permissions
  • Leverage NodejsFunction and PythonFunction for automatic bundling
  • Test stacks with assertions and snapshots
  • Optimize costs based on environment (dev vs prod)
  • Validate infrastructure before deployment
  • Document custom constructs and patterns

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides safe and constructive workflows for developing infrastructure using the AWS Cloud Development Kit (CDK). It contains standard shell commands for AWS configuration and template validation without malicious attributes.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    2/3 files flagged

  • ZeroLeaks5mo

    2 findings · Score: 80/100

Signed by skilld at 11d6c5b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 4 months ago
What it can do
Runs commands
MCP servers
cdkaws-mcpawsdocs
context
fork
skills
[
  "aws-mcp-setup"
]
All 8 allowed tools
mcp__cdk__*mcp__aws-mcp__*mcp__awsdocs__*Bash(cdk *)Bash(npm *)Bash(npx *)Bash(aws cloudformation *)Bash(aws sts get-caller-identity)
Other metadata
hooks
{
  "PreToolUse": [
    {
      "matcher": "Bash(cdk deploy*)",
      "command": "aws sts get-caller-identity --query Account --output text",
      "once": true
    }
  ]
}

README badge

README badge for zxkane/aws-skills/aws-cdk-development