@blend-col/alb-to-fargate
v1.0.6
Published
A **high-level AWS CDK Construct** for rapidly deploying a secure, scalable Application Load Balancer (ALB) in front of an ECS Fargate service. Handles VPC, ECS cluster, ECR, Route53, ACM, and more — all with minimal configuration.
Keywords
Readme
🚀 aws-cdk-alb-to-fargate
A high-level AWS CDK Construct for rapidly deploying a secure, scalable Application Load Balancer (ALB) in front of an ECS Fargate service. Handles VPC, ECS cluster, ECR, Route53, ACM, and more — all with minimal configuration.
📦 Installation
Option 1: Install with Peer Dependencies (Recommended)
# Install the construct and its peer dependencies
npm install @blend-col/alb-to-fargate [email protected] constructs@^10.0.0
# Or with yarn
yarn add @blend-col/alb-to-fargate [email protected] constructs@^10.0.0Option 2: Install with Legacy Peer Deps (If conflicts occur)
npm install @blend-col/alb-to-fargate --legacy-peer-deps
npm install [email protected] constructs@^10.0.0Why Peer Dependencies?
Peer dependencies ensure compatibility with your existing CDK version and prevent version conflicts in your project. This construct requires:
- [email protected] - Exact CDK version for guaranteed compatibility
- constructs@^10.0.0 - CDK constructs library for infrastructure components
By using peer dependencies, you maintain control over your CDK version while ensuring this construct works seamlessly with your existing infrastructure code.
⚡️ Quick Start
Basic Example (Create New ALB)
import * as cdk from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { Repository } from 'aws-cdk-lib/aws-ecr';
import { Certificate } from 'aws-cdk-lib/aws-certificatemanager';
import { ApplicationProtocol, SslPolicy, ApplicationTargetGroupProps } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { AlbToFargate } from '@blend-col/alb-to-fargate';
const app = new cdk.App();
const stack = new cdk.Stack(app, 'MyStack');
// Use an existing VPC
const vpc = Vpc.fromLookup(stack, 'Vpc', { isDefault: true });
// Reference your ECR repository
const ecrRepository = Repository.fromRepositoryName(
stack,
'MyRepo',
'my-app-repo'
);
// (Optional) Use an existing ACM certificate
const certificate = Certificate.fromCertificateArn(
stack,
'Cert',
'arn:aws:acm:us-east-1:123456789012:certificate/abcdefg'
);
new AlbToFargate(stack, 'MyAlbFargate', {
namespace: 'MyApp',
publicApi: true,
existingVpc: vpc,
ecrRepository: ecrRepository,
ecrImageVersion: 'latest',
domainName: 'example.com',
appDomainName: 'api.example.com',
certificate: certificate,
loadBalancerProps: {
vpc,
internetFacing: true,
},
listenerProps: {
port: 443,
protocol: ApplicationProtocol.HTTPS,
sslPolicy: SslPolicy.RECOMMENDED,
certificates: [certificate],
},
targetGroupProps: {
port: 80,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/',
interval: cdk.Duration.seconds(30),
healthyThresholdCount: 2,
timeout: cdk.Duration.seconds(5),
},
} as ApplicationTargetGroupProps,
clusterProps: {
vpc,
clusterName: 'MyCluster'
},
fargateTaskDefinitionProps: {
cpu: 256,
memoryLimitMiB: 512,
},
fargateServiceProps: {
desiredCount: 2,
},
});Using Existing ALB
import { ApplicationLoadBalancer, ApplicationListener } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { ListenerCondition } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
// Reference existing ALB and listener
const existingAlb = ApplicationLoadBalancer.fromApplicationLoadBalancerAttributes(
stack,
'ExistingALB',
{
loadBalancerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/1234567890123456',
securityGroupId: 'sg-12345678',
vpc: vpc,
}
);
const existingListener = ApplicationListener.fromApplicationListenerAttributes(
stack,
'ExistingListener',
{
listenerArn: 'arn:aws:elasticloadbalancing:us-east-1:123456789012:listener/app/my-alb/1234567890123456/0123456789012345',
securityGroup: existingAlb.connections.securityGroups[0],
}
);
new AlbToFargate(stack, 'MyAlbFargateExisting', {
namespace: 'MyApp',
publicApi: true,
existingVpc: vpc,
ecrRepository: ecrRepository,
ecrImageVersion: 'latest',
existingLoadBalancerObj: {
loadBalancer: existingAlb,
listener: existingListener,
},
targetGroupProps: {
port: 80,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/health',
},
} as ApplicationTargetGroupProps,
ruleProps: {
priority: 100,
conditions: [
ListenerCondition.pathPatterns(['/api/*']),
],
},
});🛠️ Configuration Properties
Core Configuration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| namespace | string | ❌ | Prefix for all resource names (defaults to construct ID) |
| publicApi | boolean | ✅ | Whether to make the ALB internet-facing (true) or internal/private (false). Set to false for internal services only accessible within your VPC |
VPC Configuration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| existingVpc | IVpc | ⚠️ | Use existing VPC (either this or vpcProps) |
| vpcProps | VpcProps | ⚠️ | Properties for new VPC (either this or existingVpc) |
Container & ECR
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| ecrRepository | IRepository | ✅ | ECR repository containing your container image |
| ecrImageVersion | string | ❌ | Docker image tag (defaults to 'latest') |
| containerDefinitionProps | ContainerDefinitionProps | ❌ | Custom container configuration |
| existingContainerDefinitionObject | ContainerDefinition | ❌ | Use existing container definition |
Load Balancer Configuration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| loadBalancerProps | LoadBalancerProps | ⚠️ | ALB configuration (either this or existingLoadBalancerObj) |
| existingLoadBalancerObj | existingLoadBalancerObj | ⚠️ | Use existing ALB + listener |
| listenerProps | ListenerProps \| ApplicationListenerProps | ❌ | ALB listener configuration (required with loadBalancerProps) |
| targetGroupProps | ApplicationTargetGroupProps | ✅ | Target group settings for health checks and routing |
| ruleProps | AddRuleProps | ❌ | Listener rules (required with existingLoadBalancerObj) |
ECS Configuration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| clusterProps | ClusterProps | ⚠️ | ECS cluster configuration (either this or existingFargateServiceObject) |
| existingFargateServiceObject | FargateService | ⚠️ | Use existing Fargate service |
| fargateTaskDefinitionProps | FargateTaskDefinitionProps | ❌ | Task definition settings (CPU, memory) |
| fargateServiceProps | FargateServiceProps | ❌ | Service settings (desired count, etc.) |
SSL/TLS & DNS
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| certificate | ICertificate | ❌ | ACM certificate for HTTPS |
| domainName | string | ❌ | Root domain (e.g., 'example.com') |
| appDomainName | string | ❌ | Full domain (e.g., 'api.example.com') |
Logging
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| logAlbAccessLogs | boolean | ❌ | Enable ALB access logging |
| albLoggingBucketProps | BucketProps | ❌ | S3 bucket configuration for ALB logs |
🔗 Accessing Construct Properties
The AlbToFargate construct exposes all created resources as public readonly properties, allowing you to access and configure them further:
Available Properties
const albFargate = new AlbToFargate(stack, 'MyService', { /* props */ });
// Access all created resources
const vpc = albFargate.vpc; // VPC instance
const cluster = albFargate.cluster; // ECS Cluster
const taskDefinition = albFargate.taskDefinition; // Fargate Task Definition
const fargateService = albFargate.fargateService; // Fargate Service
const loadBalancer = albFargate.loadBalancer; // Application Load Balancer
const listener = albFargate.listener; // ALB Listener
const targetGroup = albFargate.targetGroupService; // Target Group
const containerDefinition = albFargate.containerDefinition; // Container Definition
const certificate = albFargate.certificate; // ACM Certificate (if created)
const hostedZone = albFargate.hostedZone; // Route53 Hosted Zone (if created)
const ecrRepository = albFargate.ecrRepository; // ECR RepositoryAdvanced Configuration Examples
Configure Auto-Scaling with Custom Metrics
const apiService = new AlbToFargate(stack, 'APIService', {
// ... basic configuration
fargateServiceProps: {
desiredCount: 2,
enableAutoScaling: true,
autoScaleTaskCount: {
minCapacity: 2,
maxCapacity: 10,
},
},
});
// Access the Fargate service for advanced auto-scaling configuration
const scalingTarget = apiService.fargateService!.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10,
});
// Scale based on CPU utilization
scalingTarget.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.minutes(5),
scaleOutCooldown: cdk.Duration.minutes(2),
});
// Scale based on memory utilization
scalingTarget.scaleOnMemoryUtilization('MemoryScaling', {
targetUtilizationPercent: 80,
});
// Scale based on ALB request count
scalingTarget.scaleOnMetric('RequestCountScaling', {
metric: apiService.targetGroupService!.metricRequestCountPerTarget(),
scalingSteps: [
{ upper: 100, change: -1 },
{ lower: 500, change: +1 },
{ lower: 1000, change: +2 },
],
});Add Additional ALB Listeners and Rules
const webService = new AlbToFargate(stack, 'WebService', {
// ... configuration
});
// Add a redirect from HTTP to HTTPS
webService.loadBalancer!.addListener('HttpRedirect', {
port: 80,
protocol: ApplicationProtocol.HTTP,
defaultAction: ListenerAction.redirect({
protocol: 'HTTPS',
port: '443',
permanent: true,
}),
});
// Add additional rules to existing listener
webService.listener!.addRule('StaticContentRule', {
priority: 50,
conditions: [
ListenerCondition.pathPatterns(['/static/*', '/assets/*']),
],
action: ListenerAction.fixedResponse(200, {
contentType: 'text/plain',
messageBody: 'Static content served by CDN',
}),
});Configure CloudWatch Alarms
import { Alarm, Metric, TreatMissingData } from 'aws-cdk-lib/aws-cloudwatch';
import { SnsAction } from 'aws-cdk-lib/aws-cloudwatch-actions';
const monitoredService = new AlbToFargate(stack, 'MonitoredService', {
// ... configuration
});
// Create CloudWatch alarms
const highCpuAlarm = new Alarm(stack, 'HighCpuAlarm', {
metric: monitoredService.fargateService!.metricCpuUtilization(),
threshold: 80,
evaluationPeriods: 2,
treatMissingData: TreatMissingData.NOT_BREACHING,
});
const highMemoryAlarm = new Alarm(stack, 'HighMemoryAlarm', {
metric: monitoredService.fargateService!.metricMemoryUtilization(),
threshold: 85,
evaluationPeriods: 2,
});
const unhealthyTargetsAlarm = new Alarm(stack, 'UnhealthyTargetsAlarm', {
metric: monitoredService.targetGroupService!.metricUnhealthyHostCount(),
threshold: 1,
evaluationPeriods: 1,
});
// Add SNS notifications (assuming you have an SNS topic)
// highCpuAlarm.addAlarmAction(new SnsAction(alertTopic));Access Load Balancer for Additional Configuration
import { CfnLoadBalancer } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
const service = new AlbToFargate(stack, 'Service', {
// ... configuration
});
// Configure additional ALB attributes
const cfnLoadBalancer = service.loadBalancer!.node.defaultChild as CfnLoadBalancer;
cfnLoadBalancer.addPropertyOverride('LoadBalancerAttributes', [
{
Key: 'idle_timeout.timeout_seconds',
Value: '60',
},
{
Key: 'routing.http2.enabled',
Value: 'true',
},
{
Key: 'access_logs.s3.enabled',
Value: 'true',
},
{
Key: 'access_logs.s3.bucket',
Value: 'my-alb-logs-bucket',
},
]);
// Get load balancer DNS name for outputs
new cdk.CfnOutput(stack, 'LoadBalancerDNS', {
value: service.loadBalancer!.loadBalancerDnsName,
description: 'Load Balancer DNS Name',
});Configure Task Definition with Additional Containers
import * as ecs from 'aws-cdk-lib/aws-ecs';
const multiContainerService = new AlbToFargate(stack, 'MultiContainerService', {
// ... basic configuration
});
// Add a sidecar container (e.g., for logging or monitoring)
multiContainerService.taskDefinition.addContainer('LoggingContainer', {
image: ecs.ContainerImage.fromRegistry('fluent/fluent-bit:latest'),
memoryLimitMiB: 128,
cpu: 64,
essential: false,
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'sidecar-logging',
}),
environment: {
FLB_LOG_LEVEL: 'info',
},
});
// Add a monitoring sidecar
multiContainerService.taskDefinition.addContainer('MonitoringContainer', {
image: ecs.ContainerImage.fromRegistry('prom/node-exporter:latest'),
memoryLimitMiB: 64,
cpu: 32,
essential: false,
portMappings: [{
containerPort: 9100,
protocol: ecs.Protocol.TCP,
}],
});🔄 Mutual Exclusivity Rules
- VPC: Provide either
existingVpcORvpcProps - ALB: Provide either
existingLoadBalancerObjOR (loadBalancerProps+listenerProps) - Container: Provide either
containerDefinitionPropsORexistingContainerDefinitionObject - ECS: Provide either
clusterPropsORexistingFargateServiceObject - Domains: If providing domain configuration, both
domainNameANDappDomainNameare required - Rules: If using
existingLoadBalancerObj,rulePropsis required
🏗️ Architecture Overview
This construct creates a complete serverless web application infrastructure:
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Route 53 │ │ ALB │ │ ECS Fargate │
│ (Optional) │───▶│ │───▶│ │
│ │ │ • HTTPS/HTTP │ │ • Auto-scaling │
│ │ │ • Health Check │ │ • Private/Pub │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────┐
│ Target Group │
│ │
│ • Health Checks │
│ • Port Mapping │
└─────────────────┘🚀 Features
- ✅ Flexible VPC: Use existing VPC or create new one
- ✅ SSL/TLS: Automatic certificate management with ACM
- ✅ Health Checks: Configurable health check endpoints
- ✅ Auto Scaling: Built-in ECS service auto-scaling
- ✅ Security: Proper security groups and IAM roles
- ✅ Logging: Optional ALB access logging to S3
- ✅ DNS: Optional Route53 integration
- ✅ Existing Resources: Support for existing ALB, ECS services, etc.
🔧 Comprehensive Deployment Examples
1. Simple HTTP API Service
import * as cdk from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { Repository } from 'aws-cdk-lib/aws-ecr';
import { ApplicationProtocol } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
import { AlbToFargate } from '@blend-col/alb-to-fargate';
const simpleApi = new AlbToFargate(stack, 'SimpleAPI', {
namespace: 'simple-api',
publicApi: true,
existingVpc: vpc,
ecrRepository: repository,
ecrImageVersion: 'latest',
loadBalancerProps: {
vpc,
internetFacing: true,
},
listenerProps: {
name: 'HttpListener',
port: 80,
protocol: ApplicationProtocol.HTTP,
},
targetGroupProps: {
port: 3000,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(30),
healthyThresholdCount: 2,
},
},
clusterProps: {
clusterName: 'SimpleAPICluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 256,
memoryLimitMiB: 512,
},
fargateServiceProps: {
desiredCount: 2,
assignPublicIp: false,
},
});2. Private (Internal) ALB Service
import * as ecs from 'aws-cdk-lib/aws-ecs';
const privateApi = new AlbToFargate(stack, 'PrivateAPI', {
namespace: 'private-api',
publicApi: false, // Creates an internal ALB
existingVpc: vpc,
ecrRepository: repository,
ecrImageVersion: 'latest',
loadBalancerProps: {
vpc,
internetFacing: false, // Internal ALB - only accessible within VPC
loadBalancerName: 'PrivateALB',
},
listenerProps: {
name: 'PrivateListener',
port: 80,
protocol: ApplicationProtocol.HTTP,
},
targetGroupProps: {
port: 8080,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/internal/health',
interval: cdk.Duration.seconds(30),
},
},
clusterProps: {
clusterName: 'PrivateCluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 512,
memoryLimitMiB: 1024,
},
fargateServiceProps: {
serviceName: 'private-api-service',
desiredCount: 2,
assignPublicIp: false, // No public IP - runs in private subnets
vpcSubnets: {
subnets: vpc.privateSubnets,
},
},
containerDefinitionProps: {
containerName: 'private-api-container',
image: ecs.ContainerImage.fromEcrRepository(repository),
memoryLimitMiB: 1024,
cpu: 512,
portMappings: [{
containerPort: 8080,
protocol: ecs.Protocol.TCP,
}],
environment: {
ENVIRONMENT: 'private',
INTERNAL_SERVICE: 'true',
},
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'private-api',
}),
},
});
// The private ALB is only accessible from within the VPC
// Useful for internal microservices, backend APIs, or services
// that should only be accessed by other services in your VPC3. Production HTTPS Service with Auto-Scaling
import { Certificate } from 'aws-cdk-lib/aws-certificatemanager';
import { SslPolicy } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
const productionApi = new AlbToFargate(stack, 'ProductionAPI', {
namespace: 'prod-api',
publicApi: true,
existingVpc: vpc,
ecrRepository: repository,
ecrImageVersion: 'v1.2.0',
domainName: 'mycompany.com',
appDomainName: 'api.mycompany.com',
certificate: certificate,
loadBalancerProps: {
vpc,
internetFacing: true,
loadBalancerName: 'ProductionALB',
},
listenerProps: {
name: 'HttpsListener',
port: 443,
protocol: ApplicationProtocol.HTTPS,
sslPolicy: SslPolicy.RECOMMENDED,
certificates: [certificate],
},
targetGroupProps: {
port: 8080,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/api/health',
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
},
},
clusterProps: {
clusterName: 'ProductionCluster',
vpc,
containerInsights: true,
},
fargateTaskDefinitionProps: {
cpu: 1024,
memoryLimitMiB: 2048,
},
fargateServiceProps: {
serviceName: 'production-api-service',
desiredCount: 3,
assignPublicIp: false,
enableAutoScaling: true,
minHealthyPercent: 50,
maxHealthyPercent: 200,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE',
weight: 1,
base: 2,
},
{
capacityProvider: 'FARGATE_SPOT',
weight: 4,
base: 0,
},
],
autoScaleTaskCount: {
minCapacity: 2,
maxCapacity: 10,
},
},
containerDefinitionProps: {
containerName: 'api-container',
memoryLimitMiB: 2048,
cpu: 1024,
portMappings: [{
containerPort: 8080,
protocol: 'tcp',
}],
environment: {
NODE_ENV: 'production',
LOG_LEVEL: 'info',
},
logging: {
streamPrefix: 'production-api',
},
},
logAlbAccessLogs: true,
});
// Configure additional auto-scaling based on CPU utilization
const scalingTarget = productionApi.fargateService!.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 10,
});
scalingTarget.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.minutes(5),
scaleOutCooldown: cdk.Duration.minutes(2),
});
scalingTarget.scaleOnMemoryUtilization('MemoryScaling', {
targetUtilizationPercent: 80,
});4. Microservice with Existing ALB and Path-Based Routing
import { ListenerCondition } from 'aws-cdk-lib/aws-elasticloadbalancingv2';
const userService = new AlbToFargate(stack, 'UserService', {
namespace: 'user-service',
publicApi: true,
existingVpc: vpc,
ecrRepository: userServiceRepository,
ecrImageVersion: 'latest',
existingLoadBalancerObj: {
loadBalancer: existingAlb,
listener: existingListener,
},
ruleProps: {
priority: 100,
conditions: [
ListenerCondition.pathPatterns(['/api/users/*']),
ListenerCondition.hostHeaders(['api.mycompany.com']),
],
},
targetGroupProps: {
port: 3000,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/api/users/health',
matcher: '200,204',
},
},
clusterProps: {
clusterName: 'MicroservicesCluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 512,
memoryLimitMiB: 1024,
},
fargateServiceProps: {
serviceName: 'user-service',
desiredCount: 2,
assignPublicIp: false,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE_SPOT',
weight: 1,
base: 1,
},
],
},
});5. Development Environment with Custom Container Configuration
import { LogDrivers } from 'aws-cdk-lib/aws-ecs';
const devApi = new AlbToFargate(stack, 'DevAPI', {
namespace: 'dev-api',
publicApi: true,
existingVpc: vpc,
ecrRepository: repository,
ecrImageVersion: 'dev-latest',
loadBalancerProps: {
vpc,
internetFacing: true,
loadBalancerName: 'DevALB',
},
listenerProps: {
name: 'DevListener',
port: 80,
protocol: ApplicationProtocol.HTTP,
},
targetGroupProps: {
port: 3000,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(60),
healthyThresholdCount: 2,
},
},
clusterProps: {
clusterName: 'DevCluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 256,
memoryLimitMiB: 512,
},
fargateServiceProps: {
serviceName: 'dev-api-service',
desiredCount: 1,
assignPublicIp: false,
},
containerDefinitionProps: {
containerName: 'dev-api-container',
memoryLimitMiB: 512,
cpu: 256,
portMappings: [{
containerPort: 3000,
protocol: 'tcp',
}],
environment: {
NODE_ENV: 'development',
DEBUG: 'true',
LOG_LEVEL: 'debug',
},
logging: LogDrivers.awsLogs({
streamPrefix: 'dev-api',
logRetention: 7, // 7 days retention for dev
}),
},
});
// Access the created resources for additional configuration
console.log(`Load Balancer DNS: ${devApi.loadBalancer!.loadBalancerDnsName}`);
console.log(`Cluster ARN: ${devApi.cluster.clusterArn}`);6. Multi-Environment Service with Shared ALB
// Shared ALB for multiple services
const sharedAlb = new ApplicationLoadBalancer(stack, 'SharedALB', {
vpc,
internetFacing: true,
loadBalancerName: 'SharedMicroservicesALB',
});
const httpsListener = sharedAlb.addListener('HttpsListener', {
port: 443,
protocol: ApplicationProtocol.HTTPS,
certificates: [certificate],
sslPolicy: SslPolicy.RECOMMENDED,
});
// Service 1: Auth Service
const authService = new AlbToFargate(stack, 'AuthService', {
namespace: 'auth-service',
publicApi: true,
existingVpc: vpc,
ecrRepository: authRepository,
ecrImageVersion: 'v2.1.0',
existingLoadBalancerObj: {
loadBalancer: sharedAlb,
listener: httpsListener,
},
ruleProps: {
priority: 10,
conditions: [
ListenerCondition.pathPatterns(['/auth/*']),
],
},
targetGroupProps: {
port: 8080,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: { path: '/auth/health' },
},
clusterProps: {
clusterName: 'AuthCluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 512,
memoryLimitMiB: 1024,
},
fargateServiceProps: {
desiredCount: 2,
assignPublicIp: false,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE',
weight: 1,
base: 1,
},
],
},
});
// Service 2: Payment Service
const paymentService = new AlbToFargate(stack, 'PaymentService', {
namespace: 'payment-service',
publicApi: true,
existingVpc: vpc,
ecrRepository: paymentRepository,
ecrImageVersion: 'v1.5.2',
existingLoadBalancerObj: {
loadBalancer: sharedAlb,
listener: httpsListener,
},
ruleProps: {
priority: 20,
conditions: [
ListenerCondition.pathPatterns(['/payments/*']),
],
},
targetGroupProps: {
port: 9000,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: { path: '/payments/health' },
},
clusterProps: {
clusterName: 'PaymentCluster',
vpc,
},
fargateTaskDefinitionProps: {
cpu: 1024,
memoryLimitMiB: 2048,
},
fargateServiceProps: {
desiredCount: 3,
assignPublicIp: false,
enableAutoScaling: true,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE',
weight: 2,
base: 1,
},
{
capacityProvider: 'FARGATE_SPOT',
weight: 3,
base: 0,
},
],
autoScaleTaskCount: {
minCapacity: 2,
maxCapacity: 8,
},
},
});
// Configure auto-scaling for payment service based on custom metrics
const paymentScalingTarget = paymentService.fargateService!.autoScaleTaskCount({
minCapacity: 2,
maxCapacity: 8,
});
paymentScalingTarget.scaleOnMetric('RequestCountScaling', {
metric: paymentService.targetGroupService!.metricRequestCountPerTarget(),
scalingSteps: [
{ upper: 100, change: -1 },
{ lower: 500, change: +1 },
{ lower: 1000, change: +2 },
],
});7. High-Availability Service with Logging and Monitoring
import { BucketProps } from 'aws-cdk-lib/aws-s3';
import { RemovalPolicy } from 'aws-cdk-lib';
const haService = new AlbToFargate(stack, 'HAService', {
namespace: 'ha-service',
publicApi: true,
existingVpc: vpc,
ecrRepository: repository,
ecrImageVersion: 'stable',
domainName: 'mycompany.com',
appDomainName: 'ha-api.mycompany.com',
certificate: certificate,
loadBalancerProps: {
vpc,
internetFacing: true,
loadBalancerName: 'HA-ALB',
crossZone: true,
},
listenerProps: {
name: 'HAListener',
port: 443,
protocol: ApplicationProtocol.HTTPS,
sslPolicy: SslPolicy.RECOMMENDED,
certificates: [certificate],
},
targetGroupProps: {
port: 8080,
protocol: ApplicationProtocol.HTTP,
targetType: 'ip',
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(15),
timeout: cdk.Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 2,
},
deregistrationDelay: cdk.Duration.seconds(30),
},
clusterProps: {
clusterName: 'HACluster',
vpc,
containerInsights: true,
},
fargateTaskDefinitionProps: {
cpu: 2048,
memoryLimitMiB: 4096,
},
fargateServiceProps: {
serviceName: 'ha-service',
desiredCount: 4,
assignPublicIp: false,
enableAutoScaling: true,
minHealthyPercent: 75,
maxHealthyPercent: 200,
capacityProviderStrategies: [
{
capacityProvider: 'FARGATE',
weight: 1,
base: 2,
},
],
autoScaleTaskCount: {
minCapacity: 4,
maxCapacity: 20,
},
vpcSubnets: {
subnets: vpc.privateSubnets,
},
},
containerDefinitionProps: {
containerName: 'ha-api-container',
memoryLimitMiB: 4096,
cpu: 2048,
portMappings: [{
containerPort: 8080,
protocol: 'tcp',
}],
environment: {
NODE_ENV: 'production',
LOG_LEVEL: 'info',
METRICS_ENABLED: 'true',
},
logging: LogDrivers.awsLogs({
streamPrefix: 'ha-service',
logRetention: 30,
}),
},
logAlbAccessLogs: true,
albLoggingBucketProps: {
bucketName: 'ha-service-alb-logs',
removalPolicy: RemovalPolicy.RETAIN,
lifecycleRules: [{
id: 'DeleteOldLogs',
expiration: cdk.Duration.days(90),
}],
} as BucketProps,
});
// Configure comprehensive auto-scaling
const haScalingTarget = haService.fargateService!.autoScaleTaskCount({
minCapacity: 4,
maxCapacity: 20,
});
haScalingTarget.scaleOnCpuUtilization('CpuScaling', {
targetUtilizationPercent: 60,
scaleInCooldown: cdk.Duration.minutes(10),
scaleOutCooldown: cdk.Duration.minutes(3),
});
haScalingTarget.scaleOnMemoryUtilization('MemoryScaling', {
targetUtilizationPercent: 70,
scaleInCooldown: cdk.Duration.minutes(10),
scaleOutCooldown: cdk.Duration.minutes(3),
});
// Add custom metric scaling based on ALB request count
haScalingTarget.scaleOnMetric('RequestCountScaling', {
metric: haService.loadBalancer!.metricRequestCount(),
scalingSteps: [
{ upper: 1000, change: -2 },
{ lower: 5000, change: +2 },
{ lower: 10000, change: +4 },
],
adjustmentType: AdjustmentType.CHANGE_IN_CAPACITY,
});
// Output important information
new cdk.CfnOutput(stack, 'HAServiceURL', {
value: `https://${haService.loadBalancer!.loadBalancerDnsName}`,
description: 'HA Service Load Balancer URL',
});
new cdk.CfnOutput(stack, 'HAServiceClusterArn', {
value: haService.cluster.clusterArn,
description: 'HA Service ECS Cluster ARN',
});📋 Prerequisites
- AWS CDK v2.x installed
- Node.js 18+ or TypeScript 4.7+
- ECR repository with your container image
- (Optional) Existing VPC, ALB, or ECS resources
🎯 Best Practices
Security
- Always use HTTPS in production (
ApplicationProtocol.HTTPS) - Enable ALB access logging for audit trails
- Use specific security groups and VPC configurations
- Implement proper health check endpoints
Performance
- Configure appropriate CPU and memory for your containers
- Set reasonable health check intervals
- Use multiple AZs for high availability
- Consider using Application Auto Scaling
Cost Optimization
- Use Fargate Spot capacity when appropriate
- Configure appropriate desired count for your traffic
- Monitor and adjust resource allocation based on usage
🔍 Troubleshooting
Common Issues
Health Check Failures
targetGroupProps: {
healthCheck: {
path: '/health',
interval: cdk.Duration.seconds(30),
timeout: cdk.Duration.seconds(5),
healthyThresholdCount: 2,
unhealthyThresholdCount: 3,
},
}Certificate Issues
- Ensure your certificate is in the same region as your ALB
- Verify domain validation is complete
- Check that certificate covers your domain
Container Issues
- Verify your container exposes the correct port
- Check container logs in CloudWatch
- Ensure proper IAM permissions for ECR access
🤝 Contributing
We welcome contributions! Here's how to get started:
- Fork this repository
- Clone your fork:
git clone https://github.com/your-username/aws-cdk-alb-to-fargate.git - Install dependencies:
npm install - Make your changes
- Build and test:
npm run build && npm test - Submit a Pull Request
Development Setup
git clone https://github.com/ai-scm/cdk-albToFargate-construct.git
cd cdk-albToFargate-construct
npm install
npm run build
npm testRunning Tests
npm test # Run all tests
npm run test:watch # Run tests in watch mode📄 License
MIT - see the LICENSE file for details.
🆘 Support
- 📚 Documentation: Check this README and inline code comments
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📧 Email: For enterprise support, contact us
🏷️ Tags
aws-cdk fargate ecs alb application-load-balancer serverless containers infrastructure-as-code typescript aws
