Skip to main content
Skip to main content

Best Practices

Learn industry best practices for CDK development with real-world examples. See how to build secure, performant, and cost-effective infrastructure using AWS CDK.

💡 Note: These are general AWS CDK best practices. CDK Insights automatically detects many of these issues, including: unencrypted S3 buckets, overly permissive IAM policies, missing versioning, public access configurations, and more. The examples below show both what to avoid (which CDK Insights will flag) and recommended implementations.

Best Practice Categories

Security

Essential security practices for CDK applications

  • Principle of least privilege
  • Encryption at rest and in transit
  • Network security controls
  • Secrets management

Performance

Optimization strategies for better performance

  • Resource sizing optimization
  • Caching strategies
  • Connection pooling
  • Auto-scaling configuration

Cost Optimization

Cost-effective infrastructure patterns

  • Right-sizing resources
  • Reserved instances usage
  • Lifecycle policies
  • Monitoring and alerting

Maintainability

Code organization and maintainability

  • Consistent naming conventions
  • Modular architecture
  • Comprehensive documentation
  • Testing strategies

Security Best Practices

S3 Bucket Security

Implement secure S3 bucket configuration

❌ Avoid This:

// ❌ Insecure S3 bucket
new s3.Bucket(this, 'DataBucket', {
  publicReadAccess: true,
  removalPolicy: RemovalPolicy.DESTROY,
  // No encryption, no access logging
});

✅ Do This Instead:

// ✅ Secure S3 bucket
new s3.Bucket(this, 'DataBucket', {
  // Block all public access
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
  
  // Enable encryption
  encryption: s3.BucketEncryption.S3_MANAGED,
  
  // Enable versioning
  versioned: true,
  
  // Enable access logging
  serverAccessLogsBucket: accessLogsBucket,
  
  // Lifecycle policy for cost optimization
  lifecycleRules: [
    {
      id: 'DeleteIncompleteMultipartUploads',
      abortIncompleteMultipartUploadAfter: Duration.days(1),
    },
    {
      id: 'TransitionToIA',
      transitions: [
        {
          storageClass: s3.StorageClass.INFREQUENT_ACCESS,
          transitionAfter: Duration.days(30),
        },
      ],
    },
  ],
  
  // Prevent accidental deletion
  removalPolicy: RemovalPolicy.RETAIN,
});

Benefits:

Prevents data exposure
Encrypts data at rest
Maintains audit trail
Optimizes storage costs

IAM Role Best Practices

Create least-privilege IAM roles

❌ Avoid This:

// ❌ Overly permissive IAM role
new iam.Role(this, 'LambdaRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'), // Too broad!
  ],
});

✅ Do This Instead:

// ✅ Least-privilege IAM role
new iam.Role(this, 'LambdaRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
  
  // Use minimal managed policies
  managedPolicies: [
    iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
  ],
  
  // Specific inline policies
  inlinePolicies: {
    S3Access: new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['s3:GetObject'],
          resources: [
            `${dataBucket.bucketArn}/specific-folder/*`,
          ],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['s3:PutObject'],
          resources: [
            `${outputBucket.bucketArn}/processed/*`,
          ],
        }),
      ],
    }),
  },
  
  // Set maximum session duration
  maxSessionDuration: Duration.hours(1),
});

Benefits:

Minimizes security risk
Follows least privilege principle
Easier to audit permissions
Reduces blast radius

VPC Security Configuration

Secure network configuration for VPC

❌ Avoid This:

// ❌ Insecure VPC configuration
const vpc = new ec2.Vpc(this, 'VPC', {
  maxAzs: 2,
});

// Public subnet with no restrictions
const publicSubnet = vpc.publicSubnets[0];
new ec2.SecurityGroup(this, 'WebSG', {
  vpc: vpc,
  allowAllOutbound: true,
});
publicSubnet.securityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.allTraffic(), // Too permissive!
);

✅ Do This Instead:

// ✅ Secure VPC configuration
const vpc = new ec2.Vpc(this, 'VPC', {
  maxAzs: 2,
  subnetConfiguration: [
    {
      cidrMask: 24,
      name: 'Public',
      subnetType: ec2.SubnetType.PUBLIC,
    },
    {
      cidrMask: 24,
      name: 'Private',
      subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS,
    },
    {
      cidrMask: 28,
      name: 'Database',
      subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
    },
  ],
});

// Web tier security group
const webSecurityGroup = new ec2.SecurityGroup(this, 'WebSG', {
  vpc: vpc,
  description: 'Security group for web servers',
  allowAllOutbound: false,
});

webSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(80),
  'Allow HTTP from anywhere'
);

webSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(443),
  'Allow HTTPS from anywhere'
);

// Application tier security group
const appSecurityGroup = new ec2.SecurityGroup(this, 'AppSG', {
  vpc: vpc,
  description: 'Security group for application servers',
  allowAllOutbound: false,
});

appSecurityGroup.addIngressRule(
  webSecurityGroup,
  ec2.Port.tcp(8080),
  'Allow traffic from web tier'
);

Benefits:

Network segmentation
Defense in depth
Controlled access patterns
Easier security monitoring

Performance Best Practices

Lambda Function Optimization

Optimize Lambda function configuration

❌ Inefficient:

// ❌ Inefficient Lambda configuration
new lambda.Function(this, 'DataProcessor', {
  runtime: lambda.Runtime.NODEJS_14_X, // Outdated runtime
  handler: 'index.handler',
  code: lambda.Code.fromAsset('lambda'),
  memorySize: 3008, // Maximum memory (expensive)
  timeout: Duration.minutes(15), // Too long
  // No environment variables, no dead letter queue
});

✅ Optimized:

// ✅ Optimized Lambda configuration
new lambda.Function(this, 'DataProcessor', {
  runtime: lambda.Runtime.NODEJS_18_X, // Latest LTS
  handler: 'index.handler',
  code: lambda.Code.fromAsset('lambda'),
  
  // Right-sized memory based on actual usage
  memorySize: 512,
  
  // Appropriate timeout
  timeout: Duration.minutes(5),
  
  // Environment variables
  environment: {
    LOG_LEVEL: 'INFO',
    MAX_RETRIES: '3',
    S3_BUCKET: dataBucket.bucketName,
  },
  
  // Dead letter queue for failed invocations
  deadLetterQueue: new sqs.Queue(this, 'DLQ'),
  
  // Reserved concurrency to prevent cost spikes
  reservedConcurrency: 10,
  
  // Enable X-Ray tracing
  tracing: lambda.Tracing.ACTIVE,
  
  // Retry configuration
  retryAttempts: 2,
});

Benefits:

Reduced costs
Better error handling
Improved monitoring
Predictable performance

RDS Database Optimization

Optimize RDS database configuration

❌ Inefficient:

// ❌ Inefficient RDS configuration
new rds.DatabaseInstance(this, 'Database', {
  engine: rds.DatabaseInstanceEngine.mysql({
    version: rds.MysqlEngineVersion.VER_5_7, // Outdated version
  }),
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.XLARGE),
  allocatedStorage: 100, // Fixed storage
  storageEncrypted: false, // No encryption
  backupRetention: Duration.days(7), // Short retention
  // No monitoring, no parameter group
});

✅ Optimized:

// ✅ Optimized RDS configuration
new rds.DatabaseInstance(this, 'Database', {
  engine: rds.DatabaseInstanceEngine.mysql({
    version: rds.MysqlEngineVersion.VER_8_0, // Latest version
  }),
  
  // Right-sized instance
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
  
  // Auto-scaling storage
  allocatedStorage: 20,
  maxAllocatedStorage: 100,
  
  // Enable encryption
  storageEncrypted: true,
  storageEncryptionKey: encryptionKey,
  
  // Enhanced monitoring
  monitoringInterval: Duration.seconds(60),
  enablePerformanceInsights: true,
  
  // Backup configuration
  backupRetention: Duration.days(30),
  deleteAutomatedBackups: false,
  
  // Parameter group for optimization
  parameterGroup: new rds.ParameterGroup(this, 'DBParams', {
    engine: rds.DatabaseInstanceEngine.mysql({
      version: rds.MysqlEngineVersion.VER_8_0,
    }),
    parameters: {
      innodb_buffer_pool_size: '{DBInstanceClassMemory*3/4}',
      max_connections: '100',
    },
  }),
  
  // Multi-AZ for high availability
  multiAz: true,
  
  // VPC configuration
  vpc: vpc,
  vpcSubnets: {
    subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
  },
});

Benefits:

Auto-scaling storage
Enhanced monitoring
High availability
Cost optimization

Cost Optimization Best Practices

Auto Scaling Configuration

Implement cost-effective auto scaling

// ✅ Cost-optimized auto scaling
const autoScalingGroup = new autoscaling.AutoScalingGroup(this, 'ASG', {
  vpc: vpc,
  instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
  machineImage: ec2.MachineImage.latestAmazonLinux(),
  minCapacity: 1,
  maxCapacity: 10,
  desiredCapacity: 2,
  
  // Use Spot instances for cost savings
  mixedInstancesPolicy: {
    instancesDistribution: {
      onDemandPercentageAboveBaseCapacity: 20, // 20% on-demand, 80% spot
      spotAllocationStrategy: autoscaling.SpotAllocationStrategy.CAPACITY_OPTIMIZED,
    },
    instancesTypes: [
      ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
      ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL),
    ],
  },
  
  // Scaling policies
  scalingPolicies: [
    {
      id: 'ScaleUp',
      scalingTarget: {
        metric: new cloudwatch.Metric({
          namespace: 'AWS/EC2',
          metricName: 'CPUUtilization',
          statistic: 'Average',
        }),
        targetValue: 70,
      },
      scaleOutCooldown: Duration.minutes(5),
      scaleInCooldown: Duration.minutes(10),
    },
  ],
  
  // Lifecycle hooks for graceful shutdowns
  lifecycleHook: {
    defaultResult: autoscaling.DefaultResult.ABANDON,
    heartbeatTimeout: Duration.minutes(5),
  },
});

Benefits:

Up to 90% cost savings with Spot instances
Automatic scaling based on demand
Graceful instance lifecycle management
Multiple instance types for availability

Implementation Checklist

Security Checklist

  • Enable encryption for all storage services
  • Implement least-privilege IAM policies
  • Use VPC security groups appropriately
  • Enable CloudTrail logging
  • Implement secrets management
  • Use HTTPS/TLS everywhere

Performance Checklist

  • Right-size all compute resources
  • Implement auto-scaling policies
  • Use appropriate caching strategies
  • Enable monitoring and alerting
  • Optimize database configurations
  • Use CDN for static content

Ready to Implement Best Practices?

Start applying these best practices to your CDK projects and use CDK Insights to validate your implementations.