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