Search code examples
amazon-web-servicesaws-cloudformationamazon-ecsaws-code-deploy

AWS CloudFormation cannot create Stack using CodeDeploy blue/green deployments


I am trying to deploy a new stack using CloudFormation with an ECS service using the CodeDeploy launch type to enable blue/green deployments.

In the User Guide for performing blue/green deployments via CloudFormation, they state the following:

To enable CloudFormation to perform blue/green deployments on a Stack, include the following information in its stack template:

At least one of the ECS resources that will trigger a blue/green deployment if replaced during a stack update. Currently, those resources are AWS::ECS::TaskDefinition and AWS::ECS::TaskSet.

When I exclude a AWS::ECS::TaskSet, the Stack creation fails and I receive the following error:

Transform AWS::CodeDeployBlueGreen failed with: Failed to transform template. 
ECSAttributes must include TaskSets in AWS::CodeDeploy::BlueGreen Hook

If I add a AWS::ECS::TaskSet, the stack fails to create with the following error instead:

Resource handler returned message: 
"Invalid request provided: Amazon ECS does not support task set management on services where deployments 
are controlled by AWS CodeDeploy. 
(Service: AmazonECS; Status Code: 400; Error Code: InvalidParameterException; 
Request ID: 61b8c146-3ae9-4bc2-ac5c-08a11e194f06; Proxy: null)" 
(RequestToken: 86a8a3a5-fe89-9939-15c2-45b08b28c3f3, HandlerErrorCode: InvalidRequest)

These are the relevant parts of my stack template:

Transform:
  - AWS::CodeDeployBlueGreen

Hooks:
  CodeDeployBlueGreenHook:
    Type: AWS::CodeDeploy::BlueGreen
    Properties:
      ServiceRole: BlueGreenDeploymentRole
      Applications:
        - Target:
            Type: AWS::ECS::Service
            LogicalID: EcsService
          ECSAttributes:
            TaskDefinitions:
              - TaskDefinitionBlue
              - TaskDefinitionGreen
            TaskSets:
              - TaskSetBlue
              - TaskSetGreen
            TrafficRouting:
              ProdTrafficRoute:
                Type: AWS::ElasticLoadBalancingV2::Listener
                LogicalID: LoadBalancerListener
              TargetGroups:
                - TargetGroupBlue
                - TargetGroupGreen
      TrafficRoutingConfig:
        Type: TimeBasedLinear
        TimeBasedLinear:
          StepPercentage: 20
          BakeTimeMins: 10
      AdditionalOptions:
        TerminationWaitTimeInMinutes: 60

Resources:
  # IAM Role for blue/green deployments
  BlueGreenDeploymentRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: blue-green-deployment-role
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: codedeploy.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: BlueGreenDeploymentPolicy
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - codedeploy:Get*
                  - codedeploy:CreateCloudFormationDeployment
                Resource: '*'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS

  #########################
  # Load Balancer
  #########################

  # Application Load Balancer
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      IpAddressType: ipv4
      Name: service-alb
      Scheme: internal
      Subnets:
        - !Ref SubnetOne
        - !Ref SubnetTwo
      SecurityGroups:
        - !Ref SecurityGroup

  # Load Balancer Listener
  LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref TargetGroupBlue
                Weight: 1
              - TargetGroupArn: !Ref TargetGroupGreen
                Weight: 1
      LoadBalancerArn: !Ref LoadBalancer
      Port: 8080
      Protocol: HTTP

  # Load Balancer Target Groups
  TargetGroupBlue:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${ServiceName}-blue
      TargetType: ip
      VpcId: !Ref Vpc
      Port: 8080
      Protocol: HTTP
      HealthCheckPort: 8080
      HealthCheckPath: /actuator/health

  TargetGroupGreen:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${ServiceName}-green
      TargetType: ip
      VpcId: !Ref Vpc
      Port: 8080
      Protocol: HTTP
      HealthCheckPort: 8080
      HealthCheckPath: /actuator/health

  #########################
  # ECS
  #########################

  # ECS Cluster
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterName: !Ref ServiceName

  # ECS Service
  EcsService:
    Type: AWS::ECS::Service
    DependsOn: LoadBalancerListener
    Properties:
      ServiceName: !Ref ServiceName
      Cluster: !Ref Cluster
      TaskDefinition: !Ref TaskDefinitionBlue
      DeploymentController:
        Type: CODE_DEPLOY
      DesiredCount: 0
      LaunchType: FARGATE
      LoadBalancers:
        - ContainerName: !Sub ${ServiceName}-container
          ContainerPort: 8080
          TargetGroupArn: !Ref TargetGroupBlue
        - ContainerName: !Sub ${ServiceName}-container
          ContainerPort: 8080
          TargetGroupArn: !Ref TargetGroupGreen
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref SubnetOne
            - !Ref SubnetTwo
          SecurityGroups:
            - !Ref SecurityGroup
      SchedulingStrategy: REPLICA

  # Task Definitions
  TaskDefinitionBlue:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Name: !Sub ${ServiceName}-container
          Image: !Sub ${ImageRepository.RepositoryUri}:latest
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: payments
          PortMappings:
            - ContainerPort: 8080
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      Family: !Sub ${ServiceName}
      ExecutionRoleArn: !Ref TaskExecutionRole
      RequiresCompatibilities:
        - FARGATE

  TaskDefinitionGreen:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Name: !Sub ${ServiceName}-container
          Image: !Sub ${ImageRepository.RepositoryUri}:latest
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: payments
          PortMappings:
            - ContainerPort: 8080
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      Family: !Sub ${ServiceName}
      ExecutionRoleArn: !Ref TaskExecutionRole
      RequiresCompatibilities:
        - FARGATE

  # Image Repository
  ImageRepository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Sub ${ServiceName}
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Maintain at most 25 images",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "imageCountMoreThan",
                  "countNumber": 25
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }

  TaskSetBlue:
    Type: AWS::ECS::TaskSet
    Properties:
      Cluster: !Ref Cluster
      LaunchType: FARGATE
      Service: !Ref EcsService
      TaskDefinition: !Ref TaskDefinitionBlue
      NetworkConfiguration:
        AwsVpcConfiguration:
          Subnets:
            - !Ref SubnetOne
            - !Ref SubnetTwo
          SecurityGroups:
            - !Ref SecurityGroup
      LoadBalancers:
        - ContainerName: !Sub ${ServiceName}-container
          ContainerPort: 8080
          TargetGroupArn: !Ref TargetGroupBlue

How can I update my template to allow for the blue/green deployment strategy via CodeDeploy?


Solution

  • G/B deployment using CFN is EXTERNAL, not CODE_DEPLOY. There could be many other issues with your template, but your current error relates to using wrong DeploymentController. Please study AWS docs and example: