

# Lambda 支持的自定义资源
<a name="template-custom-resources-lambda"></a>

如果将 Lambda 函数与自定义资源关联，则在创建、更新或删除自定义资源时就会调用该函数。CloudFormation 将调用 Lambda API 来调用此函数，并将所有请求数据（如请求类型和资源属性）传递给此函数。通过将 Lambda 函数的强大功能和可自定义性与 CloudFormation 结合，您可以支持各种方案，例如在堆栈创建期间动态查找 AMI ID，或者实现和使用实用工具函数（例如，字符串反转函数）。

有关自定义资源及其工作原理的介绍，请参阅[自定义资源的工作原理](template-custom-resources.md#how-custom-resources-work)。

**Topics**
+ [演练：使用 Lambda 支持的自定义资源创建延迟机制](walkthrough-lambda-backed-custom-resources.md)
+ [`cfn-response` 模块](cfn-lambda-function-code-cfnresponsemodule.md)

# 演练：使用 Lambda 支持的自定义资源创建延迟机制
<a name="walkthrough-lambda-backed-custom-resources"></a>

本演练向您展示如何使用示例 CloudFormation 模板配置和启动 Lambda 支持的自定义资源。此模板创建了一种延迟机制，可在指定时间暂停堆栈部署。当您需要在资源预置期间引入故意的延迟时（例如在创建依赖资源之前等待资源稳定时），这可能非常有用。

**注意**  
虽然之前建议使用 Lambda 支持的自定义资源来检索 AMI ID，但现在建议使用 AWS Systems Manager 参数。此方法可以使您的模板提高可重用性和更易于维护。有关更多信息，请参阅 [获取 Systems Manager Parameter Store 中的纯文本值](dynamic-references-ssm.md)。

**Topics**
+ [概述](#walkthrough-lambda-backed-custom-resources-overview)
+ [示例模板](#walkthrough-lambda-backed-custom-resources-sample-template)
+ [示例模板演练](#walkthrough-lambda-backed-custom-resources-sample-template-walkthrough)
+ [先决条件](#walkthrough-lambda-backed-custom-resources-prerequisites)
+ [启动堆栈](#walkthrough-lambda-backed-custom-resources-createfunction-createstack)
+ [清理资源](#walkthrough-lambda-backed-custom-resources-createfunction-cleanup)
+ [相关信息](#w2aac11c45b9c24b9c23)

## 概述
<a name="walkthrough-lambda-backed-custom-resources-overview"></a>

此演练中使用的示例堆栈模板创建了 Lambda 支持的自定义资源。此自定义资源在堆栈创建过程中引入了可配置的延迟（默认为 60 秒）。只有在修改自定义资源的属性时，才会在堆栈更新期间出现延迟。

此模板预置以下资源：
+ 自定义资源，
+ Lambda 函数，和
+ 使 Lambda 能够将日志写入到 CloudWatch 的 IAM 角色。

它还定义了两个输出：
+ 函数等待的实际时间。
+ 每次执行 Lambda 函数期间生成的唯一标识符。



**注意**  
CloudFormation 是一项免费服务，但 Lambda 会根据您的函数请求数量和代码执行时间收费。有关 Lambda 定价的更多信息，请参阅 [AWS Lambda 定价](https://aws.amazon.com/lambda/pricing/)。

## 示例模板
<a name="walkthrough-lambda-backed-custom-resources-sample-template"></a>

您可以查看具有以下延迟机制的 Lambda 支持的自定义资源示例模板：

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-json"></a>

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-yaml"></a>

```
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
```

## 示例模板演练
<a name="walkthrough-lambda-backed-custom-resources-sample-template-walkthrough"></a>

以下代码段解释了示例模板的相关部分，以帮助您了解 Lambda 函数如何与自定义资源关联并理解输出。

[AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html) 资源 `CFNWaiter`  
`AWS::Lambda::Function` 资源指定了函数的源代码、处理程序名称、运行时环境和执行角色的 Amazon 资源名称（ARN）。  
由于 `Handler` 属性使用 Python 源代码，因此将其设置为 `index.handler`。有关使用内联函数源代码时可接受的处理程序标识符的更多信息，请参阅 [ AWS::Lambda::Function 代码](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-zipfile)。  
由于源文件是 Python 代码，因此 `Runtime` 指定为 `python3.9`。  
`Timeout` 设置为 900 秒。  
`Role` 属性使用 `Fn::GetAtt` 函数来获取在模板中的 `AWS::IAM::Role` 资源中声明的 `LambdaExecutionRole` 执行角色的 ARN。  
`Code` 属性使用 Python 函数以内联方式定义函数代码。示例模板中的 Python 函数执行下面的操作：  
+ 使用 UUID 创建唯一 ID
+ 检查请求是创建请求还是更新请求
+ 在 `Create` 或 `Update` 请求期间休眠 `ServiceTimeout` 指定的持续时间
+ 返回等待时间和唯一 ID

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-json"></a>

```
...
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-yaml"></a>

```
...
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
...
```

[AWS::IAM::Role](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-iam-role.html) 资源 `LambdaExecutionRole`  
`AWS::IAM:Role` 资源为 Lambda 函数创建一个执行角色，其中包括允许 Lambda 使用该角色的代入角色策略。它还包含允许 CloudWatch Logs 访问的策略。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-json"></a>

```
...
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-yaml"></a>

```
...
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
...
```

[AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html) 资源 `CFNWaiterCustomResource`  
自定义资源使用 `!GetAtt CFNWaiter.Arn` 将其 ARN 链接到 Lambda 函数。它将为创建和更新操作实现 60 秒的等待时间，如 `ServiceTimeout` 中设置的那样。只有当属性被修改时，才会调用资源进行更新操作。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-json"></a>

```
...
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-yaml"></a>

```
...
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
...
```

`Outputs`  
此模板的 `Outputs` 是 `TimeWaited` 和 `WaiterId`。`TimeWaited` 值使用 `Fn::GetAtt` 函数提供等待者资源实际等待的时间。`WaiterId` 使用 `Fn::GetAtt` 函数来提供生成并与执行关联的唯一 ID。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-json"></a>

```
...
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-yaml"></a>

```
...
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
...
```

## 先决条件
<a name="walkthrough-lambda-backed-custom-resources-prerequisites"></a>

您必须拥有 IAM 权限才能使用所有相应的服务，例如 Lambda 和 CloudFormation。

## 启动堆栈
<a name="walkthrough-lambda-backed-custom-resources-createfunction-createstack"></a>

**要创建 堆栈，请执行以下操作：**

1. 从[示例模板](#walkthrough-lambda-backed-custom-resources-sample-template)部分找到您喜欢的模板（YAML 或 JSON），并将其保存到您的计算机，名称为 `samplelambdabackedcustomresource.template`。

1. 通过以下网址打开 CloudFormation 控制台：[https://console.aws.amazon.com/cloudformation/](https://console.aws.amazon.com/cloudformation/)。

1. 在**堆栈**页面，选择右上角的**创建堆栈**，然后选择**使用新资源（标准）**。

1. 在**先决条件 – 准备模板**中，选择**选择现有模板**。

1. 对于**指定模板**，选择**上传模板文件**，然后选择**选择文件**。

1. 选择您之前保存的 `samplelambdabackedcustomresource.template` 模板文件。

1. 选择**下一步**。

1. 对于**堆栈名称**，键入 **SampleCustomResourceStack**，然后选择**下一步**。

1. 在本演练中，您无需添加标记或指定高级设置，因此请选择 **Next**。

1. 确保堆栈名​​称看起来正确，然后选择**创建**。

CloudFormation 可能需要几分钟的时间创建堆栈。要监控进度，可查看堆栈事件。有关更多信息，请参阅 [从 CloudFormation 控制台查看堆栈信息](cfn-console-view-stack-data-resources.md)。

如果堆栈创建成功，则堆栈中的所有资源（例如 Lambda 函数和自定义资源）都已创建。您已成功使用 Lambda 函数和自定义资源。

如果 Lambda 函数返回错误，则在 CloudWatch Logs [控制台](https://console.aws.amazon.com/cloudwatch/home#logs:)中查看此函数的日志。日志流的名称与自定义资源的物理 ID 相同，您可通过查看堆栈的资源来查找该名称。有关更多信息，请参阅《Amazon CloudWatch 用户指南》中的[查看日志数据](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html#ViewingLogData)**。

## 清理资源
<a name="walkthrough-lambda-backed-custom-resources-createfunction-cleanup"></a>

删除堆栈以清理您创建的所有堆栈资源，从而避免为不必要的资源付费。

**删除堆栈**

1. 从 CloudFormation 控制台中，选择 **SampleCustomResourceStack** 堆栈。

1. 选择 **Actions**，然后选择 **Delete Stack**。

1. 在确认消息中，选择 **Yes, Delete**。

您创建的所有资源都会被删除。

既然您已了解如何创建和使用 Lambda 支持的自定义资源，可以使用本演练中的示例模板和代码来生成和试验其他堆栈和函数。

## 相关信息
<a name="w2aac11c45b9c24b9c23"></a>
+ [CloudFormation 自定义资源参考](crpg-ref.md)
+ [AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html)

# `cfn-response` 模块
<a name="cfn-lambda-function-code-cfnresponsemodule"></a>

在 CloudFormation 模板中，您可以指定 Lambda 函数作为自定义资源的对象。如果使用 `ZipFile` 属性指定[函数](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html)的源代码，可以加载 `cfn-response` 模块，以将 Lambda 函数的响应发送到自定义资源。`cfn-response` 模块是一个库，可简化向调用 Lambda 函数的自定义资源发送响应的过程。该模块包含一个 `send` 方法，可以通过 Amazon S3 预签名 URL (`ResponseURL`) 将[响应对象](crpg-ref.md#crpg-ref-responses)发送到自定义资源。

只有当您使用 `cfn-response` 属性编写您的源代码时，`ZipFile` 模块可用。它对于存储在 Amazon S3 存储桶中的源代码不可用。对于 存储桶中的代码，您必须编写自己的函数来发送响应。

**注意**  
在执行 `send` 方法后，Lambda 函数将终止，因此，将忽略在该方法之后写入的任何内容。

## 加载 `cfn-response` 模块
<a name="cfn-lambda-function-code-cfnresponsemodule-loading"></a>

对于 Node.js 函数，使用 `require()` 函数来加载 `cfn-response` 模块。例如，以下代码示例创建一个名为 `cfn-response` 的 `response` 对象：

```
var response = require('cfn-response');
```

对于 Python，使用 `import` 语句来加载 `cfnresponse` 模块，如下例所示：

**注意**  
使用这个确切的导入语句。如果使用导入语句的其他变体，CloudFormation 将不会包含响应模块。

```
import cfnresponse
```

## `send` 方法参数
<a name="cfn-lambda-function-code-cfnresponsemodule-send-parameters"></a>

您可以将以下参数用于 `send` 方法。

`event`  
[自定义资源请求](crpg-ref.md#crpg-ref-requesttypes)中的字段。

`context`  
Lambda 函数特定的对象，可用于指定何时函数和任何回调完成执行，或从 Lambda 执行环境中访问信息。有关更多信息，请参阅《AWS Lambda 开发人员指南》**中的[使用 Node.js 构建 Lambda 函数](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html)。

`responseStatus`  
函数是否成功完成。使用 `cfnresponse` 模块常量指定状态：`SUCCESS` 表示执行成功；`FAILED` 表示执行失败。

`responseData`  
自定义资源[响应对象](crpg-ref.md#crpg-ref-responses)的 `Data` 字段。数据为名称-值对列表。

`physicalResourceId`  
可选。调用此函数的自定义资源的唯一标识符。默认情况下，该模块使用与 Lambda 函数关联的 Amazon CloudWatch Logs 日志流的名称。  
为 `PhysicalResourceId` 返回的值可以更改自定义资源更新操作。如果返回的值相同，则将其视为正常更新。如果返回的值不同，CloudFormation 会将该更新视为替换，并向旧资源发送删除请求。有关更多信息，请参阅 [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cloudformation-customresource.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cloudformation-customresource.html)。

`noEcho`  
可选。指示在使用 `Fn::GetAtt` 函数检索自定义资源的输出时是否遮蔽它。如果设置为 `true`，则所有返回的值都将用星号 (\$1\$1\$1\$1\$1) 遮蔽，但存储在下面指定位置的信息除外。默认情况下，此值为 `false`。  
使用 `NoEcho` 属性不会遮蔽在以下各区段中存储的任何信息：  
+ `Metadata` 模板区段。CloudFormation 不会转换、修改或编辑您在 `Metadata` 部分中包含的任何信息。有关更多信息，请参阅 [Metadata](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html)。
+ `Outputs` 模板区段。有关更多信息，请参阅 [Outputs](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html)。
+ 资源定义的 `Metadata` 属性。有关更多信息，请参阅 [`Metadata` 属性](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-attribute-metadata.html)。
强烈建议您不要使用这些机制来包含敏感信息，例如密码。
有关使用 `NoEcho` 遮蔽敏感信息的更多信息，请参阅 [请勿将凭证嵌入您的模板](security-best-practices.md#creds) 最佳实践。

## 示例
<a name="cfn-lambda-function-code-cfnresponsemodule-examples"></a>

### Node.js
<a name="cfn-lambda-function-code-zipfile-examplenodejs"></a>

在下面的 Node.js 示例中，内联 Lambda 函数获取输入值并将其乘以 5。内联函数对于较小的函数尤为有用，因为它们让您能够在模板中直接指定源代码，而不必先创建包再将包上传到 Amazon S3 存储桶中。函数使用 `cfn-response` `send` 方法将结果发回给调用此函数的自定义资源。

#### JSON
<a name="cfn-lambda-function-code-zipfile-examplenodejs.json"></a>

```
"ZipFile": { "Fn::Join": ["", [
  "var response = require('cfn-response');",
  "exports.handler = function(event, context) {",
  "  var input = parseInt(event.ResourceProperties.Input);",
  "  var responseData = {Value: input * 5};",
  "  response.send(event, context, response.SUCCESS, responseData);",
  "};"
]]}
```

#### YAML
<a name="cfn-lambda-function-code-zipfile-examplenodejs-yaml"></a>

```
ZipFile: >
  var response = require('cfn-response');
  exports.handler = function(event, context) {
    var input = parseInt(event.ResourceProperties.Input);
    var responseData = {Value: input * 5};
    response.send(event, context, response.SUCCESS, responseData);
  };
```

### Python
<a name="cfn-lambda-function-code-zipfile-examplepython"></a>

在下面的 Python 示例中，内联 Lambda 函数采用一个整数值并将它乘以 5。

#### JSON
<a name="cfn-lambda-function-code-zipfile-examplepython.json"></a>

```
"ZipFile" : { "Fn::Join" : ["\n", [
  "import json",
  "import cfnresponse",
  "def handler(event, context):",
  "   responseValue = int(event['ResourceProperties']['Input']) * 5",
  "   responseData = {}",
  "   responseData['Data'] = responseValue",
  "   cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, \"CustomResourcePhysicalID\")"
]]}
```

#### YAML
<a name="cfn-lambda-function-code-zipfile-examplepython.yaml"></a>

```
ZipFile: |
  import json
  import cfnresponse
  def handler(event, context):
    responseValue = int(event['ResourceProperties']['Input']) * 5
    responseData = {}
    responseData['Data'] = responseValue
    cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
```

## 模块源代码
<a name="cfn-lambda-function-code-cfnresponsemodule-source"></a>

**Topics**
+ [异步 Node.js 源代码](#cfn-lambda-function-code-cfnresponsemodule-source-nodejs-async)
+ [Node.js 源代码](#cfn-lambda-function-code-cfnresponsemodule-source-nodejs)
+ [Python 源代码](#cfn-lambda-function-code-cfnresponsemodule-source-python)

### 异步 Node.js 源代码
<a name="cfn-lambda-function-code-cfnresponsemodule-source-nodejs-async"></a>

以下是在处理程序异步进行处理时，Node.js 函数的响应模块源代码。请查看这些内容，以理解此模块的功能并帮助您实现自己的响应函数。

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, noEcho) {

    return new Promise((resolve, reject) => {
        var responseBody = JSON.stringify({
            Status: responseStatus,
            Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
            PhysicalResourceId: physicalResourceId || context.logStreamName,
            StackId: event.StackId,
            RequestId: event.RequestId,
            LogicalResourceId: event.LogicalResourceId,
            NoEcho: noEcho || false,
            Data: responseData
        });

        console.log("Response body:\n", responseBody);

        var https = require("https");
        var url = require("url");

        var parsedUrl = url.parse(event.ResponseURL);
        var options = {
            hostname: parsedUrl.hostname,
            port: 443,
            path: parsedUrl.path,
            method: "PUT",
            headers: {
                "content-type": "",
                "content-length": responseBody.length
            }
        };

        var request = https.request(options, function(response) {
            console.log("Status code: " + parseInt(response.statusCode));
            resolve(context.done());
        });

        request.on("error", function(error) {
            console.log("send(..) failed executing https.request(..): " + maskCredentialsAndSignature(error));
            reject(context.done(error));
        });

        request.write(responseBody);
        request.end();
    })
}
 
function maskCredentialsAndSignature(message) {
    return message.replace(/X-Amz-Credential=[^&\s]+/i, 'X-Amz-Credential=*****')
        .replace(/X-Amz-Signature=[^&\s]+/i, 'X-Amz-Signature=*****');
}
```

### Node.js 源代码
<a name="cfn-lambda-function-code-cfnresponsemodule-source-nodejs"></a>

以下是在处理程序不是异步进行处理时，Node.js 函数的响应模块源代码。请查看这些内容，以理解此模块的功能并帮助您实现自己的响应函数。

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
 
exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, noEcho) {

    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: physicalResourceId || context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        NoEcho: noEcho || false,
        Data: responseData
    });

    console.log("Response body:\n", responseBody);

    var https = require("https");
    var url = require("url");

    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };

    var request = https.request(options, function(response) {
        console.log("Status code: " + parseInt(response.statusCode));
        context.done();
    });

    request.on("error", function(error) {
        console.log("send(..) failed executing https.request(..): " + maskCredentialsAndSignature(error));
        context.done();
    });

    request.write(responseBody);
    request.end();
}
```

### Python 源代码
<a name="cfn-lambda-function-code-cfnresponsemodule-source-python"></a>

以下是 Python 函数的响应模块源代码。

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
 
from __future__ import print_function
import urllib3
import json
import re

SUCCESS = "SUCCESS"
FAILED = "FAILED"

http = urllib3.PoolManager()


def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None):
    responseUrl = event['ResponseURL']

    responseBody = {
        'Status' : responseStatus,
        'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name),
        'PhysicalResourceId' : physicalResourceId or context.log_stream_name,
        'StackId' : event['StackId'],
        'RequestId' : event['RequestId'],
        'LogicalResourceId' : event['LogicalResourceId'],
        'NoEcho' : noEcho,
        'Data' : responseData
    }

    json_responseBody = json.dumps(responseBody)

    print("Response body:")
    print(json_responseBody)

    headers = {
        'content-type' : '',
        'content-length' : str(len(json_responseBody))
    }

    try:
        response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
        print("Status code:", response.status)


    except Exception as e:

        print("send(..) failed executing http.request(..):", mask_credentials_and_signature(e))
 
 
def mask_credentials_and_signature(message):
    message = re.sub(r'X-Amz-Credential=[^&\s]+', 'X-Amz-Credential=*****', message, flags=re.IGNORECASE)
    return re.sub(r'X-Amz-Signature=[^&\s]+', 'X-Amz-Signature=*****', message, flags=re.IGNORECASE)
```