

# Step 1: Set up your infrastructure
<a name="tutorial-lambda-sam-setup-infrastructure"></a>

 This topic shows you how to use AWS SAM to create files for your AWS SAM template and your Lambda functions. Then, you use the AWS SAM `package` and `deploy` commands to generate the components in your infrastructure. When your infrastructure is ready, you have a CodeDeploy application and deployment group, the Lambda function to update and deploy, and two Lambda functions that contain validation tests that run when you deploy the Lambda function. When complete, you can use CloudFormation to view your components in the Lambda console or the AWS CLI to test your Lambda function. 

**Topics**
+ [Create your files](tutorial-lambda-create-files.md)
+ [Package the AWS SAM application](tutorial-lambda-sam-package.md)
+ [Deploy the AWS SAM application](tutorial-lambda-sam-deploy.md)
+ [(Optional) inspect and test your infrastructure](tutorial-lambda-sam-confirm-components.md)

# Create your files
<a name="tutorial-lambda-create-files"></a>

 To create your infrastructure, you must create the following files: 
+ `template.yml`
+ `myDateTimeFunction.js`
+ `beforeAllowTraffic.js`
+ `afterAllowTraffic.js`

**Topics**
+ [Create your AWS SAM template](tutorial-lambda-sam-template.md)
+ [Create a file for your Lambda function](tutorial-lambda-sam-create-lambda-function.md)
+ [Create a file for your BeforeAllowTraffic Lambda function](tutorial-lambda-sam-create-lambda-before-traffic.md)
+ [Create a file for your AfterAllowTraffic Lambda function](tutorial-lambda-sam-create-lambda-after-traffic.md)

# Create your AWS SAM template
<a name="tutorial-lambda-sam-template"></a>

Create an AWS SAM template file that specifies the components in your infrastructure.

**To create your AWS SAM template**

1.  Create a directory named `SAM-Tutorial`. 

1.  In your `SAM-Tutorial` directory, create a file named `template.yml`. 

1.  Copy the following YAML code into `template.yml`. This is your AWS SAM template. 

   ```
   AWSTemplateFormatVersion : '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   Description: A sample SAM template for deploying Lambda functions.
   
   Resources:
   # Details about the myDateTimeFunction Lambda function
     myDateTimeFunction:
       Type: AWS::Serverless::Function
       Properties:
         Handler: myDateTimeFunction.handler
         Runtime: nodejs18.x
   # Instructs your myDateTimeFunction is published to an alias named "live".      
         AutoPublishAlias: live
   # Grants this function permission to call lambda:InvokeFunction
         Policies:
           - Version: "2012-10-17"		 	 	 
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: '*'
         DeploymentPreference:
   # Specifies the deployment configuration      
             Type: Linear10PercentEvery1Minute
   # Specifies Lambda functions for deployment lifecycle hooks
             Hooks:
               PreTraffic: !Ref beforeAllowTraffic
               PostTraffic: !Ref afterAllowTraffic
               
   # Specifies the BeforeAllowTraffic lifecycle hook Lambda function
     beforeAllowTraffic:
       Type: AWS::Serverless::Function
       Properties:
         Handler: beforeAllowTraffic.handler
         Policies:
           - Version: "2012-10-17"		 	 	 
   # Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus        
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "codedeploy:PutLifecycleEventHookExecutionStatus"
               Resource:
                 !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
           - Version: "2012-10-17"		 	 	 
   # Grants this function permission to call lambda:InvokeFunction        
             Statement: 
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: !Ref myDateTimeFunction.Version
         Runtime: nodejs18.x
   # Specifies the name of the Lambda hook function      
         FunctionName: 'CodeDeployHook_beforeAllowTraffic'
         DeploymentPreference:
           Enabled: false
         Timeout: 5
         Environment:
           Variables:
             NewVersion: !Ref myDateTimeFunction.Version
             
   # Specifies the AfterAllowTraffic lifecycle hook Lambda function
     afterAllowTraffic:
       Type: AWS::Serverless::Function
       Properties:
         Handler: afterAllowTraffic.handler
         Policies:
           - Version: "2012-10-17"		 	 	 
             Statement: 
   # Grants this function permission to call codedeploy:PutLifecycleEventHookExecutionStatus         
             - Effect: "Allow"
               Action: 
                 - "codedeploy:PutLifecycleEventHookExecutionStatus"
               Resource:
                 !Sub 'arn:aws:codedeploy:${AWS::Region}:${AWS::AccountId}:deploymentgroup:${ServerlessDeploymentApplication}/*'
           - Version: "2012-10-17"		 	 	 
             Statement: 
   # Grants this function permission to call lambda:InvokeFunction          
             - Effect: "Allow"
               Action: 
                 - "lambda:InvokeFunction"
               Resource: !Ref myDateTimeFunction.Version
         Runtime: nodejs18.x
   # Specifies the name of the Lambda hook function      
         FunctionName: 'CodeDeployHook_afterAllowTraffic'
         DeploymentPreference:
           Enabled: false
         Timeout: 5
         Environment:
           Variables:
             NewVersion: !Ref myDateTimeFunction.Version
   ```

This template specifies the following. For more information, see [AWS SAM template concepts](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-template-basics.html).

**A Lambda function called `myDateTimeFunction`**  
 When this Lambda function is published, the `AutoPublishAlias` line in the template links it to an alias named `live`. Later in this tutorial, an update to this function triggers a deployment by AWS CodeDeploy that incrementally shifts production traffic from the original version to the updated version. 

**Two Lambda deployment validation functions**  
 The following Lambda functions are executed during CodeDeploy lifecycle hooks. The functions contain code that validate the deployment of the updated `myDateTimeFunction`. The result of the validation tests are passed to CodeDeploy using its `PutLifecycleEventHookExecutionStatus` API method. If a validation test fails, the deployment fails and is rolled back.   
+  `CodeDeployHook_beforeAllowTraffic` runs during the `BeforeAllowTraffic` hook. 
+  `CodeDeployHook_afterAllowTraffic` runs during the `AfterAllowTraffic` hook. 
The name of both functions start with `CodeDeployHook_`. The `CodeDeployRoleForLambda` role allows calls to the Lambda `invoke` method only in Lambda functions with names that start with this prefix. For more information, see [AppSpec 'hooks' section for an AWS Lambda deployment](reference-appspec-file-structure-hooks.md#appspec-hooks-lambda) and [PutLifecycleEventHookExecutionStatus](https://docs.aws.amazon.com/codedeploy/latest/APIReference/API_PutLifecycleEventHookExecutionStatus.html) in the *CodeDeploy API Reference*. 

**Automatic detection of an updated Lambda function**  
 The `AutoPublishAlias` term tells the framework to detect when the `myDateTimeFunction` function changes, and then deploy it using the `live` alias. 

**A deployment configuration**  
 The deployment configuration determines the rate at which your CodeDeploy application shifts traffic from the original version of the Lambda function to the new version. This template specifies the predefined deployment configuration `Linear10PercentEvery1Minute`.   
 You cannot specify a custom deployment configuration in an AWS SAM template. For more information, see [Create a deployment configuration with CodeDeploy](deployment-configurations-create.md).

**Deployment lifecycle hook functions**  
 The `Hooks` section specifies the functions to run during lifecycle event hooks. `PreTraffic` specifies the function that runs during the `BeforeAllowTraffic` hook. `PostTraffic` specifies the function that runs during the `AfterAllowTraffic` hook. 

**Permissions for Lambda to invoke another Lambda function**  
 The specified `lambda:InvokeFunction` permission grants the role used by the AWS SAM application permission to invoke a Lambda function. This is required when the `CodeDeployHook_beforeAllowTraffic` and `CodeDeployHook_afterAllowTraffic` functions invoke the deployed Lambda function during validation tests. 

# Create a file for your Lambda function
<a name="tutorial-lambda-sam-create-lambda-function"></a>

Create the file for the function you update and deploy later in this tutorial.

**Note**  
 A Lambda function can use any runtime supported by AWS Lambda. For more information, see [AWS Lambda runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). 

**To create your Lambda function**

1.  Create a text file and save it as `myDateTimeFunction.js` in the `SAM-Tutorial` directory. 

1.  Copy the following Node.js code into `myDateTimeFunction.js`. 

   

   ```
   'use strict';
       
       exports.handler = function(event, context, callback) {
       
         if (event.body) {
           event = JSON.parse(event.body);
         }
       
         var sc; // Status code
         var result = ""; // Response payload
       
         switch(event.option) {
           case "date": 
             switch(event.period) {
               case "yesterday":
                 result = setDateResult("yesterday");
                 sc = 200;
                 break;
               case "today":
                 result = setDateResult();
                 sc = 200;
                 break;
               case "tomorrow":
                 result = setDateResult("tomorrow");
                 sc = 200;
                 break;
               default:
                 result = {
                   "error": "Must specify 'yesterday', 'today', or 'tomorrow'."
                 };
                 sc = 400;
                 break;
             }
             break;
             
       /*      Later in this tutorial, you update this function by uncommenting 
               this section. The framework created by AWS SAM detects the update 
               and triggers a deployment by CodeDeploy. The deployment shifts 
               production traffic to the updated version of this function.
               
               case "time":
               var d = new Date();
               var h = d.getHours();
               var mi = d.getMinutes();
               var s = d.getSeconds();
       
               result = {
                 "hour": h,
                 "minute": mi,
                 "second": s
               };
               sc = 200;
               break;
       */
             default:
               result = {
                 "error": "Must specify 'date' or 'time'."
               };
               sc = 400;
             break;
         }
       
         const response = {
           statusCode: sc,
           headers: { "Content-type": "application/json" },
           body: JSON.stringify( result )
         };
       
         callback(null, response);
       
         function setDateResult(option) {
       
           var d = new Date(); // Today
           var mo; // Month
           var da; // Day
           var y; // Year
       
           switch(option) {
             case "yesterday":
               d.setDate(d.getDate() - 1);
               break;
             case "tomorrow":
               d.setDate(d.getDate() + 1);
             default:
              break;
           }
       
           mo = d.getMonth() + 1; // Months are zero offset (0-11)
           da = d.getDate();
           y = d.getFullYear();
       
           result = {
             "month": mo,
             "day": da,
             "year": y
           };
       
           return result;
         }
       };
   ```

The Lambda function returns the day, month, and year for yesterday, today, or tomorrow. Later in this tutorial, you uncomment code that updates the function to return information about the day or time you specify (for example, the day, month, and year, or the current hour, minute, and second). The framework created by AWS SAM detects and deploys the updated version of the function. 

**Note**  
 This Lambda function is also used in an AWS Cloud9 tutorial. AWS Cloud9 is a cloud-based integrated development environment. For information about how to create, execute, update, and debug this function in AWS Cloud9, see [AWS Lambda tutorial for AWS Cloud9](https://docs.aws.amazon.com/cloud9/latest/user-guide/tutorial-lambda.html). 

# Create a file for your BeforeAllowTraffic Lambda function
<a name="tutorial-lambda-sam-create-lambda-before-traffic"></a>

Create the file for your `beforeAllowTraffic` hook Lambda function.

1.  Create a text file and save it as `beforeAllowTraffic.js` in the `SAM-Tutorial` directory. 

1.  Copy the following Node.js code into `beforeAllowTraffic.js`. This function executes during your deployment's `BeforeAllowTraffic` hook. 

   ```
   'use strict';
       
       const AWS = require('aws-sdk'); 
       const codedeploy = new AWS.CodeDeploy({apiVersion: '2014-10-06'});
       var lambda = new AWS.Lambda();
       
       exports.handler = (event, context, callback) => {
       
       	console.log("Entering PreTraffic Hook!");
       	
       	// Read the DeploymentId and LifecycleEventHookExecutionId from the event payload
         var deploymentId = event.DeploymentId;
       	var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId;
       
       	var functionToTest = process.env.NewVersion;
       	console.log("BeforeAllowTraffic hook tests started");
       	console.log("Testing new function version: " + functionToTest);
       
       	// Create parameters to pass to the updated Lambda function that
       	// include the newly added "time" option. If the function did not
       	// update, then the "time" option is invalid and function returns
       	// a statusCode of 400 indicating it failed.
       	var lambdaParams = {
       		FunctionName: functionToTest,    
       		Payload: "{\"option\": \"time\"}", 
       		InvocationType: "RequestResponse"
       	};
       
       	var lambdaResult = "Failed";
       	// Invoke the updated Lambda function.
       	lambda.invoke(lambdaParams, function(err, data) {
       		if (err){	// an error occurred
       			console.log(err, err.stack);
       			lambdaResult = "Failed";
       		}
       		else{	// successful response
       			var result = JSON.parse(data.Payload);
       			console.log("Result: " +  JSON.stringify(result));
             console.log("statusCode: " + result.statusCode);
             
             // Check if the status code returned by the updated
             // function is 400. If it is, then it failed. If 
             // is not, then it succeeded.
       			if (result.statusCode != "400"){
               console.log("Validation succeeded");
       				lambdaResult = "Succeeded";
             }
             else {
               console.log("Validation failed");
             }
       
       			// Complete the PreTraffic Hook by sending CodeDeploy the validation status
       			var params = {
       				deploymentId: deploymentId,
       				lifecycleEventHookExecutionId: lifecycleEventHookExecutionId,
       				status: lambdaResult // 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("CodeDeploy Status update failed");
       					console.log(err, err.stack);
       					callback("CodeDeploy Status update failed");
       				} else {
       					// Validation succeeded.
       					console.log("CodeDeploy status updated successfully");
       					callback(null, "CodeDeploy status updated successfully");
       				}
       			});
       		}  
       	});
       }
   ```

# Create a file for your AfterAllowTraffic Lambda function
<a name="tutorial-lambda-sam-create-lambda-after-traffic"></a>

Create the file for your `afterAllowTraffic` hook Lambda function.

1.  Create a text file and save it as `afterAllowTraffic.js` in the `SAM-Tutorial` directory. 

1.  Copy the following Node.js code into `afterAllowTraffic.js`. This function executes during your deployment's `AfterAllowTraffic` hook. 

   ```
   'use strict';
       
       const AWS = require('aws-sdk');
       const codedeploy = new AWS.CodeDeploy({apiVersion: '2014-10-06'});
       var lambda = new AWS.Lambda();
       
       exports.handler = (event, context, callback) => {
       
       	console.log("Entering PostTraffic Hook!");
       	
       	// Read the DeploymentId and LifecycleEventHookExecutionId from the event payload
         var deploymentId = event.DeploymentId;
       	var lifecycleEventHookExecutionId = event.LifecycleEventHookExecutionId;
       
       	var functionToTest = process.env.NewVersion;
       	console.log("AfterAllowTraffic hook tests started");
       	console.log("Testing new function version: " + functionToTest);
       
       	// Create parameters to pass to the updated Lambda function that
       	// include the original "date" parameter. If the function did not 
       	// update as expected, then the "date" option might be invalid. If 
       	// the parameter is invalid, the function returns
       	// a statusCode of 400 indicating it failed.
       	var lambdaParams = {
       		FunctionName: functionToTest,    
       		Payload: "{\"option\": \"date\", \"period\": \"today\"}", 
       		InvocationType: "RequestResponse"
       	};
       
       	var lambdaResult = "Failed";
       	// Invoke the updated Lambda function.
       	lambda.invoke(lambdaParams, function(err, data) {
       		if (err){	// an error occurred
       			console.log(err, err.stack);
       			lambdaResult = "Failed";
       		}
       		else{	// successful response
       			var result = JSON.parse(data.Payload);
       			console.log("Result: " +  JSON.stringify(result));
             console.log("statusCode: " + result.statusCode);
             
             // Check if the status code returned by the updated
             // function is 400. If it is, then it failed. If 
             // is not, then it succeeded.
       			if (result.statusCode != "400"){
               console.log("Validation of time parameter succeeded");
       				lambdaResult = "Succeeded";
             }
             else {
               console.log("Validation failed");
             }
       
       			// Complete the PostTraffic Hook by sending CodeDeploy the validation status
       			var params = {
       				deploymentId: deploymentId,
       				lifecycleEventHookExecutionId: lifecycleEventHookExecutionId,
       				status: lambdaResult // 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("CodeDeploy Status update failed");
       					console.log(err, err.stack);
       					callback("CodeDeploy Status update failed");
       				} else {
       					// Validation succeeded.
       					console.log("CodeDeploy status updated successfully");
       					callback(null, "CodeDeploy status updated successfully");
       				}
       			});
       		}  
       	});
       }
   ```

# Package the AWS SAM application
<a name="tutorial-lambda-sam-package"></a>

 You should now have four files in your `SAM-Tutorial` directory: 
+ `beforeAllowTraffic.js`
+ `afterAllowTraffic.js`
+ `myDateTimeFunction.js`
+ `template.yml`

 You are now ready to use the AWS SAM **sam package** command to create and package artifacts for your Lambda functions and CodeDeploy application. The artifacts are uploaded to an S3 bucket. The output of the command is a new file called `package.yml`. This file is used by the AWS SAM **sam deploy** command in the next step. 

**Note**  
 For more information on the **sam package** command, see [AWS SAM CLI command reference](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) in the *AWS Serverless Application Model Developer Guide*. 

 In the `SAM-Tutorial` directory, run the following. 

```
sam package \
  --template-file template.yml \
  --output-template-file package.yml \
  --s3-bucket amzn-s3-demo-bucket
```

For the `s3-bucket` parameter, specify the Amazon S3 bucket you created as a prerequisite for this tutorial. The `output-template-file` specifies the name of the new file that is used by the AWS SAM **sam deploy** command.

# Deploy the AWS SAM application
<a name="tutorial-lambda-sam-deploy"></a>

 Use the AWS SAM **sam deploy** command with the `package.yml` file to create your Lambda functions and CodeDeploy application and deployment group using CloudFormation. 

**Note**  
For more information on the **sam deploy** command, see [AWS SAM CLI command reference](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-command-reference.html) in the *AWS Serverless Application Model Developer Guide*. 

 In the `SAM-Tutorial` directory, run the following command. 

```
sam deploy \
  --template-file package.yml \
  --stack-name my-date-time-app \
  --capabilities CAPABILITY_IAM
```

 The `--capabilities CAPABILITY_IAM` parameter is required to authorize CloudFormation to create IAM roles. 

# (Optional) inspect and test your infrastructure
<a name="tutorial-lambda-sam-confirm-components"></a>

 This topic shows how to view your infrastructure components and test your Lambda function. 

**To see the result of your stack after you run `sam deploy`**

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

1.  In the navigation pane, choose **Stacks**. The `my-date-time-app` stack appears at the top. 

1.  Choose the **Events** tab to see which events are complete. You can view the events while the stack creation is in progress. When creation of the stack is complete, you can see all stack creation events. 

1.  With your stack selected, choose **Resources**. In the **Type** column, you can see your Lambda functions, `myDateTimeFunction`, `CodeDeployHook_beforeAllowTraffic`, and `CodeDeployHook_afterAllowTraffic`. The **Physical ID** column of each of your Lambda functions contains a link to view the functions in the Lambda console. 
**Note**  
 The name of the `myDateTimeFunction` Lambda function is prepended with the name of the CloudFormation stack and has an identifier added to it, so it looks like `my-date-time-app-myDateTimeFunction-123456ABCDEF`. 

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

1.  In the navigation pane, expand **Deploy**, and then choose **Applications**. 

1.  You should see a new CodeDeploy application created by CloudFormation with a name that starts with `my-date-time-app-ServerlessDeploymentApplication`. Choose this application. 

1.  You should see a deployment group with a name that starts with `my-date-time-app-myDateTimeFunctionDeploymentGroup`. Choose this deployment group. 

    Under **Deployment configuration**, you should see **CodeDeployDefault.LambdaLinear10PercentEvery1Minute**. 

**(Optional) to test your function (console)**

1. Open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).

1.  From the navigation pane, choose your `my-date-time-app-myDateTimeFunction` function. In the console, its name contains an identifier, so it looks like `my-date-time-app-myDateTimeFunction-123456ABCDEF`. 

1.  Choose **Test**. 

1.  In **Event name**, enter a name for your test event. 

1.  Enter the following for your test event, and then choose **Create**. 

   ```
   {
     "option": "date",
     "period": "today"
   }
   ```

1.  Choose **Test**. You should see only your test event in the list of test events. 

    For **Execution result**, you should see **succeeded**. 

1.  Under **Execution result**, expand **Details** to see the results. You should see the current month, day, and year. 

**(Optional) to test your function (AWS CLI)**

1.  Locate the ARN of your Lambda function. It appears at the top of the Lambda console when you are viewing your function. 

1.  Run the following command. Replace *your-function-arn* with the function ARN. 

   ```
   aws lambda invoke \
   --function your-function-arn \
   --cli-binary-format raw-in-base64-out \
   --payload "{\"option\": \"date\", \"period\": \"today\"}" out.txt
   ```

1.  Open `out.txt` to confirm the result contains the current month, day, and year. 