

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

# 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 IDs，但現在建議使用 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 Resource Name (ARN)。  
由於使用 Python 原始程式碼，因此 `Handler` 屬性設定為 `index.handler`。如需有關使用內嵌函數原始程式碼時可接受處理常式識別碼的詳細資訊，請參閱 [ AWS::Lambda::Function Code](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`，使用 Lambda 函式的 ARN 與其建立關聯。根據 `ServiceTimeout` 中的設定，它會為建立和更新操作實作 60 秒的等待時間。僅當屬性被修改時，才會在更新操作中調用此資源。

### 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>

您必須具備使用所有對應服務 (如 Lambda 和 CloudFormation) 的 IAM 許可。

## 啟動堆疊
<a name="walkthrough-lambda-backed-custom-resources-createfunction-createstack"></a>

**建立堆疊**

1. 從 [範例範本](#walkthrough-lambda-backed-custom-resources-sample-template) 區段尋找您偏好設定的範本 (YAML 或 JSON)，並將其儲存到名為 `samplelambdabackedcustomresource.template` 的機器。

1. 開啟位在 [https://console.aws.amazon.com/cloudformation/](https://console.aws.amazon.com/cloudformation/) 的 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` 模組，如下列範例所示：

**注意**  
使用此確切 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)
```