Skip to main content

Progressive Lambda

GitHub license Maintained

A production-ready AWS CDK construct that creates a NodeJS Lambda function with progressive deployment capabilities, integrated monitoring, and automatic rollback functionality using AWS CodeDeploy and CloudWatch.

Overview

The ProgressiveLambda construct combines several AWS services to provide a robust serverless deployment solution:

  • Progressive Deployments: Uses AWS CodeDeploy to safely shift traffic between Lambda versions
  • Error Monitoring: CloudWatch alarms that trigger automatic rollbacks when error thresholds are exceeded
  • Observability: X-Ray tracing, optional dashboard widgets, and comprehensive metrics
  • Security Best Practices: Latest Node.js runtime, proper IAM roles, and CDK Nag compliance

Key Features

🚀 Progressive Deployment Strategies

  • Linear: Gradually shift traffic in equal increments (e.g., 10% every minute)
  • Canary: Deploy to a small percentage, validate, then complete rollout
  • Blue/Green: All-at-once deployment with instant rollback capability

📊 Automated Monitoring & Rollback

  • CloudWatch alarms based on custom error metrics
  • Automatic rollback when error threshold (10 errors/minute) is breached
  • SNS notifications for deployment events and alarm states

📈 Built-in Observability

  • X-Ray tracing enabled by default for request tracking
  • Optional CloudWatch dashboard widgets (success, error, alarm status)
  • Custom metrics integration for business-specific monitoring

🔒 Security & Compliance

  • Uses Node.js 22.x runtime by default (latest security patches)
  • CDK Nag compliant with documented security considerations
  • Proper IAM role configuration with least privilege

Usage

Basic Example

import * as cdk from 'aws-cdk-lib';
import * as codeDeploy from 'aws-cdk-lib/aws-codedeploy';
import * as sns from 'aws-cdk-lib/aws-sns';
import { Construct } from 'constructs';
import { ProgressiveLambda } from '@leighton-digital/cloud-blocks';

export class MyStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

// Create CodeDeploy application
const application = new codeDeploy.LambdaApplication(this, 'MyApp');

// Create SNS topic for alerts
const alertTopic = new sns.Topic(this, 'Alerts');

// Create progressive Lambda with linear deployment
const progressiveLambda = new ProgressiveLambda(this, 'OrderProcessor', {
entry: 'src/handlers/process-order.ts',
stageName: 'prod',
application,
deploymentConfig: codeDeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE,
alarmEnabled: true,
snsTopic: alertTopic,
namespace: 'ECommerce',
serviceName: 'OrderProcessor',
metricErrorName: 'ProcessingErrors',
region: 'us-east-1',
// Optional: Custom metric names and titles (will use defaults if not specified)
metricSuccessName: 'ProcessedOrders',
metricSuccessNameTitle: 'Successfully Processed Orders',
metricErrorNameTitle: 'Order Processing Failures',
// Optional: Lambda function configuration
timeout: cdk.Duration.seconds(30),
memorySize: 512,
environment: {
STAGE: 'prod'
}
});
}
}

Minimal Configuration with Defaults

// Create progressive Lambda using default metric names and titles
const minimalLambda = new ProgressiveLambda(this, 'SimpleProcessor', {
entry: 'src/handlers/simple-processor.ts',
stageName: 'prod',
application,
deploymentConfig: codeDeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE,
alarmEnabled: true,
snsTopic: alertTopic,
namespace: 'MyApp',
serviceName: 'SimpleProcessor',
metricErrorName: 'ProcessingErrors',
region: 'us-east-1'
// metricSuccessName defaults to 'SimpleProcessor-SuccessOperation'
// metricSuccessNameTitle defaults to 'SimpleProcessor - Success Operation'
// metricErrorNameTitle defaults to 'SimpleProcessor - Error Operation'
});

Canary Deployment Example

const canaryLambda = new ProgressiveLambda(this, 'RiskyFeature', {
entry: 'src/handlers/new-feature.ts',
stageName: 'prod',
application,
// Deploy 10% of traffic, wait 5 minutes, then proceed or rollback
deploymentConfig: codeDeploy.LambdaDeploymentConfig.CANARY_10PERCENT_5MINUTES,
alarmEnabled: true,
snsTopic: alertTopic,
namespace: 'MyApp',
serviceName: 'NewFeature',
metricErrorName: 'FeatureErrors',
region: 'us-east-1',
// Optional: Custom metric names and titles
metricSuccessName: 'SuccessfulRequests',
metricSuccessNameTitle: 'Successful Feature Requests',
metricErrorNameTitle: 'Feature Processing Errors'
});

Custom Alarm Configuration

// Create progressive Lambda with custom alarm settings
const customAlarmLambda = new ProgressiveLambda(this, 'SensitiveProcessor', {
entry: 'src/handlers/sensitive-processor.ts',
stageName: 'prod',
application,
deploymentConfig: codeDeploy.LambdaDeploymentConfig.LINEAR_10PERCENT_EVERY_1MINUTE,
alarmEnabled: true,
snsTopic: alertTopic,
namespace: 'CriticalApp',
serviceName: 'SensitiveProcessor',
metricErrorName: 'CriticalErrors',
region: 'us-east-1',
// Custom alarm configuration for more sensitive error detection
alarmConfiguration: {
threshold: 5, // Trigger on 5 errors instead of default 10
evaluationPeriods: 2, // Require 2 consecutive periods
treatMissingData: cloudwatch.TreatMissingData.BREACHING // Treat missing data as breaching
}
});

With Dashboard Widgets

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

const lambda = new ProgressiveLambda(this, 'MonitoredFunction', {
// ... other configuration
createWidget: true // Enable dashboard widgets
});

// Create dashboard and add widgets
const dashboard = new cloudwatch.Dashboard(this, 'MyDashboard', {
dashboardName: 'Lambda-Monitoring'
});

dashboard.addWidgets(...lambda.widgets);

Lambda Function Integration

Your Lambda function code should publish the specified success and error metrics to enable proper monitoring:

import { CloudWatch } from 'aws-sdk';

const cloudwatch = new CloudWatch();

export const handler = async (event: any) => {
try {
// Your business logic here
const result = await processOrder(event);

// Publish success metric
await cloudwatch.putMetricData({
Namespace: 'ECommerce',
MetricData: [{
MetricName: 'ProcessedOrders',
Value: 1,
Dimensions: [{ Name: 'service', Value: 'OrderProcessor' }],
Timestamp: new Date()
}]
}).promise();

return {
statusCode: 200,
body: JSON.stringify(result)
};
} catch (error) {
// Publish error metric
await cloudwatch.putMetricData({
Namespace: 'ECommerce',
MetricData: [{
MetricName: 'ProcessingErrors',
Value: 1,
Dimensions: [{ Name: 'service', Value: 'OrderProcessor' }],
Timestamp: new Date()
}]
}).promise();

throw error;
}
};

Configuration Reference

Required Properties

PropertyTypeDescription
stageNamestringDeployment stage (e.g., 'dev', 'prod')
applicationLambdaApplicationCodeDeploy application for deployments
deploymentConfigILambdaDeploymentConfigTraffic shifting strategy
alarmEnabledbooleanWhether alarms trigger actions
snsTopicTopicSNS topic for notifications
namespacestringCloudWatch metrics namespace
serviceNamestringService dimension for metrics
metricErrorNamestringName of error metric
regionstringAWS region for resources

Optional Properties

PropertyTypeDefaultDescription
createWidgetbooleanfalseCreate CloudWatch dashboard widgets
metricSuccessNamestring${id}-SuccessOperationName of success metric
metricSuccessNameTitlestring${id} - Success OperationDisplay title for success metric
metricErrorNameTitlestring${id} - Error OperationDisplay title for error metric
alarmConfigurationobjectSee defaults belowCustom CloudWatch alarm settings
runtimeRuntimeNODEJS_22_XLambda runtime version
timeoutDuration30 secondsFunction timeout
memorySizenumber256Memory allocation in MB
tracingTracingACTIVEX-Ray tracing configuration

Alarm Configuration Defaults

The alarmConfiguration object supports the following optional properties:

PropertyTypeDefaultDescription
thresholdnumber10Number of errors to trigger alarm
evaluationPeriodsnumber1Number of consecutive periods to trigger
treatMissingDataTreatMissingDataNOT_BREACHINGHow to handle missing metric data

Deployment Configurations

ConfigurationDescriptionUse Case
LINEAR_10PERCENT_EVERY_1MINUTEShift 10% every minuteGradual, safe deployments
LINEAR_10PERCENT_EVERY_2MINUTESShift 10% every 2 minutesConservative deployments
CANARY_10PERCENT_5MINUTES10% canary for 5 minutesValidation before full rollout
ALL_AT_ONCEImmediate blue/green switchFast deployments with instant rollback

Metric Name Defaults

When optional metric properties are not specified, the construct generates sensible defaults using the construct ID:

  • metricSuccessName: Defaults to ${id}-SuccessOperation (e.g., OrderProcessor-SuccessOperation)
  • metricSuccessNameTitle: Defaults to ${id} - Success Operation (e.g., OrderProcessor - Success Operation)
  • metricErrorNameTitle: Defaults to ${id} - Error Operation (e.g., OrderProcessor - Error Operation)

These defaults ensure that each Lambda function has unique metric names and descriptive titles without requiring explicit configuration for every deployment.


Monitoring & Alerting

Alarm Configuration

The construct creates a CloudWatch alarm with the following default settings:

  • Threshold: 10 errors within 1 minute triggers alarm
  • Evaluation: Single period for fast rollback response
  • Missing Data: Treated as NOT_BREACHING to avoid false alarms
  • Actions: SNS notification + automatic deployment rollback

These settings can be customized using the optional alarmConfiguration property:

alarmConfiguration: {
threshold: 5, // Lower threshold for critical services
evaluationPeriods: 2, // Require multiple periods to reduce false alarms
treatMissingData: cloudwatch.TreatMissingData.BREACHING // Change missing data behavior
}

Common Alarm Configuration Patterns

  • Sensitive Services: Lower threshold (3-5 errors) with single evaluation period
  • High-Traffic Services: Higher threshold (15-20 errors) to account for normal error rates
  • Development Environments: Multiple evaluation periods (2-3) to reduce noise
  • Critical Production: Treat missing data as breaching to ensure monitoring coverage

Dashboard Widgets (Optional)

When createWidget: true, three widgets are created:

  1. Success Metric Widget: Shows successful operations count
  2. Error Metric Widget: Shows error/failure count
  3. Alarm Status Widget: Visual alarm state indicator

Security Considerations

SNS Topic SSL Enforcement

For security compliance (AwsSolutions-SNS3), configure your SNS topic to require SSL:

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

const topic = new sns.Topic(this, 'SecureTopic');

topic.addToResourcePolicy(new iam.PolicyStatement({
effect: iam.Effect.DENY,
principals: [new iam.AnyPrincipal()],
actions: ['sns:Publish'],
resources: [topic.topicArn],
conditions: {
Bool: { 'aws:SecureTransport': 'false' }
}
}));

CDK Nag Compliance

This construct is CDK Nag compliant with the following documented exceptions:

  • AwsSolutions-IAM4: Uses AWS managed policies for standard Lambda execution roles
  • AwsSolutions-IAM5: X-Ray tracing requires wildcard permissions for trace segments


API Reference

ProgressiveLambda Class

The main construct that creates a Lambda function with progressive deployment capabilities.

Public Properties

PropertyTypeDescription
lambdaNodejsFunctionThe underlying Lambda function
aliasAliasLambda alias for traffic management
alarmAlarmCloudWatch alarm for error monitoring
deploymentGroupLambdaDeploymentGroupCodeDeploy deployment group
widgetsConcreteWidget[]Dashboard widgets (when enabled)

Example Property Usage

const progressiveLambda = new ProgressiveLambda(this, 'MyFunction', { /* config */ });

// Access the underlying Lambda function
progressiveLambda.lambda.addEnvironment('NEW_VAR', 'value');

// Get the deployment group for additional configuration
const deploymentGroup = progressiveLambda.deploymentGroup;

// Add widgets to a dashboard
if (progressiveLambda.widgets.length > 0) {
dashboard.addWidgets(...progressiveLambda.widgets);
}

Best Practices

1. Metric Publishing Strategy

  • Publish metrics synchronously in critical paths to ensure accurate error tracking
  • Use dimensioned metrics to enable filtering and aggregation
  • Include timestamp for accurate time-series data

2. Deployment Configuration Selection

  • Linear: Use for steady-state applications with predictable traffic
  • Canary: Use for high-risk deployments or new features
  • All-at-once: Use only for non-critical functions or when speed is essential

3. Alarm Tuning

  • Default threshold (10 errors/minute) works well for most applications
  • Lower thresholds (3-5 errors) for critical services requiring immediate attention
  • Higher thresholds (15-20 errors) for high-traffic applications with expected error rates
  • Multiple evaluation periods reduce false alarms but increase detection time
  • Test alarm sensitivity in staging environments before production deployment
  • Consider missing data treatment based on your monitoring requirements

4. Dashboard Organization

  • Group related Lambda functions in the same dashboard
  • Use consistent naming conventions for metrics
  • Include both technical and business metrics

Troubleshooting

Common Issues

Deployment Stuck in Progress

Cause: Alarm not receiving metric data or alarm threshold too sensitive.

Solutions:

  • Verify your Lambda function is publishing the specified error metric
  • Check CloudWatch Logs for metric publishing errors
  • Temporarily disable alarms (alarmEnabled: false) for testing
  • Review alarm threshold and evaluation period in alarmConfiguration
  • Consider adjusting treatMissingData if metrics are intermittent

Automatic Rollbacks Occurring

Cause: Error threshold exceeded during deployment.

Solutions:

  • Check CloudWatch metrics for actual error rates
  • Review Lambda function logs for unexpected errors
  • Validate that success metrics are being published correctly
  • Consider adjusting alarm threshold using alarmConfiguration.threshold
  • Increase evaluationPeriods to require sustained error rates before triggering

Missing Dashboard Widgets

Cause: createWidget not enabled or widgets not added to dashboard.

Solutions:

  • Set createWidget: true in construct properties
  • Add widgets to dashboard: dashboard.addWidgets(...lambda.widgets)
  • Verify metrics are being published with correct namespace and dimensions

Contributing

Please read CONTRIBUTING.md for details on our code of conduct and the process for submitting pull requests.