DEV Community

Cover image for Migrating from AWS CodeDeploy to Amazon ECS for Blue/Green Deployments: A Comprehensive Migration Guide
Manish Kumar
Manish Kumar

Posted on

Migrating from AWS CodeDeploy to Amazon ECS for Blue/Green Deployments: A Comprehensive Migration Guide

This comprehensive guide addresses the critical migration path from AWS CodeDeploy to Amazon ECS's native blue/green deployment capabilities, introduced in July 2025. Organizations currently using CodeDeploy for blue/green deployments on Amazon ECS can now leverage Amazon ECS's built-in deployment strategy, eliminating the complexity of managing separate CodeDeploy applications while gaining enhanced operational flexibility and improved AWS service integration. Readers will learn strategic migration approaches, understand architectural differences, master implementation techniques through hands-on labs, and discover cost optimization opportunities. This migration enables organizations to reduce operational overhead, improve deployment velocity, and leverage advanced ECS features like Service Connect and multiple target group support that weren't available with CodeDeploy integration.

Learning Objectives

  • Understand Migration Drivers: Analyze the strategic benefits of migrating from CodeDeploy to ECS native blue/green deployments, including operational improvements and new capabilities
  • Master Architecture Mapping: Map CodeDeploy concepts to ECS blue/green equivalents, including TaskSets to ServiceRevisions, lifecycle hooks, and load balancer configurations
  • Implement Migration Strategies: Execute three distinct migration approaches (in-place update, new service with existing load balancer, and new service with new load balancer) based on specific organizational requirements
  • Optimize Cost and Performance: Leverage ECS deployment features to reduce operational costs while improving deployment reliability and rollback capabilities
  • Integrate Advanced Features: Utilize ECS Service Connect, multiple target groups, and flexible ALB listener configurations that weren't possible with CodeDeploy

Understanding the Migration Landscape

Why Migrate from CodeDeploy to ECS Blue/Green Deployments?

Amazon ECS's native blue/green deployment capability, launched in July 2025, represents a significant evolution in container deployment strategies. Unlike CodeDeploy-based blue/green deployments that required separate application and deployment group management, ECS blue/green deployments integrate directly with the ECS service model, providing several compelling advantages.

Key Migration Drivers:

  • Operational Simplification: Eliminates the need to manage separate CodeDeploy applications, deployment groups, and complex IAM role configurations
  • Enhanced Service Discovery: Support for both Elastic Load Balancing and ECS Service Connect, enabling headless service deployments for queue processing workloads
  • Advanced Load Balancer Integration: Flexible ALB listener rule configuration allows multiple services on single listeners with path-based routing and A/B testing capabilities
  • Improved AWS CloudFormation Support: No longer requires separate AppSpec files for service revisions and lifecycle hooks
  • Extended Lifecycle Hook Duration: ECS hooks support longer execution times compared to CodeDeploy's 1-hour limitation

Current State Assessment

Before initiating migration, organizations must evaluate their existing CodeDeploy implementation. The migration is most straightforward for services using all-at-once deployment configurations, as ECS blue/green deployments currently support only this traffic shifting pattern. Services using canary or linear deployments must first transition to all-at-once CodeDeploy configurations.

Architecture Comparison: CodeDeploy vs. ECS Blue/Green

Load Balancer Configuration Differences

CodeDeploy Approach:

{
  "deploymentController": {
    "type": "CODE_DEPLOY"
  },
  "loadBalancers": [
    {
      "targetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/primary-tg",
      "containerName": "app-container",
      "containerPort": 80
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

ECS Blue/Green Approach:

{
  "deploymentController": {
    "type": "ECS"
  },
  "deploymentConfiguration": {
    "strategy": "BLUE_GREEN",
    "productionTrafficRoute": {
      "listenerArn": "arn:aws:elasticloadbalancing:region:account:listener/app/load-balancer/listener-id",
      "ruleArn": "arn:aws:elasticloadbalancing:region:account:listener-rule/app/load-balancer/rule-id"
    },
    "targetGroups": [
      {
        "name": "primary-target-group",
        "weight": 1
      },
      {
        "name": "secondary-target-group", 
        "weight": 0
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Service Deployment Differences

CodeDeploy uses TaskSets to manage deployment versions, while ECS blue/green deployments utilize ServiceRevisions. This architectural difference provides better visibility into deployment history and aligns with the broader ECS service deployments API.

CodeDeploy Deployment Process:

  1. Create deployment via CreateDeployment() API
  2. Specify AppSpec file with task definition and configuration
  3. CodeDeploy creates replacement TaskSet
  4. Traffic routing through listener rule updates

ECS Blue/Green Deployment Process:

  1. Update service via UpdateService() API
  2. Specify replacement task definition directly
  3. ECS creates new ServiceRevision
  4. Traffic routing through target group weight changes

Migration Strategies

Strategy 1: In-Place Update (Recommended for Low-Risk Environments)

This approach updates the existing ECS service to use native blue/green deployments while reusing existing load balancer resources.

Implementation Steps:

  1. Prepare Load Balancer Configuration:
# Update ALB listener rule to include both target groups
aws elbv2 modify-rule \
    --rule-arn arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/listener-id/rule-id \
    --actions Type=forward,ForwardConfig='{
        "TargetGroups": [
            {
                "TargetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/primary-tg",
                "Weight": 1
            },
            {
                "TargetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/secondary-tg", 
                "Weight": 0
            }
        ]
    }'
Enter fullscreen mode Exit fullscreen mode
  1. Create ECS Blue/Green IAM Role:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ecs.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  1. Update ECS Service:
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --deployment-controller type=ECS \
    --deployment-configuration '{
        "strategy": "BLUE_GREEN",
        "productionTrafficRoute": {
            "listenerArn": "arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/listener-id",
            "ruleArn": "arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/rule-id"
        },
        "targetGroups": [
            {
                "name": "primary-target-group",
                "weight": 1
            },
            {
                "name": "secondary-target-group",
                "weight": 0
            }
        ],
        "roleArn": "arn:aws:iam::account:role/ECS-BlueGreen-Role"
    }'
Enter fullscreen mode Exit fullscreen mode

Strategy 2: New Service with Existing Load Balancer (Recommended for Production)

This approach creates a parallel blue/green setup for testing before switching traffic, providing the safest migration path.

Implementation Steps:

  1. Create New Target Groups:
aws elbv2 create-target-group \
    --name my-service-migration-primary \
    --protocol HTTP \
    --port 80 \
    --vpc-id vpc-12345678 \
    --health-check-path /health

aws elbv2 create-target-group \
    --name my-service-migration-secondary \
    --protocol HTTP \
    --port 80 \
    --vpc-id vpc-12345678 \
    --health-check-path /health
Enter fullscreen mode Exit fullscreen mode
  1. Create New Listeners on Different Ports:
aws elbv2 create-listener \
    --load-balancer-arn arn:aws:elasticloadbalancing:region:account:loadbalancer/app/my-alb \
    --protocol HTTP \
    --port 8080 \
    --default-actions Type=forward,ForwardConfig='{
        "TargetGroups": [
            {
                "TargetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/my-service-migration-primary",
                "Weight": 1
            },
            {
                "TargetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/my-service-migration-secondary",
                "Weight": 0
            }
        ]
    }'
Enter fullscreen mode Exit fullscreen mode
  1. Create New ECS Service:
aws ecs create-service \
    --cluster my-cluster \
    --service-name my-service-bluegreen \
    --task-definition my-app:latest \
    --desired-count 2 \
    --deployment-controller type=ECS \
    --deployment-configuration '{
        "strategy": "BLUE_GREEN",
        "productionTrafficRoute": {
            "listenerArn": "arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/8080-listener",
            "ruleArn": "arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/8080-rule"
        },
        "targetGroups": [
            {
                "name": "my-service-migration-primary",
                "weight": 1
            },
            {
                "name": "my-service-migration-secondary",
                "weight": 0
            }
        ],
        "roleArn": "arn:aws:iam::account:role/ECS-BlueGreen-Role"
    }'
Enter fullscreen mode Exit fullscreen mode
  1. Port Switching for Traffic Cutover:
# Change original listener to temporary port
aws elbv2 modify-listener \
    --listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/original-listener \
    --port 8081

# Change new listener to original port
aws elbv2 modify-listener \
    --listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/8080-listener \
    --port 80
Enter fullscreen mode Exit fullscreen mode

Strategy 3: New Service with New Load Balancer (Enterprise/Multi-Region)

This approach is suitable for organizations with existing routing layers (Route 53, API Gateway, CloudFront) that can manage traffic switching at a higher level.

Hands-on Labs

Lab 1: CodeDeploy to ECS Blue/Green Migration with CloudFormation

Objective: Migrate an existing CodeDeploy-enabled ECS service to native ECS blue/green deployments using Infrastructure as Code.

Prerequisites:

  • Existing ECS service with CodeDeploy deployment controller
  • Application Load Balancer with target groups
  • AWS CLI configured with appropriate permissions

Step 1: Create Migration CloudFormation Template

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Migrate ECS service from CodeDeploy to native blue/green deployments'

Parameters:
  ClusterName:
    Type: String
    Default: my-ecs-cluster
  ServiceName:
    Type: String
    Default: my-web-app
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
  LoadBalancerArn:
    Type: String

Resources:
  # Primary Target Group for Blue/Green
  PrimaryTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${ServiceName}-primary-tg'
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VpcId
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      TargetType: ip

  # Secondary Target Group for Blue/Green
  SecondaryTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${ServiceName}-secondary-tg'
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VpcId
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      TargetType: ip

  # ALB Listener with weighted routing
  LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancerArn
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref PrimaryTargetGroup
                Weight: 1
              - TargetGroupArn: !Ref SecondaryTargetGroup
                Weight: 0

  # IAM Role for ECS Blue/Green Deployments
  ECSBlueGreenRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub '${ServiceName}-bluegreen-role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ecs.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSServiceRolePolicy
      Policies:
        - PolicyName: BlueGreenDeploymentPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - elasticloadbalancing:ModifyRule
                  - elasticloadbalancing:ModifyListener
                  - elasticloadbalancing:DescribeRules
                  - elasticloadbalancing:DescribeListeners
                Resource: '*'

  # Task Definition
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Family: !Sub '${ServiceName}-bluegreen'
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      Cpu: 256
      Memory: 512
      ExecutionRoleArn: !Sub 'arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole'
      ContainerDefinitions:
        - Name: web-container
          Image: nginx:latest
          PortMappings:
            - ContainerPort: 80
              Protocol: tcp
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Sub '/ecs/${ServiceName}'
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: ecs

  # CloudWatch Log Group
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/ecs/${ServiceName}'
      RetentionInDays: 7

  # ECS Service with Blue/Green Deployment
  ECSService:
    Type: AWS::ECS::Service
    Properties:
      ServiceName: !Sub '${ServiceName}-bluegreen'
      Cluster: !Ref ClusterName
      TaskDefinition: !Ref TaskDefinition
      DesiredCount: 2
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
          SecurityGroups:
            - !Ref ServiceSecurityGroup
          Subnets: !Ref SubnetIds
          AssignPublicIp: ENABLED
      DeploymentController:
        Type: ECS
      DeploymentConfiguration:
        Strategy: BLUE_GREEN
        ProductionTrafficRoute:
          ListenerArn: !Ref LoadBalancerListener
        TargetGroups:
          - Name: !GetAtt PrimaryTargetGroup.TargetGroupName
            Weight: 1
          - Name: !GetAtt SecondaryTargetGroup.TargetGroupName
            Weight: 0
        RoleArn: !GetAtt ECSBlueGreenRole.Arn
        BlueGreenUpdatePolicy:
          TrafficRoutingPolicy:
            TimeBasedCanary:
              StepPercentage: 100
              BakeTimeMinutes: 5
          TerminationPolicy:
            MaxTerminationTimeMinutes: 5

  # Security Group for ECS Service
  ServiceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ECS service
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup

  LoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ALB
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

Outputs:
  ServiceArn:
    Description: ARN of the ECS service
    Value: !Ref ECSService
  PrimaryTargetGroupArn:
    Description: ARN of primary target group
    Value: !Ref PrimaryTargetGroup
  SecondaryTargetGroupArn:
    Description: ARN of secondary target group
    Value: !Ref SecondaryTargetGroup
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy the Migration Infrastructure

# Deploy the CloudFormation stack
aws cloudformation deploy \
    --template-file ecs-bluegreen-migration.yaml \
    --stack-name ecs-bluegreen-migration \
    --parameter-overrides \
        ClusterName=my-ecs-cluster \
        ServiceName=my-web-app \
        VpcId=vpc-12345678 \
        SubnetIds=subnet-12345678,subnet-87654321 \
        LoadBalancerArn=arn:aws:elasticloadbalancing:region:account:loadbalancer/app/my-alb \
    --capabilities CAPABILITY_NAMED_IAM
Enter fullscreen mode Exit fullscreen mode

Step 3: Verify Migration Success

# Check service status
aws ecs describe-services \
    --cluster my-ecs-cluster \
    --services my-web-app-bluegreen

# Monitor deployment events
aws ecs describe-services \
    --cluster my-ecs-cluster \  
    --services my-web-app-bluegreen \
    --query 'services[^0].events[0:5]'

# Verify target group health
aws elbv2 describe-target-health \
    --target-group-arn arn:aws:elasticloadbalancing:region:account:targetgroup/my-web-app-primary-tg
Enter fullscreen mode Exit fullscreen mode

Lab 2: Implementing Lifecycle Hooks for Custom Validation

Objective: Create custom validation logic using ECS blue/green deployment lifecycle hooks to ensure application readiness before traffic shifting.

Step 1: Create Validation Lambda Function

import json
import boto3
import requests
import time

def lambda_handler(event, context):
    """
    ECS Blue/Green deployment lifecycle hook for custom validation
    """

    # Extract deployment information
    deployment_id = event['DeploymentId']
    lifecycle_event = event['LifecycleEventHookExecutionId']
    task_set_arn = event.get('TaskSetArn')

    ecs_client = boto3.client('ecs')

    try:
        # Perform custom validation logic
        if event['LifecycleEventType'] == 'POST_SCALE_UP':
            # Validate new tasks are healthy
            validation_result = validate_task_health(ecs_client, task_set_arn)

        elif event['LifecycleEventType'] == 'TEST_TRAFFIC_SHIFT':
            # Run integration tests against test endpoint
            validation_result = run_integration_tests(event)

        elif event['LifecycleEventType'] == 'PRODUCTION_TRAFFIC_SHIFT':
            # Final validation before production traffic
            validation_result = final_production_validation(event)

        else:
            validation_result = {'status': 'SUCCEEDED', 'message': 'No validation required'}

        # Return hook response
        if validation_result['status'] == 'SUCCEEDED':
            return {
                'Status': 'SUCCEEDED',
                'Message': validation_result['message']
            }
        elif validation_result['status'] == 'IN_PROGRESS':
            return {
                'Status': 'IN_PROGRESS',
                'Message': validation_result['message'],
                'NextInvocationSeconds': 30
            }
        else:
            return {
                'Status': 'FAILED',
                'Message': validation_result['message']
            }

    except Exception as e:
        print(f"Validation failed with error: {str(e)}")
        return {
            'Status': 'FAILED',
            'Message': f'Validation failed: {str(e)}'
        }

def validate_task_health(ecs_client, task_set_arn):
    """Validate that all tasks in the task set are healthy"""
    try:
        # Get task set details
        response = ecs_client.describe_task_sets(
            taskSets=[task_set_arn]
        )

        task_set = response['taskSets'][^0]
        running_count = task_set['runningCount']
        desired_count = task_set['desiredCount']

        if running_count == desired_count and running_count > 0:
            return {'status': 'SUCCEEDED', 'message': f'All {running_count} tasks are healthy'}
        else:
            return {'status': 'IN_PROGRESS', 'message': f'Waiting for tasks: {running_count}/{desired_count} ready'}

    except Exception as e:
        return {'status': 'FAILED', 'message': f'Task health validation failed: {str(e)}'}

def run_integration_tests(event):
    """Run integration tests against the test endpoint"""
    try:
        # Extract test endpoint from event
        test_endpoint = event.get('TestTrafficEndpoint', 'http://test-endpoint/health')

        # Run health check
        response = requests.get(test_endpoint, timeout=10)

        if response.status_code == 200:
            # Run additional integration tests
            test_results = {
                'health_check': response.status_code == 200,
                'response_time': response.elapsed.total_seconds(),
                'content_validation': 'healthy' in response.text.lower()
            }

            if all(test_results.values()) and test_results['response_time'] < 2.0:
                return {'status': 'SUCCEEDED', 'message': 'Integration tests passed'}
            else:
                return {'status': 'FAILED', 'message': f'Integration tests failed: {test_results}'}
        else:
            return {'status': 'FAILED', 'message': f'Health check failed with status {response.status_code}'}

    except Exception as e:
        return {'status': 'FAILED', 'message': f'Integration tests failed: {str(e)}'}

def final_production_validation(event):
    """Final validation before production traffic routing"""
    try:
        # Implement custom business logic validation
        # This could include database connectivity, external service health, etc.

        validations = {
            'database_connectivity': check_database_connection(),
            'external_services': check_external_services(),
            'configuration_validation': validate_configuration()
        }

        if all(validations.values()):
            return {'status': 'SUCCEEDED', 'message': 'Production validation passed'}
        else:
            failed_checks = [k for k, v in validations.items() if not v]
            return {'status': 'FAILED', 'message': f'Production validation failed: {failed_checks}'}

    except Exception as e:
        return {'status': 'FAILED', 'message': f'Production validation failed: {str(e)}'}

def check_database_connection():
    """Validate database connectivity"""
    # Implement database connection check
    return True

def check_external_services():
    """Validate external service dependencies"""
    # Implement external service health checks
    return True

def validate_configuration():
    """Validate application configuration"""
    # Implement configuration validation
    return True
Enter fullscreen mode Exit fullscreen mode

Step 2: Deploy Lambda Function with IAM Role

# Create deployment package
zip -r lifecycle-hook-function.zip lambda_function.py

# Create Lambda function
aws lambda create-function \
    --function-name ecs-bluegreen-lifecycle-hook \
    --runtime python3.9 \
    --role arn:aws:iam::account:role/LambdaECSLifecycleRole \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://lifecycle-hook-function.zip \
    --timeout 300 \
    --memory-size 256
Enter fullscreen mode Exit fullscreen mode

Step 3: Configure ECS Service with Lifecycle Hooks

aws ecs update-service \
    --cluster my-ecs-cluster \
    --service my-web-app-bluegreen \
    --deployment-configuration '{
        "strategy": "BLUE_GREEN",
        "productionTrafficRoute": {
            "listenerArn": "arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/listener",
            "ruleArn": "arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/rule"
        },
        "targetGroups": [
            {
                "name": "primary-target-group",
                "weight": 1
            },
            {
                "name": "secondary-target-group",
                "weight": 0
            }
        ],
        "roleArn": "arn:aws:iam::account:role/ECS-BlueGreen-Role",
        "blueGreenUpdatePolicy": {
            "trafficRoutingPolicy": {
                "timeBasedCanary": {
                    "stepPercentage": 100,
                    "bakeTimeMinutes": 10
                }
            },
            "terminationPolicy": {
                "maxTerminationTimeMinutes": 5
            }
        },
        "deploymentLifecycleHooks": [
            {
                "lifecycleEventType": "POST_SCALE_UP",
                "lambdaFunctionArn": "arn:aws:lambda:region:account:function:ecs-bluegreen-lifecycle-hook",
                "roleArn": "arn:aws:iam::account:role/ECS-Lifecycle-Hook-Role"
            },
            {
                "lifecycleEventType": "TEST_TRAFFIC_SHIFT",
                "lambdaFunctionArn": "arn:aws:lambda:region:account:function:ecs-bluegreen-lifecycle-hook",
                "roleArn": "arn:aws:iam::account:role/ECS-Lifecycle-Hook-Role"
            },
            {
                "lifecycleEventType": "PRODUCTION_TRAFFIC_SHIFT",
                "lambdaFunctionArn": "arn:aws:lambda:region:account:function:ecs-bluegreen-lifecycle-hook",
                "roleArn": "arn:aws:iam::account:role/ECS-Lifecycle-Hook-Role"
            }
        ]
    }'
Enter fullscreen mode Exit fullscreen mode

Lab 3: Multi-Environment Pipeline with ECS Blue/Green Deployments

Objective: Build a complete CI/CD pipeline using AWS CodePipeline that leverages ECS blue/green deployments across development, staging, and production environments.

Step 1: Create CodePipeline with ECS Blue/Green Integration

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Multi-environment CI/CD pipeline with ECS blue/green deployments'

Parameters:
  GitHubRepository:
    Type: String
    Default: 'my-org/my-web-app'
  GitHubBranch:
    Type: String
    Default: 'main'
  ECRRepository:
    Type: String
    Default: 'my-web-app'

Resources:
  # CodeBuild Project
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: my-web-app-build
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_MEDIUM
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        PrivilegedMode: true
        EnvironmentVariables:
          - Name: AWS_DEFAULT_REGION
            Value: !Ref AWS::Region
          - Name: AWS_ACCOUNT_ID
            Value: !Ref AWS::AccountId
          - Name: IMAGE_REPO_NAME
            Value: !Ref ECRRepository
          - Name: IMAGE_TAG
            Value: latest
      Source:
        Type: CODEPIPELINE
        BuildSpec: |
          version: 0.2
          phases:
            pre_build:
              commands:
                - echo Logging in to Amazon ECR...
                - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
                - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME
                - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
                - IMAGE_TAG=${COMMIT_HASH:=latest}
            build:
              commands:
                - echo Build started on `date`
                - echo Building the Docker image...
                - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
                - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG
            post_build:
              commands:
                - echo Build completed on `date`
                - echo Pushing the Docker image...
                - docker push $REPOSITORY_URI:$IMAGE_TAG
                - echo Writing image definitions file...
                - printf '[{"name":"web-container","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
          artifacts:
            files:
              - imagedefinitions.json
              - taskdef-*.json

  # CodePipeline
  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: my-web-app-pipeline
      RoleArn: !GetAtt CodePipelineRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactsBucket
      Stages:
        - Name: Source
          Actions:
            - Name: SourceAction
              ActionTypeId:
                Category: Source
                Owner: ThirdParty
                Provider: GitHub
                Version: '1'
              Configuration:
                Owner: !Select [0, !Split ['/', !Ref GitHubRepository]]
                Repo: !Select [1, !Split ['/', !Ref GitHubRepository]]
                Branch: !Ref GitHubBranch
                OAuthToken: !Ref GitHubToken
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: SourceOutput

        - Name: Build
          Actions:
            - Name: BuildAction
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: '1'
              Configuration:
                ProjectName: !Ref CodeBuildProject
              InputArtifacts:
                - Name: SourceOutput
              OutputArtifacts:
                - Name: BuildOutput

        - Name: DeployDev
          Actions:
            - Name: DeployToDevAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: '1'
              Configuration:
                ClusterName: dev-cluster
                ServiceName: my-web-app-dev
                FileName: imagedefinitions.json
                DeploymentTimeout: 15
              InputArtifacts:
                - Name: BuildOutput
              Region: !Ref AWS::Region

        - Name: ApproveStaging
          Actions:
            - Name: ManualApproval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: '1'
              Configuration:
                CustomData: 'Please review the application in development and approve for staging deployment'

        - Name: DeployStaging
          Actions:
            - Name: DeployToStagingAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: '1'
              Configuration:
                ClusterName: staging-cluster
                ServiceName: my-web-app-staging
                FileName: imagedefinitions.json
                DeploymentTimeout: 15
              InputArtifacts:
                - Name: BuildOutput
              Region: !Ref AWS::Region

        - Name: ApproveProd
          Actions:
            - Name: ManualApproval
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Provider: Manual
                Version: '1'
              Configuration:
                CustomData: 'Please review the application in staging and approve for production deployment'

        - Name: DeployProduction
          Actions:
            - Name: DeployToProdAction
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Provider: ECS
                Version: '1'
              Configuration:
                ClusterName: production-cluster
                ServiceName: my-web-app-prod
                FileName: imagedefinitions.json
                DeploymentTimeout: 20
              InputArtifacts:
                - Name: BuildOutput
              Region: !Ref AWS::Region

  # S3 Bucket for Pipeline Artifacts
  ArtifactsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'codepipeline-artifacts-${AWS::AccountId}-${AWS::Region}'
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  # IAM Roles
  CodePipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodePipelineExecutionPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetBucketVersioning
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:PutObject
                Resource:
                  - !Sub '${ArtifactsBucket}/*'
                  - !GetAtt ArtifactsBucket.Arn
              - Effect: Allow
                Action:
                  - codebuild:BatchGetBuilds
                  - codebuild:StartBuild
                Resource: !GetAtt CodeBuildProject.Arn
              - Effect: Allow
                Action:
                  - ecs:DescribeServices
                  - ecs:DescribeTaskDefinition
                  - ecs:DescribeTasks
                  - ecs:ListTasks
                  - ecs:RegisterTaskDefinition
                  - ecs:UpdateService
                Resource: '*'
              - Effect: Allow
                Action:
                  - iam:PassRole
                Resource: '*'

  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodeBuildExecutionPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*'
              - Effect: Allow
                Action:
                  - ecr:BatchCheckLayerAvailability
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                  - ecr:GetAuthorizationToken
                  - ecr:PutImage
                  - ecr:InitiateLayerUpload
                  - ecr:UploadLayerPart
                  - ecr:CompleteLayerUpload
                Resource: '*'
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                Resource: !Sub '${ArtifactsBucket}/*'

Parameters:
  GitHubToken:
    Type: String
    NoEcho: true
    Description: GitHub OAuth token for repository access

Outputs:
  PipelineName:
    Description: Name of the CodePipeline
    Value: !Ref CodePipeline
  ArtifactsBucket:
    Description: S3 bucket for pipeline artifacts  
    Value: !Ref ArtifactsBucket
Enter fullscreen mode Exit fullscreen mode

Real-World Case Study: E-commerce Platform Migration

Background

A major e-commerce platform running on AWS needed to migrate their microservices architecture from CodeDeploy to ECS native blue/green deployments. The platform consisted of 15 microservices handling critical functions including user authentication, product catalog, shopping cart, payment processing, and order management.

Initial Architecture Challenges

CodeDeploy Limitations:

  • Complex AppSpec file management across 15 services
  • Limited load balancer flexibility preventing path-based routing consolidation
  • Manual rollback procedures causing extended downtime during incidents
  • Inability to use ECS Service Connect for internal service communication
  • High operational overhead managing separate CodeDeploy applications

Business Requirements:

  • Zero-downtime deployments during peak shopping periods
  • Automated rollback capabilities within 2 minutes
  • Support for A/B testing new features
  • Cost optimization through resource consolidation
  • Improved observability and deployment tracing

Migration Strategy Implementation

Phase 1: Development Environment (Week 1-2)
The team implemented Strategy 2 (new service with existing load balancer) for the development environment, starting with non-critical services:

# Created parallel ECS services for development
aws ecs create-service \
    --cluster dev-ecommerce-cluster \
    --service-name product-catalog-v2 \
    --task-definition product-catalog:15 \
    --desired-count 2 \
    --deployment-controller type=ECS \
    --deployment-configuration '{
        "strategy": "BLUE_GREEN",
        "productionTrafficRoute": {
            "listenerArn": "arn:aws:elasticloadbalancing:us-east-1:account:listener/app/dev-alb/dev-listener",
            "ruleArn": "arn:aws:elasticloadbalancing:us-east-1:account:listener-rule/app/dev-alb/catalog-rule"
        },
        "targetGroups": [
            {"name": "product-catalog-primary", "weight": 1},
            {"name": "product-catalog-secondary", "weight": 0}
        ],
        "blueGreenUpdatePolicy": {
            "trafficRoutingPolicy": {
                "timeBasedCanary": {
                    "stepPercentage": 100,
                    "bakeTimeMinutes": 3
                }
            }
        }
    }'
Enter fullscreen mode Exit fullscreen mode

Phase 2: Staging Environment with Lifecycle Hooks (Week 3-4)
Implemented comprehensive validation logic:

# Custom validation for payment service
def validate_payment_service(event):
    validation_checks = {
        'payment_gateway_connectivity': test_payment_gateway(),
        'database_transactions': test_database_transactions(),
        'fraud_detection_service': test_fraud_detection(),
        'pci_compliance_check': validate_pci_compliance()
    }

    failed_checks = [k for k, v in validation_checks.items() if not v]

    if not failed_checks:
        return {'status': 'SUCCEEDED', 'message': 'Payment service validation passed'}
    else:
        return {'status': 'FAILED', 'message': f'Failed checks: {failed_checks}'}
Enter fullscreen mode Exit fullscreen mode

Phase 3: Production Migration (Week 5-8)
Used Strategy 1 (in-place update) for production services during low-traffic windows:

Results and Impact

Performance Improvements:

  • Deployment Time: Reduced from 45 minutes to 12 minutes per service
  • Rollback Time: Improved from 15 minutes to 90 seconds
  • Failed Deployment Recovery: 80% reduction in manual intervention

Cost Optimization:

  • Infrastructure Costs: 25% reduction through ALB listener consolidation
  • Operational Overhead: 60% reduction in deployment pipeline maintenance
  • Development Velocity: 40% faster feature delivery

Reliability Enhancements:

  • Zero Failed Deployments: Custom lifecycle hooks prevented 12 potential production issues
  • Improved Observability: Integrated CloudWatch Insights reduced incident response time by 50%
  • Automated Testing: A/B testing capabilities increased feature adoption by 30%

Lessons Learned

Technical Insights:

  1. Lifecycle Hook Investment: Custom validation logic prevented more issues than anticipated, justifying the initial development investment
  2. Load Balancer Consolidation: Path-based routing enabled significant cost savings while improving traffic management
  3. Service Connect Integration: Internal service communication improvements reduced latency by 15%

Operational Insights:

  1. Phased Migration Approach: Starting with non-critical services allowed the team to refine processes before production migration
  2. Training Investment: Team training on ECS blue/green concepts was crucial for successful adoption
  3. Monitoring Enhancement: Enhanced CloudWatch dashboards were essential for confidence during migration

Business Impact:

  1. Customer Experience: Zero customer-facing downtime during the entire migration period
  2. Developer Productivity: Simplified deployment model increased development team satisfaction
  3. Business Agility: Faster deployment cycles enabled more frequent feature releases

Expert Tips & Pitfalls

Pro Tips for Successful Migration

  1. Gradual Traffic Shifting Strategy: While ECS blue/green currently supports only all-at-once traffic shifting, use lifecycle hooks to implement custom canary deployments by controlling when production traffic shifts occur.
  2. Target Group Health Check Optimization: Configure aggressive health check settings during migration to ensure faster failure detection and recovery:
aws elbv2 modify-target-group \
    --target-group-arn arn:aws:elasticloadbalancing:region:account:targetgroup/my-tg \
    --health-check-interval-seconds 10 \
    --health-check-timeout-seconds 5 \
    --healthy-threshold-count 2 \
    --unhealthy-threshold-count 2
Enter fullscreen mode Exit fullscreen mode
  1. Service Connect Integration: Leverage ECS Service Connect for internal service communication, which wasn't possible with CodeDeploy:
{
    "serviceConnectConfiguration": {
        "enabled": true,
        "namespace": "ecommerce-services",
        "services": [
            {
                "portName": "http",
                "discoveryName": "product-catalog",
                "clientAliases": [
                    {
                        "port": 80,
                        "dnsName": "catalog.local"
                    }
                ]
            }
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. CloudWatch Integration: Use ECS deployment events for comprehensive monitoring:
aws events put-rule \
    --name ecs-deployment-events \
    --event-pattern '{"source":["aws.ecs"],"detail-type":["ECS Service Action"],"detail":{"eventName":["SERVICE_DEPLOYMENT_STARTED","SERVICE_DEPLOYMENT_COMPLETED","SERVICE_DEPLOYMENT_FAILED"]}' \
    --targets Id=1,Arn=arn:aws:sns:region:account:deployment-notifications
Enter fullscreen mode Exit fullscreen mode
  1. Cost Optimization Through Consolidation: Utilize flexible ALB listener rules to consolidate multiple services behind fewer load balancers:
# Path-based routing for multiple services
aws elbv2 create-rule \
    --listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/shared-alb/listener \
    --conditions Field=path-pattern,Values="/api/catalog/*" \
    --priority 100 \
    --actions Type=forward,ForwardConfig='{
        "TargetGroups":[
            {"TargetGroupArn":"arn:aws:elasticloadbalancing:region:account:targetgroup/catalog-primary","Weight":1},
            {"TargetGroupArn":"arn:aws:elasticloadbalancing:region:account:targetgroup/catalog-secondary","Weight":0}
        ]
    }'
Enter fullscreen mode Exit fullscreen mode

Common Pitfalls and Solutions

  1. Insufficient IAM Permissions: Ensure the ECS deployment controller role has comprehensive permissions for load balancer manipulation:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:ModifyRule",
                "elasticloadbalancing:ModifyListener", 
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetHealth"
            ],
            "Resource": "*"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  1. Target Group Health Check Misalignment: Ensure health check paths and timeouts align between primary and secondary target groups to prevent deployment failures.
  2. Lifecycle Hook Timeout Issues: ECS lifecycle hooks have different timeout behavior than CodeDeploy. Use IN_PROGRESS status for long-running validations:
# Correct lifecycle hook response for long-running validation
return {
    'Status': 'IN_PROGRESS',
    'Message': 'Validation in progress, will retry in 60 seconds',
    'NextInvocationSeconds': 60
}
Enter fullscreen mode Exit fullscreen mode
  1. Service Discovery Timing: When migrating services that use service discovery, ensure DNS propagation completes before dependency services attempt connections.
  2. CloudFormation Stack Dependencies: Be careful with CloudFormation stack dependencies when using Strategy 2 or 3, as circular dependencies can occur between load balancer and service resources.

Advanced Optimization Techniques

  1. Multi-Target Group Strategy: Utilize ECS's ability to register services with multiple target groups for internal/external separation:
{
    "loadBalancers": [
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/external-tg",
            "containerName": "web-container",
            "containerPort": 80
        },
        {
            "targetGroupArn": "arn:aws:elasticloadbalancing:region:account:targetgroup/internal-tg", 
            "containerName": "web-container",
            "containerPort": 8080
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode
  1. Circuit Breaker Integration: Combine ECS circuit breaker with blue/green deployments for enhanced failure detection:
{
    "deploymentConfiguration": {
        "strategy": "BLUE_GREEN",
        "deploymentCircuitBreaker": {
            "enable": true,
            "rollback": true
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Custom Metrics for Validation: Implement custom CloudWatch metrics in lifecycle hooks for deployment decision-making:
import boto3

cloudwatch = boto3.client('cloudwatch')

def publish_deployment_metric(metric_name, value, unit='Count'):
    cloudwatch.put_metric_data(
        Namespace='ECS/BlueGreen/Deployments',
        MetricData=[
            {
                'MetricName': metric_name,
                'Value': value,
                'Unit': unit,
                'Dimensions': [
                    {
                        'Name': 'ServiceName',
                        'Value': os.environ.get('SERVICE_NAME', 'unknown')
                    }
                ]
            }
        ]
    )
Enter fullscreen mode Exit fullscreen mode
  1. A/B Testing Integration: Use listener rule weights for sophisticated A/B testing:
# Gradual traffic increase for A/B testing
aws elbv2 modify-rule \
    --rule-arn arn:aws:elasticloadbalancing:region:account:listener-rule/app/alb/rule \
    --actions Type=forward,ForwardConfig='{
        "TargetGroups":[
            {"TargetGroupArn":"arn:aws:elasticloadbalancing:region:account:targetgroup/version-a","Weight":70},
            {"TargetGroupArn":"arn:aws:elasticloadbalancing:region:account:targetgroup/version-b","Weight":30}
        ]
    }'
Enter fullscreen mode Exit fullscreen mode
  1. Deployment Rollback Automation: Implement automated rollback triggers based on CloudWatch alarms:
def setup_automatic_rollback(service_arn, alarm_arn):
    """Setup automatic rollback based on CloudWatch alarms"""
    lambda_client = boto3.client('lambda')

    # Create Lambda function that monitors alarms and triggers rollback
    lambda_client.create_function(
        FunctionName='ecs-auto-rollback',
        Runtime='python3.9',
        Role='arn:aws:iam::account:role/ECS-Auto-Rollback-Role',
        Handler='lambda_function.lambda_handler',
        Code={'ZipFile': rollback_function_code},
        Environment={
            'Variables': {
                'SERVICE_ARN': service_arn,
                'ALARM_ARN': alarm_arn
            }
        }
    )
Enter fullscreen mode Exit fullscreen mode

Latest Updates Section: AWS ECS Blue/Green Deployment Enhancements (2024-2025)

July 2025: Native ECS Blue/Green Deployment Launch

The most significant update came in July 2025 with the launch of native ECS blue/green deployment capabilities, eliminating the dependency on AWS CodeDeploy for blue/green deployments.

Key Features:

  • Built-in Deployment Strategy: Direct integration with ECS service model without external dependencies
  • Deployment Lifecycle Hooks: Custom validation through Lambda functions with extended timeout support
  • Multiple Load Balancer Support: Application Load Balancer, Network Load Balancer, and ECS Service Connect compatibility
  • Enhanced CloudFormation Integration: Simplified infrastructure as code without AppSpec file requirements

Impact on Migration Strategy:

{
    "deploymentConfiguration": {
        "strategy": "BLUE_GREEN",
        "blueGreenUpdatePolicy": {
            "trafficRoutingPolicy": {
                "timeBasedCanary": {
                    "stepPercentage": 100,
                    "bakeTimeMinutes": 5
                }
            },
            "terminationPolicy": {
                "maxTerminationTimeMinutes": 5
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

September 2024: ECS Service Revisions and Deployment History

Amazon ECS introduced service revisions functionality, providing enhanced visibility into deployment history and enabling better rollback capabilities.

New Capabilities:

  • 90-Day Deployment History: Track deployment changes and performance metrics over time
  • Service Revision Comparison: Compare configurations between different service versions
  • Enhanced API Support: New DescribeServiceRevisions API for programmatic access

Implementation Example:

# View service deployment history
aws ecs describe-service-revisions \
    --cluster my-cluster \
    --service-name my-service \
    --max-results 10

# Compare service revisions
aws ecs describe-service-revisions \
    --cluster my-cluster \
    --service-name my-service \
    --service-revision-arns arn:aws:ecs:region:account:service-revision/cluster/service/revision-1 arn:aws:ecs:region:account:service-revision/cluster/service/revision-2
Enter fullscreen mode Exit fullscreen mode

August 2024: Software Version Consistency

Amazon ECS enhanced container image management by resolving container image tags to specific digests for deployment consistency.

Benefits for Blue/Green Deployments:

  • Immutable Deployments: Ensures blue and green environments run identical container versions
  • Rollback Reliability: Eliminates image tag drift during rollback scenarios
  • Audit Trail: Provides definitive container version tracking across deployments

October 2024: ECS Managed Instances Enhancement

Amazon ECS introduced managed instances pricing model, providing cost-effective alternatives to Fargate for certain workloads.

Cost Implications:

  • Management Fee: \$0.01025 per hour per managed instance plus standard EC2 charges
  • Blue/Green Considerations: Managed instances support blue/green deployments with per-second billing
  • FinOps Impact: Potential cost savings for long-running, predictable workloads migrating from CodeDeploy

Troubleshooting Guide

Issue 1: Service Update Fails with "InvalidParameterException"

Symptoms:

An error occurred (InvalidParameterException) when calling the UpdateService operation: 
The deployment controller type cannot be updated for services with active deployments.
Enter fullscreen mode Exit fullscreen mode

Root Cause: Attempting to change deployment controller while a deployment is in progress.

Solution:

# Check for active deployments
aws ecs describe-services \
    --cluster my-cluster \
    --services my-service \
    --query 'services[^0].deployments[?status==`RUNNING`]'

# Wait for deployment completion or stop active deployment
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --force-new-deployment
Enter fullscreen mode Exit fullscreen mode

Prevention: Always verify deployment status before attempting deployment controller migration.

Issue 2: Target Group Health Check Failures During Blue/Green Deployment

Symptoms:
Tasks in secondary target group remain unhealthy, preventing traffic shift completion.

Root Cause: Mismatched health check configuration between primary and secondary target groups.

Solution:

# Synchronize health check settings
PRIMARY_TG_ARN="arn:aws:elasticloadbalancing:region:account:targetgroup/primary-tg"
SECONDARY_TG_ARN="arn:aws:elasticloadbalancing:region:account:targetgroup/secondary-tg"

# Get primary target group health check configuration
aws elbv2 describe-target-groups \
    --target-group-arns $PRIMARY_TG_ARN \
    --query 'TargetGroups[^0].{HealthCheckPath:HealthCheckPath,HealthCheckIntervalSeconds:HealthCheckIntervalSeconds,HealthCheckTimeoutSeconds:HealthCheckTimeoutSeconds}'

# Apply same configuration to secondary target group
aws elbv2 modify-target-group \
    --target-group-arn $SECONDARY_TG_ARN \
    --health-check-path /health \
    --health-check-interval-seconds 30 \
    --health-check-timeout-seconds 5 \
    --healthy-threshold-count 2 \
    --unhealthy-threshold-count 3
Enter fullscreen mode Exit fullscreen mode

Issue 3: Lifecycle Hook Timeout Errors

Symptoms:

Lifecycle hook execution timed out. Hook status: IN_PROGRESS
Enter fullscreen mode Exit fullscreen mode

Root Cause: Lifecycle hook Lambda function not returning proper status or exceeding maximum execution time.

Solution:

import json
import time

def lambda_handler(event, context):
    try:
        # Implement validation logic with timeout awareness
        start_time = time.time()
        max_execution_time = 4 * 60  # 4 minutes (leave buffer for Lambda overhead)

        while time.time() - start_time < max_execution_time:
            validation_result = perform_validation()

            if validation_result['complete']:
                return {
                    'Status': 'SUCCEEDED' if validation_result['success'] else 'FAILED',
                    'Message': validation_result['message']
                }

            time.sleep(30)  # Wait before retry

        # If still not complete, return IN_PROGRESS
        return {
            'Status': 'IN_PROGRESS',
            'Message': 'Validation still in progress, will retry',
            'NextInvocationSeconds': 60
        }

    except Exception as e:
        return {
            'Status': 'FAILED',
            'Message': f'Validation failed with error: {str(e)}'
        }

def perform_validation():
    # Your validation logic here
    return {'complete': True, 'success': True, 'message': 'Validation passed'}
Enter fullscreen mode Exit fullscreen mode

Issue 4: ALB Listener Rule Conflicts

Symptoms:

An error occurred (RuleNotFoundException) when calling the ModifyRule operation: 
The specified rule does not exist.
Enter fullscreen mode Exit fullscreen mode

Root Cause: Listener rule ARN changed or rule was deleted externally.

Solution:

# List all rules for the listener
aws elbv2 describe-rules \
    --listener-arn arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/listener \
    --query 'Rules[*].{RuleArn:RuleArn,Priority:Priority,Conditions:Conditions}'

# Identify correct rule ARN and update service
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --deployment-configuration '{
        "strategy": "BLUE_GREEN",
        "productionTrafficRoute": {
            "listenerArn": "arn:aws:elasticloadbalancing:region:account:listener/app/my-alb/listener",
            "ruleArn": "arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/correct-rule"
        }
    }'
Enter fullscreen mode Exit fullscreen mode

Issue 5: Service Connect Configuration Conflicts

Symptoms:
Service fails to start with Service Connect configuration after migration from CodeDeploy.

Root Cause: Service Connect namespace or port configuration conflicts with existing setup.

Solution:

# Check existing Service Connect configuration
aws ecs describe-services \
    --cluster my-cluster \
    --services my-service \
    --query 'services[^0].serviceConnectConfiguration'

# Update service with correct Service Connect configuration
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --service-connect-configuration '{
        "enabled": true,
        "namespace": "my-app-namespace",
        "services": [
            {
                "portName": "http",
                "discoveryName": "my-service",
                "clientAliases": [
                    {
                        "port": 80,
                        "dnsName": "my-service.local"
                    }
                ]
            }
        ]
    }'
Enter fullscreen mode Exit fullscreen mode

Issue 6: IAM Permission Insufficient for ECS Blue/Green

Symptoms:

User: arn:aws:sts::account:assumed-role/ECS-Service-Role is not authorized to perform: 
elasticloadbalancing:ModifyRule
Enter fullscreen mode Exit fullscreen mode

Root Cause: ECS deployment controller role lacks necessary permissions.

Solution:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:ModifyRule",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:DeregisterTargets"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecs:CreateTaskSet",
                "ecs:DeleteTaskSet",
                "ecs:DescribeServices",
                "ecs:UpdateServicePrimaryTaskSet"
            ],
            "Resource": "*"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Issue 7: Rollback Failure During Migration

Symptoms: Unable to rollback to CodeDeploy after failed ECS blue/green migration.

Root Cause: Target group or listener configuration changes prevent CodeDeploy compatibility.

Solution:

# Restore CodeDeploy-compatible listener configuration
aws elbv2 modify-rule \
    --rule-arn arn:aws:elasticloadbalancing:region:account:listener-rule/app/my-alb/rule \
    --actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:region:account:targetgroup/primary-tg

# Revert service to CodeDeploy deployment controller
aws ecs update-service \
    --cluster my-cluster \
    --service my-service \
    --deployment-controller type=CODE_DEPLOY
Enter fullscreen mode Exit fullscreen mode

Further Reading

AWS Official Documentation

  • Amazon ECS Blue/Green Deployments Guide: Comprehensive documentation on implementing native ECS blue/green deployments
  • ECS Service Deployment Strategies: Detailed comparison of rolling, blue/green, and external deployment strategies
  • CodeDeploy to ECS Migration Guide: Official AWS migration documentation with step-by-step instructions

AWS Whitepapers and Best Practices

  • AWS Well-Architected Framework - Reliability Pillar: Best practices for designing resilient deployment strategies
  • Container Security Best Practices: Security considerations for containerized applications during deployments
  • Cost Optimization for Container Workloads: Strategies for optimizing ECS deployment costs

Advanced Topics and Integration Patterns

  • ECS Service Connect Deep Dive: Advanced service mesh capabilities for internal service communication
  • Multi-Region Blue/Green Deployments: Implementing blue/green deployments across multiple AWS regions
  • Observability and Monitoring: CloudWatch integration patterns for deployment monitoring and alerting

Community Resources and Tools

  • AWS Samples GitHub Repository: Code examples and templates for ECS blue/green deployments
  • AWS Container Blog: Latest updates and best practices from AWS container specialists
  • AWS re:Invent Sessions: Annual conference sessions on container deployment strategies and innovations

This comprehensive guide provides organizations with the knowledge and tools necessary to successfully migrate from AWS CodeDeploy to Amazon ECS native blue/green deployments, enabling improved operational efficiency, enhanced deployment capabilities, and better alignment with modern container orchestration practices.

Top comments (0)