

# Tutorial: Deploy an Amazon ECS service with a validation test
<a name="tutorial-ecs-deployment-with-hooks"></a>

 In this tutorial, you learn how to use a Lambda function to validate part of the deployment of an updated Amazon ECS application. This tutorial uses the CodeDeploy application, CodeDeploy deployment group, and the Amazon ECS application you used in [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). Complete that tutorial before starting this one.

 To add validation test, you first implement the test in a Lambda function. Next, in your deployment AppSpec file, you specify the Lambda function for the lifecycle hook you want to test. If a validation test fails, the deployment stops, is rolled back, and marked failed. If the test succeeds, the deployment continues to the next deployment lifecycle event or hook. 

 During an Amazon ECS deployment with validation tests, CodeDeploy uses a load balancer that is configured with two target groups: one production traffic listener and one test traffic listener. The following diagram shows how the load balancer, production and test listeners, target groups, and your Amazon ECS application are related before the deployment starts. This tutorial uses an Application Load Balancer. You can also use a Network Load Balancer. 

![\[The connections between the Application Load Balancer or Network Load Balancer, listeners, target groups, task set, and Amazon ECS service.\]](http://docs.aws.amazon.com/codedeploy/latest/userguide/images/codedeploy-ecs-deployment-step-1.png)


 During an Amazon ECS deployment, there are five lifecycle hooks for testing. This tutorial implements one test during the third lifecycle deployment hook, `AfterAllowTestTraffic`. For more information, see [List of lifecycle event hooks for an Amazon ECS deployment](reference-appspec-file-structure-hooks.md#reference-appspec-file-structure-hooks-list-ecs). After a successful deployment, the production traffic listener serves traffic to your new replacment task set and the original task set is terminated. The following diagram shows how your resources are related after a successful deployment. For more information, see [What happens during an Amazon ECS deployment](deployment-steps-ecs.md#deployment-steps-what-happens). 

![\[The connections between the Application Load Balancer or Network Load Balancer, listeners, target groups, and replacement task set after a deployment.\]](http://docs.aws.amazon.com/codedeploy/latest/userguide/images/codedeploy-ecs-deployment-step-6.png)


**Note**  
Completing this tutorial might result in charges to your AWS account. These include possible charges for CodeDeploy, AWS Lambda, and CloudWatch. For more information, see [AWS CodeDeploy pricing](https://aws.amazon.com/codedeploy/pricing/), [AWS Lambda pricing](https://aws.amazon.com/lambda/pricing/), and [Amazon CloudWatch pricing](https://aws.amazon.com/cloudwatch/pricing/).

**Topics**
+ [Prerequisites](tutorial-ecs-with-hooks-prereqs.md)
+ [Step 1: Create a test listener](tutorial-ecs-with-hooks-create-second-listener.md)
+ [Step 2: Update your Amazon ECS application](tutorial-ecs-with-hooks-update-the-ecs-application.md)
+ [Step 3: Create a lifecycle hook Lambda function](tutorial-ecs-with-hooks-create-hooks.md)
+ [Step 4: Update your AppSpec file](tutorial-ecs-with-hooks-create-appspec-file.md)
+ [Step 5: Use the CodeDeploy console to deploy your Amazon ECS service](tutorial-ecs-with-hooks-deployment.md)
+ [Step 6: View your Lambda hook function output in CloudWatch Logs](tutorial-ecs-with-hooks-view-cw-logs.md)
+ [Step 7: Clean up](tutoria-ecs-with-hooks-clean-up.md)

# Prerequisites
<a name="tutorial-ecs-with-hooks-prereqs"></a>

To successfully complete this tutorial, you must first:
+  Complete the prerequisites in [Prerequisites](tutorial-ecs-prereqs.md) for [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). 
+  Complete the steps in [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). Make a note of the following: 
  +  The name of your load balancer. 
  +  The names of your target groups. 
  +  The port used by your load balancer's listener. 
  +  The ARN of your load balancer. You use this to create a new listener. 
  +  The ARN of one of your target groups. You use this to create a new listener. 
  +  The CodeDeploy application and deployment group you create. 
  +  The AppSpec file you create that is used by your CodeDeploy deployment. You edit this file in this tutorial. 

# Step 1: Create a test listener
<a name="tutorial-ecs-with-hooks-create-second-listener"></a>

 An Amazon ECS deployment with validation tests requires a second listener. This listener is used to serve test traffic to your updated Amazon ECS application in a replacement task set. Your validation tests run against the test traffic. 

 The listener for your test traffic can use either of your target groups. Use the [create-listener](https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-listener.html) AWS CLI command to create a second listener with a default rule that forwards test traffic to port 8080. Use the ARN of your load balancer and the ARN of one of your target groups.

```
aws elbv2 create-listener --load-balancer-arn your-load-balancer-arn \
--protocol HTTP --port 8080 \
--default-actions Type=forward,TargetGroupArn=your-target-group-arn --region your-aws-region
```

# Step 2: Update your Amazon ECS application
<a name="tutorial-ecs-with-hooks-update-the-ecs-application"></a>

 In this section, you update your Amazon ECS application to use a new revision of its task definition. You create the new revision and add a minor update to it by adding a tag. 

**To update your task definition**

1. Open the Amazon ECS classic console at [https://console.aws.amazon.com/ecs/](https://console.aws.amazon.com/ecs/).

1.  In the navigation pane, choose **Task Definitions**. 

1.  Select the check box for the task definition used by your Amazon ECS service.

1.  Choose **Create new revision**. 

1.  Make a small update to the task definition by adding a tag. At the bottom of the page, in **Tags**, create a new tag by entering a new key and value pair. 

1.  Choose **Create**. You should see that your task definition's revision number has been incremented by one. 

1.  Choose the **JSON** tab. Make a note of the value for `taskDefinitionArn`. Its format is `arn:aws:ecs:aws-region: account-id:task-definition/task-definition-family: task-definition-revision`. This is the ARN of your updated task definition. 

# Step 3: Create a lifecycle hook Lambda function
<a name="tutorial-ecs-with-hooks-create-hooks"></a>

In this section, you implement one Lambda function for your Amazon ECS deployment's `AfterAllowTestTraffic` hook. The Lambda function runs a validation test before the updated Amazon ECS application is installed. For this tutorial, the Lambda function returns `Succeeded`. During a real world deployment, validation tests return `Succeeded` or `Failed`, depending on the result of the validation test. Also during a real world deployment, you might implement a Lambda test function for one or more of the other Amazon ECS deployment lifecycle event hooks (`BeforeInstall`, `AfterInstall`, `BeforeAllowTraffic`, and `AfterAllowTraffic`). For more information, see [List of lifecycle event hooks for an Amazon ECS deployment](reference-appspec-file-structure-hooks.md#reference-appspec-file-structure-hooks-list-ecs).

 An IAM role is required to create your Lambda function. The role grants the Lambda function permission to write to CloudWatch Logs and set the status of a CodeDeploy lifecycle hook. 

**To create an IAM role**

1. Open the IAM console at [https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/).

1. From the navigation pane, choose **Roles**, and then choose **Create role**.

1.  Create a role with the following properties: 
   +  **Trusted entity**: **AWS Lambda**. 
   +  **Permissions**: **AWSLambdaBasicExecutionRole**. This grants your Lambda function permission to write to CloudWatch Logs. 
   +  **Role name**: **`lambda-cli-hook-role`**. 

   For more information, see [ Create an AWS Lambda execution role](https://docs.aws.amazon.com/lambda/latest/dg/with-userapp.html#with-userapp-walkthrough-custom-events-create-iam-role). 

1.  Attach the permission `codedeploy:PutLifecycleEventHookExecutionStatus` to the role you created. This grants your Lambda functions permission to set the status of a CodeDeploy lifecycle hook during a deployment. For more information, see [Adding IAM identity permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_manage-attach-detach.html#add-policies-console) in the *AWS Identity and Access Management User Guide* and [PutLifecycleEventHookExecutionStatus](https://docs.aws.amazon.com/codedeploy/latest/APIReference/API_PutLifecycleEventHookExecutionStatus.html) in the *CodeDeploy API Reference*. 

**To create an `AfterAllowTestTraffic` hook Lambda function**

1.  Create a file named `AfterAllowTestTraffic.js` with the following contents. 

   ```
   'use strict';
    
    const AWS = require('aws-sdk');
    const codedeploy = new AWS.CodeDeploy({apiVersion: '2014-10-06'});
    
    exports.handler = (event, context, callback) => {
    
    	console.log("Entering AfterAllowTestTraffic hook.");
    	
    	// Read the DeploymentId and LifecycleEventHookExecutionId from the event payload
     var deploymentId = event.DeploymentId;
    	var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId;
    	var validationTestResult = "Failed";
    	
    	// Perform AfterAllowTestTraffic validation tests here. Set the test result 
    	// to "Succeeded" for this tutorial.
    	console.log("This is where AfterAllowTestTraffic validation tests happen.")
    	validationTestResult = "Succeeded";
    	
    	// Complete the AfterAllowTestTraffic hook by sending CodeDeploy the validation status
    	var params = {
    		deploymentId: deploymentId,
    		lifecycleEventHookExecutionId: lifecycleEventHookExecutionId,
    		status: validationTestResult // status can be 'Succeeded' or 'Failed'
    	};
    	
    	// Pass CodeDeploy the prepared validation test results.
    	codedeploy.putLifecycleEventHookExecutionStatus(params, function(err, data) {
    		if (err) {
    			// Validation failed.
    			console.log('AfterAllowTestTraffic validation tests failed');
    			console.log(err, err.stack);
    			callback("CodeDeploy Status update failed");
    		} else {
    			// Validation succeeded.
    			console.log("AfterAllowTestTraffic validation tests succeeded");
    			callback(null, "AfterAllowTestTraffic validation tests succeeded");
    		}
    	});
    }
   ```

1.  Create a Lambda deployment package. 

   ```
   zip AfterAllowTestTraffic.zip AfterAllowTestTraffic.js 
   ```

1.  Use the `create-function` command to create a Lambda function for your `AfterAllowTestTraffic` hook. 

   ```
   aws lambda create-function --function-name AfterAllowTestTraffic \
          --zip-file fileb://AfterAllowTestTraffic.zip \
          --handler AfterAllowTestTraffic.handler \
          --runtime nodejs10.x \
          --role arn:aws:iam::aws-account-id:role/lambda-cli-hook-role
   ```

1.  Make a note of your Lambda function ARN in the `create-function` response. You use this ARN when you update your CodeDeploy deployment's AppSpec file in the next step. 

# Step 4: Update your AppSpec file
<a name="tutorial-ecs-with-hooks-create-appspec-file"></a>

 In this section, you update your AppSpec file with a `Hooks` section. In the `Hooks` section, you specify a Lambda function for the `AfterAllowTestTraffic` lifecycle hook. 

**To update your AppSpec file**

1.  Open the AppSpec file file you created in [Step 2: Create the AppSpec file](tutorial-ecs-create-appspec-file.md) of the [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). 

1.  Update the `TaskDefinition` property with the task definition ARN you noted in [Step 2: Update your Amazon ECS application](tutorial-ecs-with-hooks-update-the-ecs-application.md). 

1. Copy and paste the `Hooks` section into your AppSpec file file. Update the ARN after `AfterAllowTestTraffic` with the ARN of the Lambda function that you noted in [Step 3: Create a lifecycle hook Lambda function](tutorial-ecs-with-hooks-create-hooks.md). 

------
#### [ JSON AppSpec ]

   ```
   {
     "version": 0.0,
     "Resources": [
       {
         "TargetService": {
           "Type": "AWS::ECS::Service",
           "Properties": {
             "TaskDefinition": "arn:aws:ecs:aws-region-id:aws-account-id::task-definition/ecs-demo-task-definition:revision-number",
             "LoadBalancerInfo": {
               "ContainerName": "sample-website",
               "ContainerPort": 80
             }
           }
         }
       }
     ],
     "Hooks": [
       {
         "AfterAllowTestTraffic": "arn:aws:lambda:aws-region-id:aws-account-id:function:AfterAllowTestTraffic"
       }
     ]
   }
   ```

------
#### [ YAML AppSpec ]

   ```
   version: 0.0
   Resources:
     - TargetService:
         Type: AWS::ECS::Service
         Properties:
           TaskDefinition: "arn:aws:ecs:aws-region-id:aws-account-id::task-definition/ecs-demo-task-definition:revision-number"
           LoadBalancerInfo:
             ContainerName: "sample-website"
             ContainerPort: 80
   Hooks:
     - AfterAllowTestTraffic: "arn:aws:lambda:aws-region-id:aws-account-id:function:AfterAllowTestTraffic"
   ```

------

1.  Save your AppSpec file and upload to its S3 bucket. 

# Step 5: Use the CodeDeploy console to deploy your Amazon ECS service
<a name="tutorial-ecs-with-hooks-deployment"></a>

 In this section, you update your deployment group by specifying the port for your test listener. This is the listener you created in [Step 1: Create a test listener](tutorial-ecs-with-hooks-create-second-listener.md). During deployment, CodeDeploy runs your validation test during the `AfterAllowTestTraffic` deployment lifecycle hook using test traffic served to your replacement task set using the test listener. Your validation test returns the result `Succeeded`, so the deployment proceeds with the next deployment lifecycle event. In a real world scenario, your test function returns `Succeeded` or `Failed`. 

**To add a test listener to your deployment group**

1. Sign in to the AWS Management Console and open the CodeDeploy console at [https://console.aws.amazon.com/codedeploy/](https://console.aws.amazon.com/codedeploy/).

1. From the navigation pane, choose **Applications**. 

1. Choose the application you created in [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). If you used the suggested name, it is **ecs-demo-codedeploy-app**.

1. In **Deployment groups**, choose the deployment group you created in [Tutorial: Deploy an application into Amazon ECS](tutorial-ecs-deployment.md). If you used the suggested name, it is **ecs-demo-dg**.

1.  Choose **Edit**. 

1. From **Test listener port**, choose the port and protocol for the test listener you created earlier in this tutorial. This should be **HTTP: 8080**. 

1.  Choose **Save changes**. 

**To deploy your Amazon ECS application**

1. From your deployment group console page, choose **Create deployment**.

1.  For **Deployment group**, choose **ecs-demo-dg**. 

1.  For **Revision type**, choose **My application is stored in Amazon S3**. In **Revision location**, enter the name of your S3 bucket and AppSpec file (for example, **s3://my-s3-bucket/appspec.json**). 

1.  For **Revision file type**, choose **.json** or **.yaml** as appropriate. 

1.  (Optional) In **Deployment description**, enter a description for your deployment. 

1. Choose **Create deployment**.

 You can monitor your deployment in **Deployment status**. After 100% of production traffic is routed to the replacement task set, you can choose **Terminate original task set** to immediately terminate the original task set. If you do not choose **Terminate original task set**, the original task set terminates after the duration you specified when you created your deployment group. 

![\[The deployment status section of the CodeDeploy console.\]](http://docs.aws.amazon.com/codedeploy/latest/userguide/images/ecs-tutorial-deployment-status-with-test-listener.png)


# Step 6: View your Lambda hook function output in CloudWatch Logs
<a name="tutorial-ecs-with-hooks-view-cw-logs"></a>

 If your CodeDeploy deployment is successful, the validation tests in your Lambda hook fuctions are successful, too. You can confirm this by looking at the log for the hook function in CloudWatch Logs. 

1. Open the CloudWatch console at [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/).

1.  From the navigation pane, choose **Logs**. You should see one new log group for the Lambda hook function you specified in your AppSpec file.   
![\[The new log group in the CloudWatch console.\]](http://docs.aws.amazon.com/codedeploy/latest/userguide/images/ecs-demo-cw-logs.png)

1.  Choose the new log group. This should be **/aws/lambda/AfterAllowTestTrafficHook**. 

1.  Choose the log stream. If you see more than one log stream, choose the one with the most recent date and time under **Last Event Time**. 

1.  Expand the log stream events to confirm your Lambda hook function wrote success messages to the log. The following shows the `AfterAllowTraffic` Lambda hook function was successful.   
![\[The log stream events showing the AfterAllowTraffic hook.\]](http://docs.aws.amazon.com/codedeploy/latest/userguide/images/ecs-demo-cw-log-events.png)

# Step 7: Clean up
<a name="tutoria-ecs-with-hooks-clean-up"></a>

 When you have finished this tutorial, clean up the resources associated with it to avoid incurring charges for resources that you aren't using. The resource names in this step are the names suggested in this tutorial (for example, **ecs-demo-codedeploy-app** for the name of your CodeDeploy application). If you used different names, then be sure to use those in your cleanup. 

**To clean up the tutorial resources**

1. Use the [delete-deployment-group](https://docs.aws.amazon.com/cli/latest/reference/deploy/delete-deployment-group.html) command to delete the CodeDeploy deployment group.

   ```
   aws deploy delete-deployment-group --application-name ecs-demo-deployment-group --deployment-group-name ecs-demo-dg --region aws-region-id
   ```

1. Use the [delete-application](https://docs.aws.amazon.com/cli/latest/reference/deploy/delete-application.html) command to delete the CodeDeploy application.

   ```
   aws deploy delete-application --application-name ecs-demo-deployment-group --region aws-region-id
   ```

1. Use the [delete-function](https://docs.aws.amazon.com/cli/latest/reference/lambda/delete-function.html) command to delete your Lambda hook function.

   ```
   aws lambda delete-function --function-name AfterAllowTestTraffic
   ```

1. Use the [delete-log-group](https://docs.aws.amazon.com/cli/latest/reference/logs/delete-log-group.html) command to delete your CloudWatch log group.

   ```
   aws logs delete-log-group --log-group-name /aws/lambda/AfterAllowTestTraffic
   ```