Módulo cfn-response - AWS CloudFormation

Módulo cfn-response

No modelo do CloudFormation, é possível especificar uma função do Lambda como o destino de um recurso personalizado. Ao usar a propriedade ZipFile para especificar o código-fonte da função, você pode carregar o módulo cfn-response com a finalidade de enviar respostas da função do Lambda para um recurso personalizado. O módulo cfn-response corresponde a uma biblioteca que simplifica o envio de respostas para o recurso personalizado que invocou a função do Lambda. O módulo contém um método send que envia um objeto de resposta para um recurso personalizado por meio de um URL do Amazon S3 assinado previamente (o ResponseURL).

O módulo cfn-response está disponível somente quando você usa a propriedade ZipFile para gravar o código-fonte. Ele não está disponível para o código-fonte armazenado nos buckets do Amazon S3. Para código em buckets do , você deve criar suas próprias funções para enviar respostas.

nota

Depois de executar o método send a função do Lambda é encerrada, assim qualquer coisa que você grave após esse método é ignorada.

Carregar o módulo cfn-response

Para funções Node.js, use a função require() para carregar o módulo cfn-response. Para esclarecer, o código de exemplo a seguir cria um objeto cfn-response com o nome response:

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

Para Python, use a instrução import para carregar o módulo cfnresponse, conforme mostrado no seguinte exemplo:

nota

Use essa exata declaração de importação. Se você usar outras variantes da instrução “import”, o CloudFormation não incluirá o módulo de resposta.

import cfnresponse

Parâmetros do método send

Você pode usar os seguintes parâmetros com o método send.

event

Os campos em uma solicitação de recurso personalizado.

context

Um objeto, específico às funções do Lambda que você pode usar para especificar quando a função e qualquer retorno de chamada tiverem concluído a execução ou para acessar informações no ambiente de execução do Lambda. Para obter mais informações, consulte Construir funções do Lambda com Node.js no Guia do desenvolvedor do AWS Lambda.

responseStatus

Se a função foi concluída com êxito. Use as constantes do módulo cfnresponse para especificar o status: SUCCESS para execuções bem-sucedidas e FAILED para execuções com falha.

responseData

O campo Data de um objeto de resposta de um recurso personalizado. Os dados são uma lista de pares de nome-valor.

physicalResourceId

Opcional. O identificador exclusivo do recurso personalizado que invocou a função. Por padrão, o módulo usa o nome do fluxo de log do Amazon CloudWatch Logs que está associado à função do Lambda.

O valor retornado para um PhysicalResourceId pode alterar as operações de atualização de recursos personalizados. Se o valor retornado for o mesmo, a atualização é considerada normal. Se o valor retornado for diferente, o CloudFormation reconhecerá a atualização como uma substituição e enviará uma solicitação de exclusão para o recurso antigo. Para obter mais informações, consulte AWS::CloudFormation::CustomResource.

noEcho

Opcional. Indica se é necessário mascarar a saída do recurso personalizado quando ela for recuperada usando a função Fn::GetAtt. Se definido como true, todos os valores retornados serão mascarados com asteriscos (*****), exceto informações armazenadas nos locais especificados abaixo. Por padrão, esse valor é false.

Importante

O uso do atributo NoEcho não mascara informações armazenadas no seguinte:

  • A seção de modelo de Metadata. O CloudFormation não transforma, modifica nem edita nenhuma informação incluída na seção Metadata. Para obter mais informações, consulte Metadados.

  • A seção de modelo de Outputs. Para obter mais informações, consulte Saídas.

  • O atributo Metadata de uma definição de recurso. Para obter mais informações, consulte o atributo Metadata.

É altamente recomendável não usar esses mecanismos para incluir informações confidenciais, como senhas ou segredos.

Para obter mais informações sobre como usar NoEcho para mascarar informações confidenciais, consulte a prática recomendada Não incorporar credenciais em seus modelos.

Exemplos

Node.js

No exemplo Node.js a seguir, a função do Lambda incorporada obtém um valor de entrada e multiplica por 5. As funções em linha são especialmente úteis para funções menores, pois permitem que você especifique o código-fonte diretamente no modelo, em vez de criar um pacote e carregá-lo em um bucket do Amazon S3. A função usa o método cfn-response send para enviar o resultado de volta para o recurso personalizado que a chamou.

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

No exemplo de Python a seguir, a função do Lambda em linha pega um valor inteiro e o multiplica por 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")

Código-fonte do módulo

Código-fonte assíncrono do Node.js

Veja a seguir o código-fonte do módulo de resposta das funções do Node.js caso o handler seja assíncrono. Analise-o para entender o que o módulo faz e obter ajuda com a implementação de suas próprias funções de resposta.

// 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=*****'); }

Código fonte do Node.js

Veja a seguir o código-fonte do módulo de resposta das funções do Node.js caso o handler não seja assíncrono. Analise-o para entender o que o módulo faz e obter ajuda com a implementação de suas próprias funções de resposta.

// 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(); }

Código-fonte Python

Veja a seguir o código-fonte do módulo de resposta das funções do 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)