Blue/green deployment template example
The following example template sets up a CodeDeploy blue/green deployment on ECS, with a traffic routing progress of 15 percent per step and a stabilization period of 5 minutes between each step.
Creating a stack with the template will provision the initial configuration of the
deployment. If you then made any changes to properties in the BlueTaskSet
resource that require that resource be replaced, CloudFormation will then initiate a green
deployment as part of the stack update.
JSON
{ "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
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