Jenkins shared library and CDK constructs for AWS infrastructure. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
38 KiB
CDK Synth Command Examples
This document provides comprehensive examples of pnpm exec cdk synth commands for all stack types and configurations.
Prerequisites
cd spicy-automation
pnpm install
pnpm run build
Common Warnings
The following warnings are expected and non-critical:
-
Route Table ID Warnings: When importing VPCs, route table IDs are optional. These warnings can be ignored.
[Warning] No routeTableId was provided to the subnet 'subnet-xxx' -
Construct Metadata Warnings: CDK internal warnings about managed policies. These are informational only.
[Warning] Failed to add construct metadata for node [ExecutionRole]
CloudFormation Import Pattern (Minimal Props)
The CDK implementation supports the same CloudFormation import pattern as the old Ansible/CloudFormation templates:
- Minimal props: Only
clusterNamerequired for most deployments - Auto-imports: VPC ID, listener ARNs, and logs bucket auto-import from CloudFormation exports
- No defaults: All required values must exist in exports or be provided explicitly - fails early if missing
Export chain:
- VPC stack exports:
${vpcStackName}-VPCID,${vpcStackName}-VPCCIDR,${vpcStackName}-PrivateSubnetA1ID, etc. - Cluster stack exports:
${clusterName}-VPC,${clusterName}-internet-facing-https-listener,${clusterName}-logs-s3-bucket - ALB stack exports:
${albStackName}-internet-facing-arn,${albStackName}-internet-facing-https-listener,${albStackName}-internet-facing-http-listener
Important: The implementation fails early if required exports don't exist. There are no defaults - either the data exists in CloudFormation exports, or the synth fails with a clear error message.
VPC Stack
Minimal VPC (Default Configuration)
pnpm exec cdk synth \
-c stackType=vpc \
-c stackName=my-vpc \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=vpc
Creates:
- VPC with default CIDR (172.1.0.0/16)
- 4 Availability Zones
- Public subnets (one per AZ)
- Private subnets Type 1 (one per AZ with NAT Gateways)
- Private subnets Type 2 (one per AZ with NACLs)
- S3 Gateway Endpoint
Custom VPC Configuration
pnpm exec cdk synth \
-c stackType=vpc \
-c stackName=production-vpc \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=network \
-c vpcCidr=10.0.0.0/16 \
-c numberOfAzs=3 \
-c availabilityZones=ca-central-1a,ca-central-1b,ca-central-1c \
-c createPrivateSubnets=true \
-c createAdditionalPrivateSubnets=true
Development VPC (2 AZs, No NACL Subnets)
pnpm exec cdk synth \
-c stackType=vpc \
-c stackName=dev-vpc \
-c ownerTag=Dev \
-c productTag=myapp \
-c componentTag=vpc \
-c numberOfAzs=2 \
-c createAdditionalPrivateSubnets=false
Public-Only VPC (No NAT Gateways)
pnpm exec cdk synth \
-c stackType=vpc \
-c stackName=public-vpc \
-c ownerTag=Dev \
-c productTag=myapp \
-c componentTag=vpc \
-c createPrivateSubnets=false
ECS Cluster Stack
Minimal Cluster (No Load Balancers) - Using CloudFormation Imports
Minimal props approach: Only vpcStackName required. All VPC details auto-import from VPC stack exports.
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=my-cluster \
-c vpcStackName=my-vpc \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=ecs-cluster \
-c environment=dev
What auto-imports from VPC stack:
- VPC ID from
${vpcStackName}-VPCID - VPC CIDR from
${vpcStackName}-VPCCIDR - Number of AZs from
${vpcStackName}-NumberOfAZs - Private subnet IDs from
${vpcStackName}-PrivateSubnetA1ID,${vpcStackName}-PrivateSubnetB1ID, etc. - Public subnet IDs from
${vpcStackName}-PublicSubnetAID,${vpcStackName}-PublicSubnetBID, etc. (ifcreateExternalLoadBalancer=true) - Availability zones auto-derived from region and number of AZs
Cluster with External ALB - Using CloudFormation Imports
Minimal props:
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=my-cluster \
-c vpcStackName=my-vpc \
-c createExternalLoadBalancer=true \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=ecs-cluster \
-c environment=dev
What auto-imports:
- All VPC details (VPC ID, CIDR, subnets, AZs) from VPC stack exports
- Public subnets auto-imported for external ALB when
createExternalLoadBalancer=true
Cluster with Both ALBs
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=production-cluster \
-c vpcStackName=production-vpc \
-c createExternalLoadBalancer=true \
-c createInternalLoadBalancer=true \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=ecs-cluster \
-c environment=prod
Cluster with Spot Instances
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=dev-cluster \
-c vpcStackName=dev-vpc \
-c spotEnabled=true \
-c onDemandPercentage=20 \
-c spotAllocationStrategy=capacity-optimized \
-c instanceType=m5a.large \
-c minClusterSize=2 \
-c maxClusterSize=4 \
-c ownerTag=Dev \
-c productTag=myapp \
-c componentTag=ecs-cluster \
-c environment=dev
Cluster with Fargate Enabled
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=hybrid-cluster \
-c vpcStackName=my-vpc \
-c enableFargate=true \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=ecs-cluster \
-c environment=dev
ALB Stack (Persistent bg-common ALB)
The ALB stack creates a persistent Application Load Balancer that can be shared across multiple service deployments. This is the "bg-common" pattern where the ALB persists even when service stacks are deleted and recreated.
Important: The ALB stack must be deployed before service stacks that reference it. The ALB ARN and listener ARNs from the ALB stack outputs are required as inputs to the service stack.
Internet-Facing ALB
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-xxx,subnet-yyy \
-c scheme=internet-facing \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=dev
Creates:
- Application Load Balancer (internet-facing)
- Security Group for ALB
- HTTP Listener (port 80)
- HTTPS Listener (port 443) with SSL certificate
- HTTP→HTTPS redirect rule
- ALB access logs to S3 (if logsBucketName provided)
Internal ALB
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-aaa,subnet-bbb \
-c scheme=internal \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=dev
ALB with Blue/Green DNS
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-xxx,subnet-yyy \
-c scheme=internet-facing \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c activeHostname=api.example.com \
-c inactiveHostname=inactive-api.example.com \
-c bgHostedZoneId=Z1234567890 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=prod
Creates:
- All resources from internet-facing ALB example
- Route53 A record for active hostname (api.example.com)
- Route53 A record for inactive hostname (inactive-api.example.com)
ECS Service Stack
Minimal Service (No Load Balancer) - Using CloudFormation Imports
Minimal props approach: Only clusterName is required. VPC info auto-imports from cluster stack exports.
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
What auto-imports:
- VPC stack name from
${clusterStackName}-VPCStackName(cluster stack export) - VPC ID from
${clusterStackName}-VPC(cluster stack export) - VPC CIDR from
${vpcStackName}-VPCCIDR(VPC stack export, using imported VPC stack name) - Private subnets from
${vpcStackName}-PrivateSubnetA1ID, etc. (VPC stack exports) - Number of AZs from
${vpcStackName}-NumberOfAZs(VPC stack export) - Logs bucket from
${clusterStackName}-logs-s3-bucket(cluster stack export)
Note: The service stack imports the VPC stack name from the cluster stack export ${clusterStackName}-VPCStackName, then uses that to import all VPC details (CIDR, subnets, AZs) from the VPC stack exports.
Service with Cluster ALB - Using CloudFormation Imports
Minimal props: Listener ARN auto-imports from cluster stack.
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c hostHeader=api.example.com \
-c priority=100 \
-c useExternalALB=true \
-c healthCheckPath=/health \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
What auto-imports:
- VPC stack name from
${clusterStackName}-VPCStackName(cluster stack export) - VPC ID from
${clusterStackName}-VPC(cluster stack export) - VPC CIDR from
${vpcStackName}-VPCCIDR(VPC stack export, using imported VPC stack name) - Private subnets from
${vpcStackName}-PrivateSubnetA1ID, etc. (VPC stack exports) - Number of AZs from
${vpcStackName}-NumberOfAZs(VPC stack export) - External listener ARN from
${clusterStackName}-internet-facing-https-listener(whenuseExternalALB=true) - Internal listener ARN from
${clusterStackName}-internal-https-listener(whenuseInternalALB=true)
Explicit listener ARN (backward compatible):
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c privateSubnetIds=subnet-aaa,subnet-bbb \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c hostHeader=api.example.com \
-c priority=100 \
-c useExternalALB=true \
-c externalListenerArn=arn:aws:elasticloadbalancing:ca-central-1:123456789:listener/app/test-alb/1234567890123456/1234567890123456 \
-c healthCheckPath=/health \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
Service with bg-common ALB (Default - useClusterAlb=false)
Important: When using bg-common ALB, the ALB and Route53 DNS records are created in a separate ALB stack, not in the service stack. The service stack only creates listener rules on the existing ALB.
Step 1: Deploy ALB Stack (creates ALB and Route53 records)
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-xxx,subnet-yyy \
-c scheme=internet-facing \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=dev
Step 2: Deploy Service Stack (creates listener rules on existing ALB)
Minimal props using CloudFormation imports:
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c albStackName=my-service-alb \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c healthCheckPath=/health \
-c hostHeader=api.example.com \
-c priority=100 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
What auto-imports:
- VPC ID from
${clusterName}-VPC(cluster stack export) - VPC CIDR from
${clusterName}-VPCCIDR(cluster stack export) - Private subnets from
${clusterName}-PrivateSubnetA1ID, etc. (cluster stack exports) - Availability zones from
${clusterName}-AvailabilityZones(cluster stack export) - ALB ARN from
${albStackName}-internet-facing-arn(ALB stack export) - HTTPS listener ARN from
${albStackName}-internet-facing-https-listener(ALB stack export) - HTTP listener ARN from
${albStackName}-internet-facing-http-listener(ALB stack export) - Logs bucket from
${clusterName}-logs-s3-bucket(cluster stack export)
Note: vpcStackName is optional - the service stack prefers importing from the cluster stack. The ALB stack name (albStackName) is automatically derived from the service stackName (e.g., my-service-dev → my-service-dev-alb).
Note: The service creates listener rules on both HTTP and HTTPS listeners (like the old template), so both listeners are imported if available.
Note: In the Jenkins pipeline, the ALB stack is created automatically. For manual CDK synth, you must deploy the ALB stack first and use albStackName for auto-imports.
Service with bg-common ALB (Internal)
Step 1: Deploy Internal ALB Stack
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-aaa,subnet-bbb \
-c scheme=internal \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=dev
Step 2: Deploy Service Stack
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c albStackName=my-service-alb \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c healthCheckPath=/health \
-c hostHeader=api-internal.example.com \
-c priority=100 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
Service with bg-common ALB + Blue/Green Deployment
Note: Blue/green deployments are handled automatically by the Jenkins pipeline. The pipeline:
- Automatically creates the bg-common ALB stack (if it doesn't exist)
- Deploys to the inactive color (blue or green)
- Runs
blueGreenTestagainst the inactive hostname - Waits for deployment to stabilize
- Swaps hostnames to make the new deployment active
- Runs
smokeTestagainst the new active hostname - Keeps the old stack for rollback window (default 2 hours)
For manual CDK synth, deploy both stacks:
Step 1: Deploy ALB Stack with Blue/Green DNS
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=my-service-alb \
-c vpcId=vpc-12345678 \
-c vpcCidrBlock=10.0.0.0/16 \
-c availabilityZones=ca-central-1a,ca-central-1b \
-c subnetIds=subnet-xxx,subnet-yyy \
-c scheme=internet-facing \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/xxx \
-c logsBucketName=my-cluster-logs \
-c activeHostname=api.example.com \
-c inactiveHostname=inactive-api.example.com \
-c bgHostedZoneId=Z1234567890 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=alb \
-c environment=prod
Step 2: Deploy Service Stack (Blue or Green)
After deploying the ALB stack, use albStackName for auto-imports:
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service-blue \
-c clusterName=my-cluster \
-c albStackName=my-service-alb \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c activeHostname=api.example.com \
-c inactiveHostname=inactive-api.example.com \
-c bgHostedZoneId=Z1234567890 \
-c healthCheckPath=/health \
-c hostHeader=api.example.com \
-c priority=100 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=prod
How blue/green works:
- ALB stack creates DNS records for both active and inactive hostnames (both point to the same ALB)
- Pipeline determines current active color from SSM Parameter Store (defaults to 'blue')
- Pipeline deploys new version to inactive color (green if blue is active, blue if green is active)
- Pipeline runs
blueGreenTesthook against inactive hostname - Pipeline swaps hostnames by updating listener rule priorities
- Pipeline runs
smokeTesthook against new active hostname - Old stack is retained for rollback window (configurable via
rollbackWindowHours) - Rollback is instant - just swaps hostnames back (no new deployment needed)
Service with Auto-Scaling
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c desiredCount=2 \
-c minCapacity=2 \
-c maxCapacity=20 \
-c targetCpuUtilization=70 \
-c targetMemoryUtilization=80 \
-c targetRequestsPerTarget=1000 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
Service with Environment Variables and Secrets
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c environment_vars='{"NODE_ENV":"production","LOG_LEVEL":"info"}' \
-c secrets='{"DATABASE_URL":"arn:aws:secretsmanager:ca-central-1:123456789:secret:db-url","API_KEY":"arn:aws:secretsmanager:ca-central-1:123456789:secret:api-key"}' \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
Service with Mixed Capacity Strategy (EC2 + Fargate)
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=80 \
-c cpu=512 \
-c memory=1024 \
-c capacityProviderStrategy='[{"capacityProvider":"my-cluster-ec2","base":2,"weight":3},{"capacityProvider":"FARGATE_SPOT","weight":1}]' \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=dev
Service with Custom Resource Limits
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=my-service \
-c clusterName=my-cluster \
-c serviceName=my-service \
-c image=nginx:latest \
-c containerPort=3000 \
-c cpu=1024 \
-c memory=2048 \
-c desiredCount=4 \
-c ownerTag=MyTeam \
-c productTag=myproduct \
-c componentTag=service \
-c environment=prod
Complete Production Example
Full Stack Deployment Sequence
# 1. Deploy VPC
pnpm exec cdk synth \
-c stackType=vpc \
-c stackName=production-vpc \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=vpc \
-c numberOfAzs=4 \
-c availabilityZones=ca-central-1a,ca-central-1b,ca-central-1c,ca-central-1d
# 2. Deploy ECS Cluster
pnpm exec cdk synth \
-c stackType=ecs-cluster \
-c stackName=production-cluster \
-c vpcStackName=production-vpc \
-c createExternalLoadBalancer=true \
-c createInternalLoadBalancer=true \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/prod \
-c instanceType=m5a.xlarge \
-c minClusterSize=3 \
-c maxClusterSize=10 \
-c spotEnabled=true \
-c onDemandPercentage=50 \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=ecs-cluster \
-c environment=prod
# 3. Deploy ALB Stack (bg-common pattern)
pnpm exec cdk synth \
-c stackType=alb \
-c stackName=api-service-prod-alb \
-c vpcStackName=production-vpc \
-c numberOfAzs=3 \
-c scheme=internet-facing \
-c certificateArn=arn:aws:acm:ca-central-1:123456789:certificate/prod \
-c logsBucketName=production-cluster-ca-central-1-alb-logs \
-c activeHostname=api.example.com \
-c inactiveHostname=inactive-api.example.com \
-c bgHostedZoneId=Z1234567890 \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=alb \
-c environment=prod
# 4. Deploy Service Stack (references existing ALB via albStackName)
pnpm exec cdk synth \
-c stackType=ecs-service \
-c stackName=api-service-prod-blue \
-c clusterName=production-cluster \
-c albStackName=api-service-prod-alb \
-c serviceName=api-service \
-c image=nexus.kodeniks.com/docker-hosted/api-service:v1.2.3 \
-c containerPort=3000 \
-c cpu=512 \
-c memory=1024 \
-c activeHostname=api.example.com \
-c inactiveHostname=inactive-api.example.com \
-c bgHostedZoneId=Z1234567890 \
-c desiredCount=4 \
-c minCapacity=4 \
-c maxCapacity=24 \
-c targetCpuUtilization=70 \
-c targetMemoryUtilization=80 \
-c targetRequestsPerTarget=1000 \
-c healthCheckPath=/health \
-c hostHeader=api.example.com \
-c priority=100 \
-c ownerTag=Platform \
-c productTag=spicy \
-c componentTag=api \
-c environment=prod
Parameter Reference
Common Parameters (All Stacks)
| Parameter | Required | Description | Example |
|---|---|---|---|
stackType |
✅ | Stack type: vpc, ecs-cluster, ecs-service |
vpc |
stackName |
✅ | CloudFormation stack name | my-vpc |
ownerTag |
✅ | Owner tag | MyTeam |
productTag |
✅ | Product tag | myproduct |
componentTag |
✅ | Component tag | vpc |
environment |
⚠️ | Environment (required for cluster/service) | dev |
VPC-Specific Parameters
| Parameter | Default | Description |
|---|---|---|
vpcCidr |
172.1.0.0/16 |
VPC CIDR block |
numberOfAzs |
4 |
Number of AZs (2, 3, or 4) |
availabilityZones |
auto | Comma-separated AZ list |
createPrivateSubnets |
true |
Create private subnets |
createAdditionalPrivateSubnets |
true |
Create NACL-protected subnets |
ECS Cluster Parameters
| Parameter | Default | Description |
|---|---|---|
vpcStackName |
✅ Required | VPC stack name - all VPC details (VPC ID, CIDR, subnets, AZs) auto-import from VPC stack exports |
instanceType |
m5a.large |
EC2 instance type |
minClusterSize |
2 |
Minimum instances |
maxClusterSize |
4 |
Maximum instances |
createExternalLoadBalancer |
false |
Create external ALB (public subnets auto-imported from VPC stack if enabled) |
createInternalLoadBalancer |
false |
Create internal ALB |
certificateArn |
- | SSL certificate ARN |
spotEnabled |
false |
Enable Spot instances |
onDemandPercentage |
100 |
On-demand percentage |
enableFargate |
false |
Enable Fargate providers |
ALB Stack Parameters
| Parameter | Default | Description |
|---|---|---|
vpcId |
- | VPC ID (required, or use vpcStackName to import) |
vpcCidrBlock |
- | VPC CIDR (required, or use vpcStackName to import) |
availabilityZones |
- | Comma-separated AZs (required, or use vpcStackName + numberOfAzs to import) |
subnetIds |
- | Subnet IDs for ALB (required) |
scheme |
internet-facing |
ALB scheme: internet-facing or internal |
certificateArn |
- | SSL certificate ARN (required for HTTPS) |
logsBucketName |
- | S3 bucket name for ALB access logs |
logsPrefix |
- | S3 prefix for ALB access logs |
idleTimeout |
60 |
ALB idle timeout in seconds |
redirectHttpToHttps |
true |
Enable HTTP→HTTPS redirect |
hostName |
- | Simple hostname (e.g., api.example.com) - auto-generates active/inactive hostnames (like old HostName) |
activeHostname |
- | Active hostname for blue/green DNS |
inactiveHostname |
- | Inactive hostname for blue/green DNS |
bgHostedZoneId |
- | Route53 hosted zone ID for blue/green DNS (optional - only needed if creating DNS records) |
ECS Service Parameters
| Parameter | Default | Description |
|---|---|---|
clusterName |
- | ECS cluster name (required) - used to auto-import VPC stack name, VPC ID, and listener ARNs from cluster stack exports |
clusterStackName |
- | Cluster stack name (defaults to clusterName) - used for CloudFormation imports |
vpcStackName |
- | VPC stack name (auto-imported from ${clusterStackName}-VPCStackName, or provide explicitly) |
albStackName |
- | ALB stack name (for bg-common ALB) - used to auto-import ALB ARN and listener ARNs from ALB stack |
vpcId |
- | VPC ID (required, or auto-imported from ${clusterStackName}-VPC) |
vpcCidrBlock |
- | VPC CIDR (required, or auto-imported from ${vpcStackName}-VPCCIDR using imported VPC stack name) |
availabilityZones |
- | Comma-separated AZs (required, or auto-derived from VPC stack imports with numberOfAzs) |
privateSubnetIds |
- | Private subnet IDs (required, or auto-imported from ${vpcStackName}-PrivateSubnetA1ID, etc.) |
numberOfAzs |
- | Number of AZs (required when importing subnets from VPC stack) |
publicSubnetIds |
- | Public subnet IDs (for individual ALB) |
serviceName |
- | Service name (required) |
image |
- | Docker image URI (required) |
containerPort |
3000 |
Container port |
cpu |
256 |
CPU units |
memory |
512 |
Memory in MiB |
desiredCount |
2 |
Desired task count |
bg-common ALB Parameters (useClusterAlb=false)
When useClusterAlb is false or not set, the service uses a bg-common ALB from a separate ALB stack. These parameters reference the existing ALB:
Minimal props using CloudFormation imports:
- Provide
albStackNameto auto-import ALB ARN and listener ARNs - Service creates listener rules on both HTTP and HTTPS listeners (like the old template)
| Parameter | Default | Description |
|---|---|---|
albStackName |
- | ALB stack name - used to auto-import ALB ARN and listener ARNs (preferred) |
existingAlbArn |
- | ALB ARN (auto-imported from ${albStackName}-internet-facing-arn, or provide explicitly) |
existingAlbHttpsListenerArn |
- | HTTPS listener ARN (auto-imported from ${albStackName}-internet-facing-https-listener, or provide explicitly) |
existingAlbHttpListenerArn |
- | HTTP listener ARN (auto-imported from ${albStackName}-internet-facing-http-listener, or provide explicitly) |
hostHeader |
- | Host header for listener rule |
priority |
100 |
Listener rule priority (lower = higher precedence) |
stickiness |
true |
Enable session stickiness on target group |
stickinessDuration |
86400 |
Session stickiness duration in seconds |
deregistrationDelay |
60 |
Target deregistration delay in seconds |
hostName |
- | Simple hostname (e.g., api.example.com) - auto-generates active/inactive (like old HostName) |
activeHostname |
- | Active hostname for blue/green DNS (must match ALB stack config) |
inactiveHostname |
- | Inactive hostname for blue/green DNS (must match ALB stack config) |
bgHostedZoneId |
- | Route53 hosted zone ID (optional - only needed if creating DNS records) |
Blue/Green Deployment Parameters
| Parameter | Default | Description |
|---|---|---|
blueGreen |
false |
Enable blue/green deployment (pipeline handles color switching automatically) |
hostName |
- | Simple hostname (e.g., api.example.com) - auto-generates activeHostname and inactiveHostname (like old HostName) |
activeHostname |
- | Active hostname (e.g., api.example.com) - used by bg-common ALB stack to create DNS records |
inactiveHostname |
- | Inactive hostname (e.g., inactive-api.example.com) - used by bg-common ALB stack to create DNS records |
bgHostedZoneId |
- | Route53 hosted zone ID (for bg-common ALB stack DNS records) - required for DNS records (hostnames are optional) |
rollbackWindowHours |
2 |
Hours to keep old stack for rollback (default 2 hours) |
Note: Hostnames are optional. Services without hostnames (pub/sub workers, background jobs) don't need DNS records. Only provide bgHostedZoneId if you want Route53 DNS records created.
Cloudflare DNS Delegation: If using Cloudflare for DNS, you can delegate a subdomain to AWS Route53:
- Create a Route53 hosted zone for the subdomain (e.g.,
production.mydomain.com) - Point Cloudflare NS records for
*.production.mydomain.comto the Route53 name servers - Use the Route53 hosted zone ID as
bgHostedZoneId - AWS will manage DNS records for all services under that subdomain
Cluster ALB Parameters (useClusterAlb=true)
When useClusterAlb is true, the service uses the cluster's shared ALB:
Minimal props using CloudFormation imports:
- Provide
clusterNameto auto-import listener ARNs from cluster stack exports - Listener ARNs auto-import from
${clusterName}-internet-facing-https-listeneror${clusterName}-internal-https-listener
| Parameter | Default | Description |
|---|---|---|
useClusterAlb |
false |
Set to true to use cluster ALB |
hostHeader |
- | Host header for routing |
pathPatterns |
- | Comma-separated path patterns |
priority |
100 |
Listener rule priority |
useExternalALB |
false |
Use external cluster ALB (auto-imports listener ARN from ${clusterName}-internet-facing-https-listener) |
useInternalALB |
false |
Use internal cluster ALB (auto-imports listener ARN from ${clusterName}-internal-https-listener) |
externalListenerArn |
- | External listener ARN (auto-imported when useExternalALB=true, or provide explicitly) |
internalListenerArn |
- | Internal listener ARN (auto-imported when useInternalALB=true, or provide explicitly) |
Auto-Scaling Parameters
| Parameter | Default | Description |
|---|---|---|
minCapacity |
- | Minimum tasks |
maxCapacity |
- | Maximum tasks |
targetCpuUtilization |
- | Target CPU % (0-100) |
targetMemoryUtilization |
- | Target memory % (0-100) |
targetRequestsPerTarget |
- | Target requests per task per minute |
Testing Checklist
When running synth commands, verify:
- ✅ No errors (only expected warnings)
- ✅ Resources are created as expected
- ✅ Logical IDs match expected patterns
- ✅ Tags are applied correctly
- ✅ Outputs are exported correctly
Troubleshooting
Common Issues
- Missing required parameters: Check error message for missing context values
- Invalid subnet IDs: Ensure subnet IDs exist in the specified VPC
- Certificate ARN format: Must be valid ACM certificate ARN
- JSON parsing errors: Ensure JSON strings are properly escaped/quoted
Getting Stack Outputs
After deployment, get stack outputs:
aws cloudformation describe-stacks \
--stack-name my-stack \
--query 'Stacks[0].Outputs' \
--output table
Quick Reference
Minimal Commands
VPC:
pnpm exec cdk synth -c stackType=vpc -c stackName=test -c ownerTag=T -c productTag=t -c componentTag=v
Cluster:
pnpm exec cdk synth -c stackType=ecs-cluster -c stackName=test -c vpcStackName=test-vpc -c ownerTag=T -c productTag=t -c componentTag=c -c environment=d
Service:
pnpm exec cdk synth -c stackType=ecs-service -c stackName=test -c clusterName=test -c vpcId=vpc-123 -c vpcCidrBlock=10.0.0.0/16 -c availabilityZones=ca-central-1a -c privateSubnetIds=subnet-123 -c serviceName=test -c image=nginx:latest -c containerPort=80 -c ownerTag=T -c productTag=t -c componentTag=s -c environment=d