cfn-response 模組 - AWS CloudFormation

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

cfn-response 模組

在 CloudFormation 範本中,您可以將 Lambda 函式指定為自訂資源的目標。使用 ZipFile 屬性來指定函數的原始程式碼時,可以載入 cfn-response 模組,以將回應從 Lambda 函式傳送至自訂資源。cfn-response 模組是一個程式庫,可簡化將回應傳送至調用 Lambda 函式的自訂資源。此模組具有 send 方法,它可透過 Amazon S3 預先簽章的 URL (ResponseURL) 將回應物件傳送給自訂資源。

只有在您使用 cfn-response 屬性撰寫來源碼時,才能使用 ZipFile 模組。這不適用於 Amazon S3 儲存貯體中所存放的來源碼。針對儲存貯體中的程式碼,您必須撰寫自己的函數來傳送回應。

注意

執行 send 方法之後,Lambda 函數即會終止,因此會忽略您在該方法之後撰寫的任何內容。

載入 cfn-response 模組

對於 Node.js 函數,使用 require() 函數來載入 cfn-response 模組。例如,下列程式碼範例會建立名稱為 cfn-responseresponse 物件:

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

對於 Python,使用 import 陳述式載入 cfnresponse 模組,如下列範例所示:

注意

使用此確切 import 陳述式。如果您使用 import 陳述式的其他變體,則 CloudFormation 不會包括回應模組。

import cfnresponse

send 方法參數

您可以搭配使用下列參數與 send 方法。

event

自訂資源請求中的欄位。

context

Lambda 函數特有的物件,可用來指定函數和任何回呼完成執行的時機,或存取 Lambda 執行環境內的資訊。如需詳細資訊,請參閱《AWS Lambda 開發人員指南》中的使用 Node.js 建立 Lambda 函式

responseStatus

函數是否成功完成。使用 cfnresponse 模組常數指定狀態:SUCCESS 表示執行成功,而 FAILED 表示執行失敗。

responseData

自訂資源回應物件Data 欄位。資料是名稱/值組清單。

physicalResourceId

選用。已呼叫函數之自訂資源的唯一識別碼。根據預設,模組會使用與 Lambda 函數建立關聯之 Amazon CloudWatch Logs 日誌串流的名稱。

傳回的 PhysicalResourceId 值可以變更自訂資源更新操作。如果傳回的值相同,則視為正常更新。若傳回的值不同,CloudFormation 會將該更新視為取代,並傳送刪除請求給舊資源。如需詳細資訊,請參閱AWS::CloudFormation::CustomResource

noEcho

選用。指示在使用 Fn::GetAtt 函數擷取自訂資源時是否要遮蔽其輸出。如果設定為 true,則所有傳回的值都會以星號 (*****) 遮罩,但儲存在下列指定位置的資訊除外。根據預設,此值為 false

重要

使用 NoEcho 屬性不會遮罩任何儲存在下列資訊中的資訊:

  • Metadata 範本區段。CloudFormation 不會轉換、修改或標記您在 Metadata 區段中包含的任何資訊。若要取得更多資訊,請參閱中繼資料

  • Outputs 範本區段。如需詳細資訊,請參閱輸出

  • 資源定義的 Metadata 屬性。如需詳細資訊,請參閱 Metadata 屬性

我們強烈建議您不要使用這些機制來包含敏感資訊,例如密碼或秘密。

如需使用 NoEcho 遮罩敏感資訊的詳細資訊,請參閱 請勿在您的範本中內嵌憑證 最佳實務。

範例

Node.js

在下列 Node.js 範例中,內嵌 Lambda 函數採用輸入值,並將它乘以 5。內嵌函數特別適用於較小的函數,因為它們可讓您直接在範本中指定來源碼,而不是建立套件並將它上傳至 Amazon S3 儲存貯體。此函數使用 cfn-response send 方法,將結果送回給呼叫它的自訂資源。

JSON

"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

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

在下列 Python 範例中,內嵌 Lambda 函數採用整數值,並將它乘以 5。

JSON

"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

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")

模組來源碼

非同步 Node.js 原始程式碼

如果處理常式為異步,以下是 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 原始程式碼

如果處理常式不是異步,以下是 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 原始碼

以下是 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)