

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# `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` 模組，如下列範例所示：

**注意**  
使用此確切 import 陳述式。如果您使用 import 陳述式的其他變體，則 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` 區段中包含的任何資訊。若要取得更多資訊，請參閱[中繼資料](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html)。
+ `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)
```