

# 通过 CodeDeploy 使用 CloudFormation 执行 ECS 蓝/绿部署
<a name="blue-green"></a>

要更新在 Amazon Elastic Container Service（Amazon ECS）上运行的应用程序，您可以使用 CodeDeploy 蓝绿部署策略。此策略有助于尽可能减少因更改应用程序版本造成的中断。

在蓝绿部署中，您可以在当前的实时环境（称为*蓝色*）之外创建一个新的应用程序环境（称为*绿色*）。这使您可以先监控和测试绿色环境，然后再将实时流量从蓝色环境路由到绿色环境。在绿色环境开始为实时流量提供服务后，您可以安全地终止蓝色环境。

要使用 CloudFormation 在 ECS 上执行 CodeDeploy 蓝绿部署，请在堆栈模板中包括以下信息：
+ 描述 `AWS::CodeDeploy::BlueGreen` 钩子的 `Hooks` 部分。
+  指定 `AWS::CodeDeployBlueGreen` 变换的 `Transform` 部分。

以下主题提供了有关为 ECS 上的蓝绿部署设置 CloudFormation 模板的指导。

**Topics**
+ [关于蓝绿部署](about-blue-green-deployments.md)
+ [使用 CloudFormation 管理 ECS 蓝/绿部署时的注意事项](blue-green-considerations.md)
+ [`AWS::CodeDeploy::BlueGreen` 钩子语法](blue-green-hook-syntax.md)
+ [蓝绿部署模板示例](blue-green-template-example.md)

# 关于蓝绿部署
<a name="about-blue-green-deployments"></a>

本主题概述了如何使用 CloudFormation 执行蓝绿部署。此外还介绍了如何准备用于蓝绿部署的 CloudFormation 模板。

**Topics**
+ [工作原理](#blue-green-how-it-works)
+ [会启动绿色部署的资源更新](#blue-green-resources)
+ [准备 模板](#blue-green-setup)
+ [为蓝绿部署建模](#blue-green-required)
+ [更改集](#blue-green-changesets)
+ [监控堆栈事件](#blue-green-events)
+ [IAM 权限](#blue-green-iam)

## 工作原理
<a name="blue-green-how-it-works"></a>

使用 CloudFormation 通过 CodeDeploy 执行 ECS 蓝绿部署时，首先创建一个堆栈模板，用于定义蓝色和绿色应用程序环境的资源，包括指定要使用的流量路由和稳定设置。然后基于该模板创建一个堆栈。这会生成蓝色（当前）应用程序。CloudFormation 仅在堆栈创建过程中创建蓝色资源。绿色部署的资源仅在需要时才会创建。

然后，如果在将来的堆栈更新中更新蓝色应用程序中的任务定义或任务集资源，CloudFormation 会执行以下操作：
+ 生成所有必需的绿色应用程序环境资源
+ 根据指定的流量路由参数转移流量
+ 删除蓝色资源

如果在绿色部署成功和完成之前的任何时刻发生错误，CloudFormation 会将堆栈回滚到启动整个绿色部署之前的状态。

## 会启动绿色部署的资源更新
<a name="blue-green-resources"></a>

执行将会更新特定 ECS 资源的特定属性的堆栈更新时，CloudFormation 将会启动绿色部署过程。将会启动此过程的资源有：
+ [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html)
+ [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html) 

但是，如果对这些资源的更新不涉及需要替换的属性更改，则不会启动绿色部署。有关更多信息，请参阅 [理解堆栈资源的更新行为](using-cfn-updating-stacks-update-behaviors.md)。

请注意，不能在同一堆栈更新操作中同时包括对上述资源的更新以及对其他资源的更新。如果需要更新列出的资源以及同一堆栈中的其他资源，则有两种选择：
+ 执行两个单独的堆栈更新操作：一个仅包括对上述资源的更新，另一个单独的堆栈更新包括对任何其他资源的更改。
+ 从模板中删除 `Transform` 和 `Hooks` 部分，然后执行堆栈更新。在这种情况下，CloudFormation 不会执行绿色部署。

## 准备模板以执行 ECS 蓝/绿部署
<a name="blue-green-setup"></a>

要在堆栈上启用蓝/绿部署，请在执行堆栈更新之前在堆栈模板中包含以下部分。
+ 将 `AWS::CodeDeployBlueGreen` 转换的引用添加到模板中：

  ```
  "Transform": [
    "AWS::CodeDeployBlueGreen"
  ],
  ```
+ 添加调用 `AWS::CodeDeploy::BlueGreen` 挂钩并指定部署属性的 `Hooks` 部分。有关更多信息，请参阅 [`AWS::CodeDeploy::BlueGreen` 钩子语法](blue-green-hook-syntax.md)。
+ 在 `Resources` 部分中，定义部署的蓝色和绿色资源。

您可以在第一次创建模板时（即创建堆栈本身之前）添加这些部分，也可以在执行堆栈更新之前将它们添加到现有模板中。如果是为新堆栈指定蓝/绿部署，则 CloudFormation 只在堆栈创建期间创建蓝色资源 - 仅当在堆栈更新期间需要绿色部署的资源时才会创建它们。

## 使用 CloudFormation 资源对蓝/绿部署建模
<a name="blue-green-required"></a>

要在 ECS 上执行 CodeDeploy 蓝绿部署，CloudFormation 模板需要包含为部署建模的资源，例如 Amazon ECS 服务和负载均衡器。如需更详细地了解这些资源代表了什么，请参阅《AWS CodeDeploy 用户指南》中的[开始 Amazon ECS 部署之前](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-steps-ecs.html#deployment-steps-prerequisites-ecs)。


| 要求 | 资源 | 必需/可选 | 如果替换则会启动蓝绿部署？ | 
| --- | --- | --- | --- | 
| Amazon ECS 集群 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-cluster.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-cluster.html) | 可选。可以使用默认集群。 | 否 | 
| Amazon ECS 服务 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-service.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-service.html) | 必需。 | 否 | 
| 应用程序或网络负载均衡器 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-service-loadbalancer.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-service-loadbalancer.html) | 必需。 | 否 | 
| 生产侦听器 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html) | 必需。 | 否 | 
| 测试侦听器  | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html) | 可选。 | 否 | 
| 两个目标组 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html) | 必需。 | 否 | 
| Amazon ECS 任务定义  | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html) | 必需。 | 是 | 
| 您的 Amazon ECS 应用程序的容器 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-containerdefinition.html#cfn-ecs-taskdefinition-containerdefinition-name](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-containerdefinition.html#cfn-ecs-taskdefinition-containerdefinition-name) | 必需。 | 否 | 
| 您的替换任务集的端口 | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-portmapping.html#cfn-ecs-taskdefinition-portmapping-containerport](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-portmapping.html#cfn-ecs-taskdefinition-portmapping-containerport) | 必需。 | 否 | 

## 更改集
<a name="blue-green-changesets"></a>

我们强烈建议您在执行会启动绿色部署的堆栈更新之前创建更改集。这样便于您在执行堆栈更新之前查看对堆栈作出的实际更改。请注意，资源更改可能不会按堆栈更新期间的执行顺序列出。有关更多信息，请参阅 [使用更改集更新 CloudFormation 堆栈](using-cfn-updating-stacks-changesets.md)。

## 监控堆栈事件
<a name="blue-green-events"></a>

您可以在 **Stack**（堆栈）页面的 **Events**（事件）选项卡上，使用 AWS CLI 查看 ECS 部署的每个步骤生成的堆栈事件。有关更多信息，请参阅 [监控堆栈进度](monitor-stack-progress.md)。

## 蓝绿部署所需的 IAM 权限
<a name="blue-green-iam"></a>

为了使 CloudFormation 成功执行蓝绿部署，您必须具有以下 CodeDeploy 权限：
+ `codedeploy:Get*`
+ `codedeploy:CreateCloudFormationDeployment`

有关更多信息，请参阅《服务授权参考》中的 [Actions, resources, and condition keys for CodeDeploy](https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodedeploy.html)**。

# 使用 CloudFormation 管理 ECS 蓝/绿部署时的注意事项
<a name="blue-green-considerations"></a>

使用 CloudFormation 来通过 CodeDeploy 执行 ECS 蓝绿部署的过程，不同于仅使用 CodeDeploy 的标准 Amazon ECS 部署过程。要详细了解这些区别，请参阅《AWS CodeDeploy 用户指南》中的[通过 CodeDeploy 和 CloudFormation 进行的 Amazon ECS 蓝绿部署的区别](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployments-create-ecs-cfn.html#differences-ecs-bg-cfn)**。

使用 CloudFormation 管理蓝绿部署时，需要记住某些限制和注意事项：
+ 只有对特定资源的更新才会启动绿色部署。有关更多信息，请参阅 [会启动绿色部署的资源更新](about-blue-green-deployments.md#blue-green-resources)。
+ 不能在同一堆栈更新中包括启动绿色部署的资源更新和对其他资源的更新。有关更多信息，请参阅 [会启动绿色部署的资源更新](about-blue-green-deployments.md#blue-green-resources)。
+ 您只能将单个 ECS 服务指定为部署目标。
+ 如果参数的值由 CloudFormation 模糊处理，则 CodeDeploy 无法在绿色部署期间更新该参数，并且会导致错误和堆栈更新失败。这些方法包括：
  + 使用 `NoEcho` 属性定义的参数。
  + 使用动态引用从外部服务检索其值的参数。有关动态引用的更多信息，请参阅[使用动态引用获取存储在其他服务中的值](dynamic-references.md)。
+ 要取消仍在进行的绿色部署，请取消 CloudFormation 中的堆栈更新，而不是 CodeDeploy 或 ECS。有关更多信息，请参阅 [取消堆栈更新](using-cfn-stack-update-cancel.md)。更新完成之后，将无法取消它。但是，您可以利用任何先前的设置再次更新堆栈。
+ 定义 ECS 蓝绿部署的模板目前不支持以下 CloudFormation 功能：
  + 声明 [outputs](outputs-section-structure.md) 或使用 [Fn::ImportValue](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/intrinsic-function-reference-importvalue.html) 导入其他堆栈中的值。
  + 导入资源。有关资源导入的更多信息，请参阅[将 AWS 资源导入 CloudFormation 堆栈](import-resources.md)。
  + 您不能在包含嵌套堆栈资源的模板中使用 `AWS::CodeDeploy::BlueGreen` 钩子。有关嵌套堆栈的更多信息，请参阅[使用嵌套堆栈将模板拆分为可重复使用的部分](using-cfn-nested-stacks.md)。
  + 在嵌套堆栈中使用 `AWS::CodeDeploy::BlueGreen` 钩子。

# `AWS::CodeDeploy::BlueGreen` 钩子语法
<a name="blue-green-hook-syntax"></a>

以下语法描述了 ECS 蓝绿部署的 `AWS::CodeDeploy::BlueGreen` 钩子结构。

## 语法
<a name="cfn-blue-green-hook-syntax"></a>

```
"Hooks": {
  "Logical ID": {
    "Type": "AWS::CodeDeploy::BlueGreen",
    "Properties": {
      "TrafficRoutingConfig": {
        "Type": "Traffic routing type",
        "TimeBasedCanary": {
          "StepPercentage": Integer,
          "BakeTimeMins": Integer
        },
        "TimeBasedLinear": {
          "StepPercentage": Integer,
          "BakeTimeMins": Integer
        }
      },
      "AdditionalOptions": {"TerminationWaitTimeInMinutes": Integer},
      "LifecycleEventHooks": {
        "BeforeInstall": "FunctionName",
        "AfterInstall": "FunctionName",
        "AfterAllowTestTraffic": "FunctionName",
        "BeforeAllowTraffic": "FunctionName",
        "AfterAllowTraffic": "FunctionName"
      },
      "ServiceRole": "CodeDeployServiceRoleName",
      "Applications": [
        {
          "Target": {
            "Type": "AWS::ECS::Service",
            "LogicalID": "Logical ID of AWS::ECS::Service"
          },
          "ECSAttributes": {
            "TaskDefinitions": [
              "Logical ID of AWS::ECS::TaskDefinition (Blue)",
              "Logical ID of AWS::ECS::TaskDefinition (Green)"
            ],
            "TaskSets": [
              "Logical ID of AWS::ECS::TaskSet (Blue)",
              "Logical ID of AWS::ECS::TaskSet (Green)"
            ],
            "TrafficRouting": {
              "ProdTrafficRoute": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "LogicalID": "Logical ID of AWS::ElasticLoadBalancingV2::Listener (Production)"
              },
              "TestTrafficRoute": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "LogicalID": "Logical ID of AWS::ElasticLoadBalancingV2::Listener (Test)"
              },
              "TargetGroups": [
                "Logical ID of AWS::ElasticLoadBalancingV2::TargetGroup (Blue)",
                "Logical ID of AWS::ElasticLoadBalancingV2::TargetGroup (Green)"
              ]
            }
          }
        }
      ]
    }
  }
}
```

## 属性
<a name="cfn-blue-green-hook-properties"></a>

逻辑 ID（也称为*逻辑名称*）  
在模板的 `Hooks` 部分中声明的钩子。逻辑 ID 必须为字母数字 (A-Za-z0-9)，并且在模板中具有唯一性。  
*是否必需*：是    
`Type`  
挂钩的类型。`AWS::CodeDeploy::BlueGreen`  
*是否必需*：是  
`Properties`  
挂钩的属性。  
*是否必需*：是    
`TrafficRoutingConfig`  
流量路由配置设置。  
*必需*：否  
默认配置为基于时间的 canary 流量转移，具有 15% 的步骤百分比和五分钟的稳定时间。    
`Type`  
部署配置使用的流量转移类型。  
有效值：AllAtOnce \$1 TimeBasedCanary \$1 TimeBasedLinear  
*是否必需*：是    
`TimeBasedCanary`  
指定一个配置，以两个增量将流量从部署的一个版本转移到另一个版本。  
*必需*：如果指定 `TimeBasedCanary` 作为流量路由类型，则必须包含 `TimeBasedCanary` 参数。    
`StepPercentage`  
在 `TimeBasedCanary` 部署的第一个增量中要转移的流量百分比。步骤百分比必须为 14% 或更大。  
*必需*：否  
`BakeTimeMins`  
`TimeBasedCanary` 部署的第一次和第二次流量转移之间的分钟数。  
*必需*：否  
`TimeBasedLinear`  
指定一个配置，以相等的增量将流量从部署的一个版本转移到另一个版本，每个增量之间的分钟数相等。  
*必需*：如果指定 `TimeBasedLinear` 作为流量路由类型，则必须包含 `TimeBasedLinear` 参数。    
`StepPercentage`  
在 `TimeBasedLinear` 部署的每个增量开始时转移的流量百分比。步骤百分比必须为 14% 或更大。  
*必需*：否  
`BakeTimeMins`  
`TimeBasedLinear` 部署的每次增量流量转移之间的分钟数。  
*必需*：否  
`AdditionalOptions`  
蓝/绿部署的其他选项。  
*必需*：否    
`TerminationWaitTimeInMinutes`  
指定终止蓝色资源之前等待的时间（以分钟为单位）。  
*必需*：否  
`LifecycleEventHooks`  
使用生命周期事件挂钩指定 CodeDeploy 可以调用的用于验证部署的 Lambda 函数。对于部署生命周期事件，您可以使用相同函数或不同函数。验证测试完成后，Lambda `AfterAllowTraffic` 函数会回调 CodeDeploy 并提供 `Succeeded` 或 `Failed` 结果。有关更多信息，请参阅《AWS CodeDeploy 用户指南》中的 [AppSpec 的“hooks”部分](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html)。  
*必需*：否    
`BeforeInstall`  
用于在创建替换任务集之前运行任务的函数。  
*必需*：否  
`AfterInstall`  
用于在创建替换任务集并且其中一个目标组与之关联后运行任务的函数。  
*必需*：否  
`AfterAllowTestTraffic`  
用于在测试侦听器为替换任务集提供流量后运行任务的函数。  
*必需*：否  
`BeforeAllowTraffic`  
用于在第二个目标组与替换任务集关联之后但在流量转移到替换任务集之前运行任务的函数。  
*必需*：否  
`AfterAllowTraffic`  
用于在第二个目标组为替换任务集提供流量后运行任务的函数。  
*必需*：否  
`ServiceRole`  
CloudFormation 用于执行蓝绿部署的执行角色。有关必要权限的列表，请参阅 [蓝绿部署所需的 IAM 权限](about-blue-green-deployments.md#blue-green-iam)。  
*必需*：否  
`Applications`  
指定 Amazon ECS 应用程序的属性。  
*是否必需*：是    
`Target`  
  
*是否必需*：是    
`Type`  
资源的类型。  
*是否必需*：是  
`LogicalID`  
资源的逻辑 ID。  
*是否必需*：是  
`ECSAttributes`  
代表 Amazon ECS 应用程序部署的各种需求的资源。  
*是否必需*：是    
`TaskDefinitions`  
运行包含 Amazon ECS 应用程序的 Docker 容器所用的 [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html) 资源的逻辑 ID。  
*是否必需*：是  
`TaskSets`  
用作应用程序任务集的 [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html) 资源的逻辑 ID。  
*是否必需*：是  
`TrafficRouting`  
指定用于流量路由的资源。  
*是否必需*：是    
`ProdTrafficRoute`  
负载均衡器将流量定向到目标组所用的侦听器。  
*是否必需*：是    
`Type`  
资源的类型。`AWS::ElasticLoadBalancingV2::Listener`  
*是否必需*：是  
`LogicalID`  
资源的逻辑 ID。  
*是否必需*：是  
`TestTrafficRoute`  
负载均衡器将流量定向到目标组所用的侦听器。  
*是否必需*：是    
`Type`  
资源的类型。`AWS::ElasticLoadBalancingV2::Listener`  
*是否必需*：是  
`LogicalID`  
资源的逻辑 ID。  
*必需*：否  
`TargetGroups`  
用作目标组以将流量传送到注册目标的资源的逻辑 ID。  
*是否必需*：是

# 蓝绿部署模板示例
<a name="blue-green-template-example"></a>

以下示例模板设置了 ECS 上的 CodeDeploy 蓝绿部署，流量路由进度为每个步骤 15%，每个步骤之间的稳定期为 5 分钟。

使用模板创建堆栈将会预置部署的初始配置。如果您随后对 `BlueTaskSet` 资源中需要替换的属性进行了任何更改，则 CloudFormation 会在堆栈更新过程中启动绿色部署。

## JSON
<a name="blue-green-template-example.json"></a>

```
{
  "AWSTemplateFormatVersion":"2010-09-09",
  "Parameters":{
    "Vpc":{ "Type":"AWS::EC2::VPC::Id" },
    "Subnet1":{ "Type":"AWS::EC2::Subnet::Id" },
    "Subnet2":{ "Type":"AWS::EC2::Subnet::Id" }
  },
  "Transform":[ "AWS::CodeDeployBlueGreen" ],
  "Hooks":{
    "CodeDeployBlueGreenHook":{
      "Type":"AWS::CodeDeploy::BlueGreen",
      "Properties":{
        "TrafficRoutingConfig":{
          "Type":"TimeBasedCanary",
          "TimeBasedCanary":{
            "StepPercentage":15,
            "BakeTimeMins":5
          }
        },
        "Applications":[
          {
            "Target":{
              "Type":"AWS::ECS::Service",
              "LogicalID":"ECSDemoService"
            },
            "ECSAttributes":{
              "TaskDefinitions":[ "BlueTaskDefinition","GreenTaskDefinition" ],
              "TaskSets":[ "BlueTaskSet","GreenTaskSet" ],
              "TrafficRouting":{
                "ProdTrafficRoute":{
                  "Type":"AWS::ElasticLoadBalancingV2::Listener",
                  "LogicalID":"ALBListenerProdTraffic"
                },
                "TargetGroups":[ "ALBTargetGroupBlue","ALBTargetGroupGreen" ]
              }
            }
          }
        ]
      }
    }
  },
  "Resources":{
    "ExampleSecurityGroup":{
      "Type":"AWS::EC2::SecurityGroup",
      "Properties":{
        "GroupDescription":"Security group for ec2 access",
        "VpcId":{ "Ref":"Vpc" },
        "SecurityGroupIngress":[
          {
            "IpProtocol":"tcp",
            "FromPort":80,
            "ToPort":80,
            "CidrIp":"0.0.0.0/0"
          },
          {
            "IpProtocol":"tcp",
            "FromPort":8080,
            "ToPort":8080,
            "CidrIp":"0.0.0.0/0"
          },
          {
            "IpProtocol":"tcp",
            "FromPort":22,
            "ToPort":22,
            "CidrIp":"0.0.0.0/0"
          }
        ]
      }
    },
    "ALBTargetGroupBlue":{
      "Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
      "Properties":{
        "HealthCheckIntervalSeconds":5,
        "HealthCheckPath":"/",
        "HealthCheckPort":"80",
        "HealthCheckProtocol":"HTTP",
        "HealthCheckTimeoutSeconds":2,
        "HealthyThresholdCount":2,
        "Matcher":{ "HttpCode":"200" },
        "Port":80,
        "Protocol":"HTTP",
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "TargetType":"ip",
        "UnhealthyThresholdCount":4,
        "VpcId":{ "Ref":"Vpc" }
      }
    },
    "ALBTargetGroupGreen":{
      "Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
      "Properties":{
        "HealthCheckIntervalSeconds":5,
        "HealthCheckPath":"/",
        "HealthCheckPort":"80",
        "HealthCheckProtocol":"HTTP",
        "HealthCheckTimeoutSeconds":2,
        "HealthyThresholdCount":2,
        "Matcher":{ "HttpCode":"200" },
        "Port":80,
        "Protocol":"HTTP",
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "TargetType":"ip",
        "UnhealthyThresholdCount":4,
        "VpcId":{ "Ref":"Vpc" }
      }
    },
    "ExampleALB":{
      "Type":"AWS::ElasticLoadBalancingV2::LoadBalancer",
      "Properties":{
        "Scheme":"internet-facing",
        "SecurityGroups":[{ "Ref":"ExampleSecurityGroup" }],
        "Subnets":[{ "Ref":"Subnet1" },{ "Ref":"Subnet2" }],
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "Type":"application",
        "IpAddressType":"ipv4"
      }
    },
    "ALBListenerProdTraffic":{
      "Type":"AWS::ElasticLoadBalancingV2::Listener",
      "Properties":{
        "DefaultActions":[
          {
            "Type":"forward",
            "ForwardConfig":{
              "TargetGroups":[
                {
                  "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" },
                  "Weight":1
                }
              ]
            }
          }
        ],
        "LoadBalancerArn":{ "Ref":"ExampleALB" },
        "Port":80,
        "Protocol":"HTTP"
      }
    },
    "ALBListenerProdRule":{
      "Type":"AWS::ElasticLoadBalancingV2::ListenerRule",
      "Properties":{
        "Actions":[
          {
            "Type":"forward",
            "ForwardConfig":{
              "TargetGroups":[
                {
                  "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" },
                  "Weight":1
                }
              ]
            }
          }
        ],
        "Conditions":[
          {
            "Field":"http-header",
            "HttpHeaderConfig":{
              "HttpHeaderName":"User-Agent",
              "Values":[ "Mozilla" ]
            }
          }
        ],
        "ListenerArn":{ "Ref":"ALBListenerProdTraffic" },
        "Priority":1
      }
    },
    "ECSTaskExecutionRole":{
      "Type":"AWS::IAM::Role",
      "Properties":{
        "AssumeRolePolicyDocument":{
          "Version": "2012-10-17",		 	 	 
          "Statement":[
            {
              "Sid":"",
              "Effect":"Allow",
              "Principal":{
                "Service":"ecs-tasks.amazonaws.com"
              },
              "Action":"sts:AssumeRole"
            }
          ]
        },
        "ManagedPolicyArns":[ "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" ]
      }
    },
    "BlueTaskDefinition":{
      "Type":"AWS::ECS::TaskDefinition",
      "Properties":{
        "ExecutionRoleArn":{
          "Fn::GetAtt":[ "ECSTaskExecutionRole","Arn" ]
        },
        "ContainerDefinitions":[
          {
            "Name":"DemoApp",
            "Image":"nginxdemos/hello:latest",
            "Essential":true,
            "PortMappings":[
              {
                "HostPort":80,
                "Protocol":"tcp",
                "ContainerPort":80
              }
            ]
          }
        ],
        "RequiresCompatibilities":[ "FARGATE" ],
        "NetworkMode":"awsvpc",
        "Cpu":"256",
        "Memory":"512",
        "Family":"ecs-demo"
      }
    },
    "ECSDemoCluster":{
      "Type":"AWS::ECS::Cluster",
      "Properties":{}
    },
    "ECSDemoService":{
      "Type":"AWS::ECS::Service",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "DesiredCount":1,
        "DeploymentController":{ "Type":"EXTERNAL" }
      }
    },
    "BlueTaskSet":{
      "Type":"AWS::ECS::TaskSet",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "LaunchType":"FARGATE",
        "NetworkConfiguration":{
          "AwsVpcConfiguration":{
            "AssignPublicIp":"ENABLED",
            "SecurityGroups":[{ "Ref":"ExampleSecurityGroup" }],
            "Subnets":[{ "Ref":"Subnet1" },{ "Ref":"Subnet2" }]
          }
        },
        "PlatformVersion":"1.4.0",
        "Scale":{
          "Unit":"PERCENT",
          "Value":100
        },
        "Service":{ "Ref":"ECSDemoService"},
        "TaskDefinition":{ "Ref":"BlueTaskDefinition" },
        "LoadBalancers":[
          {
            "ContainerName":"DemoApp",
            "ContainerPort":80,
            "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" }
          }
        ]
      }
    },
    "PrimaryTaskSet":{
      "Type":"AWS::ECS::PrimaryTaskSet",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "Service":{ "Ref":"ECSDemoService" },
        "TaskSetId":{ "Fn::GetAtt":[ "BlueTaskSet","Id" ]
        }
      }
    }
  }
}
```

## YAML
<a name="blue-green-template-example.yaml"></a>

```
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Vpc:
    Type: AWS::EC2::VPC::Id
  Subnet1:
    Type: AWS::EC2::Subnet::Id
  Subnet2:
    Type: AWS::EC2::Subnet::Id
Transform:
  - 'AWS::CodeDeployBlueGreen'
Hooks:
  CodeDeployBlueGreenHook:
    Type: AWS::CodeDeploy::BlueGreen
    Properties:
      TrafficRoutingConfig:
        Type: TimeBasedCanary
        TimeBasedCanary:
          StepPercentage: 15
          BakeTimeMins: 5
      Applications:
        - Target:
            Type: AWS::ECS::Service
            LogicalID: ECSDemoService
          ECSAttributes:
            TaskDefinitions:
              - BlueTaskDefinition
              - GreenTaskDefinition
            TaskSets:
              - BlueTaskSet
              - GreenTaskSet
            TrafficRouting:
              ProdTrafficRoute:
                Type: AWS::ElasticLoadBalancingV2::Listener
                LogicalID: ALBListenerProdTraffic
              TargetGroups:
                - ALBTargetGroupBlue
                - ALBTargetGroupGreen
Resources:
  ExampleSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ec2 access
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
  ALBTargetGroupBlue:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 5
      HealthCheckPath: /
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 2
      HealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      Port: 80
      Protocol: HTTP
      Tags:
        - Key: Group
          Value: Example
      TargetType: ip
      UnhealthyThresholdCount: 4
      VpcId: !Ref Vpc
  ALBTargetGroupGreen:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 5
      HealthCheckPath: /
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 2
      HealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      Port: 80
      Protocol: HTTP
      Tags:
        - Key: Group
          Value: Example
      TargetType: ip
      UnhealthyThresholdCount: 4
      VpcId: !Ref Vpc
  ExampleALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - !Ref ExampleSecurityGroup
      Subnets:
        - !Ref Subnet1
        - !Ref Subnet2
      Tags:
        - Key: Group
          Value: Example
      Type: application
      IpAddressType: ipv4
  ALBListenerProdTraffic:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref ALBTargetGroupBlue
                Weight: 1
      LoadBalancerArn: !Ref ExampleALB
      Port: 80
      Protocol: HTTP
  ALBListenerProdRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref ALBTargetGroupBlue
                Weight: 1
      Conditions:
        - Field: http-header
          HttpHeaderConfig:
            HttpHeaderName: User-Agent
            Values:
              - Mozilla
      ListenerArn: !Ref ALBListenerProdTraffic
      Priority: 1
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
  BlueTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ExecutionRoleArn: !GetAtt 
        - ECSTaskExecutionRole
        - Arn
      ContainerDefinitions:
        - Name: DemoApp
          Image: 'nginxdemos/hello:latest'
          Essential: true
          PortMappings:
            - HostPort: 80
              Protocol: tcp
              ContainerPort: 80
      RequiresCompatibilities:
        - FARGATE
      NetworkMode: awsvpc
      Cpu: '256'
      Memory: '512'
      Family: ecs-demo
  ECSDemoCluster:
    Type: AWS::ECS::Cluster
    Properties: {}
  ECSDemoService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSDemoCluster
      DesiredCount: 1
      DeploymentController:
        Type: EXTERNAL
  BlueTaskSet:
    Type: AWS::ECS::TaskSet
    Properties:
      Cluster: !Ref ECSDemoCluster
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsVpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref ExampleSecurityGroup
          Subnets:
            - !Ref Subnet1
            - !Ref Subnet2
      PlatformVersion: 1.4.0
      Scale:
        Unit: PERCENT
        Value: 100
      Service: !Ref ECSDemoService
      TaskDefinition: !Ref BlueTaskDefinition
      LoadBalancers:
        - ContainerName: DemoApp
          ContainerPort: 80
          TargetGroupArn: !Ref ALBTargetGroupBlue
  PrimaryTaskSet:
    Type: AWS::ECS::PrimaryTaskSet
    Properties:
      Cluster: !Ref ECSDemoCluster
      Service: !Ref ECSDemoService
      TaskSetId: !GetAtt 
        - BlueTaskSet
        - Id
```