AWSTemplateFormatVersion: '2010-09-09' Description: The StepFunction which controls how the canary rolls forward, or backward Parameters: TemplateBucket: Type: String Resources: LambdaExecutionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: root PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: arn:aws:logs:*:*:* - Effect: Allow Action: - dynamodb:* Resource: "*" - Effect: Allow Action: - route53:* Resource: "*" - Effect: Allow Action: - elasticloadbalancing:Describe* Resource: "*" MyLambdaFunction: Type: "AWS::Lambda::Function" Properties: Handler: lambda_function.lambda_handler # . Role: !GetAtt [ LambdaExecutionRole, Arn ] Code: S3Bucket: !Ref TemplateBucket # no s3:// in prefix S3Key: lambdafunctions/changeRoute53Weights.zip Description: Updates Route53 with new weights Runtime: "python3.6" MemorySize: 128 Timeout: 3 StatesExecutionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - !Sub states.${AWS::Region}.amazonaws.com Action: "sts:AssumeRole" Path: "/" Policies: - PolicyName: StatesExecutionPolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "lambda:InvokeFunction" Resource: "*" CheckHealthFunction: Type: "AWS::Lambda::Function" Properties: Handler: lambda_function.lambda_handler # . Role: !GetAtt [ LambdaExecutionRole, Arn ] Code: S3Bucket: !Ref TemplateBucket # no s3:// in prefix S3Key: lambdafunctions/checkGreenHealth.zip Description: Check the health of the NewLB TargetGroup, which is the "green" ECS service Runtime: "python3.6" MemorySize: 128 Timeout: 3 MyStateMachine: Type: AWS::StepFunctions::StateMachine Properties: DefinitionString: !Sub - |- { "Comment": "Altering Route53 weights for canary blue-green deployment", "StartAt": "FirstState", "States": { "FirstState": { "Type": "Pass", "Result": 10, "ResultPath":"$.weight", "Next": "change_10_percent" }, "change_10_percent": { "Type": "Task", "Resource": "${lambdaArn}", "ResultPath":"$.result_10", "TimeoutSeconds":3, "Next": "wait_60_secondsA" }, "wait_60_secondsA": { "Type": "Wait", "Seconds": 60, "Next": "check_healthA" }, "check_healthA":{ "Type": "Task", "Resource": "${healthArn}", "TimeoutSeconds":3, "ResultPath":"$.health", "Next": "ChoiceStateA" }, "ChoiceStateA": { "Type": "Choice", "Choices":[ { "Variable": "$.health.State", "StringEquals":"healthy", "Next": "send_50" } ], "Default":"fall-back" }, "fall-back":{ "Type": "Pass", "Result": 0, "ResultPath":"$.weight", "Next": "change_0_percent" }, "change_0_percent":{ "Type": "Task", "Resource": "${lambdaArn}", "ResultPath":"$.target", "TimeoutSeconds":3, "Next": "DefaultState" }, "DefaultState": { "Type": "Fail", "Cause": "New ECS service is not healthy!" }, "send_50": { "Type": "Pass", "Result": 50, "InputPath":"$.target", "ResultPath":"$.weight", "Next": "change_50_percent" }, "change_50_percent": { "Type": "Task", "Resource": "${lambdaArn}", "ResultPath":"$.result_50", "TimeoutSeconds":3, "Next": "wait_60_secondsB" }, "wait_60_secondsB": { "Type": "Wait", "Seconds": 60, "Next": "check_healthB" }, "check_healthB":{ "Type": "Task", "Resource": "${healthArn}", "TimeoutSeconds":3, "ResultPath":"$.health", "Next": "ChoiceStateB" }, "ChoiceStateB": { "Type": "Choice", "Choices":[ { "Variable": "$.health.State", "StringEquals":"healthy", "Next": "send_100" } ], "Default":"fall-back" }, "send_100": { "Type": "Pass", "Result": 100, "InputPath":"$.target", "ResultPath":"$.weight", "Next": "change_100_percent" }, "change_100_percent": { "Type": "Task", "Resource": "${lambdaArn}", "ResultPath":"$.result_100", "TimeoutSeconds":3, "Next":"wait_60_secondsC" }, "check_healthC":{ "Type": "Task", "Resource": "${healthArn}", "TimeoutSeconds":3, "ResultPath":"$.health", "Next": "ChoiceStateC" }, "ChoiceStateC": { "Type": "Choice", "Choices":[ { "Variable": "$.health.State", "StringEquals":"healthy", "Next": "SuccessState" } ], "Default":"fall-back" }, "wait_60_secondsC": { "Type": "Wait", "Seconds": 60, "Next": "check_healthC" }, "SuccessState": { "Type": "Pass", "Result" : "True", "End": true } } } - {lambdaArn: !GetAtt [ MyLambdaFunction, Arn ], healthArn: !GetAtt [ CheckHealthFunction, Arn]} RoleArn: !GetAtt [ StatesExecutionRole, Arn ] Outputs: StepArn: Description: The ARN of the StepFunction. This is needed by the handleECSEvents lambda Value: !Ref MyStateMachine