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.
npm install @blend-col/alb-to-fargateA 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.
---
``bashInstall the construct and its peer dependencies
npm install @blend-col/alb-to-fargate aws-cdk-lib@2.201.0 constructs@^10.0.0
$3
`bash
npm install @blend-col/alb-to-fargate --legacy-peer-deps
npm install aws-cdk-lib@2.201.0 constructs@^10.0.0
`$3
Peer dependencies ensure compatibility with your existing CDK version and prevent version conflicts in your project. This construct requires:
- aws-cdk-lib@2.201.0 - 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
$3
`ts
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,
},
});
`$3
`ts
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
$3
| 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 |$3
| Property | Type | Required | Description |
|----------|------|----------|-------------|
|
existingVpc | IVpc | β οΈ | Use existing VPC (either this or vpcProps) |
| vpcProps | VpcProps | β οΈ | Properties for new VPC (either this or existingVpc) |$3
| 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 |$3
| 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) |$3
| 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.) |$3
| 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') |$3
| 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:$3
`ts
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 Repository
`$3
#### Configure Auto-Scaling with Custom Metrics
`ts
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
`ts
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
`ts
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
`ts
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
`ts
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,
}],
});
`$3
- VPC: Provide either
existingVpc OR vpcProps
- ALB: Provide either existingLoadBalancerObj OR (loadBalancerProps + listenerProps)
- Container: Provide either containerDefinitionProps OR existingContainerDefinitionObject
- ECS: Provide either clusterProps OR existingFargateServiceObject
- Domains: If providing domain configuration, both domainName AND appDomainName are required
- Rules: If using existingLoadBalancerObj, ruleProps is 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
$3
`ts
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,
},
});
`$3
`ts
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 VPC
`$3
`ts
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,
});
`$3
`ts
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,
},
],
},
});
`$3
`ts
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});
`$3
`ts
// 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 },
],
});
`$3
`ts
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
$3
- 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$3
- Configure appropriate CPU and memory for your containers
- Set reasonable health check intervals
- Use multiple AZs for high availability
- Consider using Application Auto Scaling$3
- Use Fargate Spot capacity when appropriate
- Configure appropriate desired count for your traffic
- Monitor and adjust resource allocation based on usage---
π Troubleshooting
$3
Health Check Failures
`ts
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:
1. Fork this repository
2. Clone your fork:
git clone https://github.com/your-username/aws-cdk-alb-to-fargate.git
3. Install dependencies: npm install
4. Make your changes
5. Build and test: npm run build && npm test
6. Submit a Pull Request$3
`bash
git clone https://github.com/ai-scm/cdk-albToFargate-construct.git
cd cdk-albToFargate-construct
npm install
npm run build
npm test
`$3
`bash
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`---
Made with β€οΈ by Nuvu Labs