У меня была похожая проблема сегодня при использовании пакета cfn-response, на котором, по-видимому, основан ваш код. Пакет cfn-response основан на обратном вызове, но ваш код также частично использует async / await (опция с Runtime: node.js8.10).
В вашем случае я подозреваю, что вы никогда не видели сообщений "STATUS:" или "HEADERS:", даже если тело ответа было выгружено в журналы (синхронно). Это отражает мой опыт при использовании основанного на обратном вызове cfn-response, смешанного с async / await.
Другими словами, при всех обстоятельствах вам нужно будет убедиться, что вы отправляете ответ на Cloudformation (PUT на событие S3 ResponseURL) до того, как ваша Lambda завершит свою работу, или шаблон может зависнуть до часа перед отказом и откатом (вероятно, с ошибкой Cloudformation по линии «Не удалось стабилизировать ресурс ...». Откат (удаление), в свою очередь, также может занять час, потому что удаление также не отвечает должным образом. Чуть больше информация здесь .
В итоге я реализовал пользовательские ресурсы, очень похожие на в этом примере на GitHub от https://github.com/rosberglinhares (лицензия MIT), с несколькими отличиями; Я не настроил отдельную лямбду для обработки функциональности sendResponse и создал пользовательские ресурсы без сервера (используя пакет облачной информации aws и команды развертывания облачной информации aws).
Ваш ApiMethodCustom не определен, поэтому мне сложно представить вам эту реализацию, и поэтому я включаю свой код node.js8.10, используя async / await для справки.
Сначала пользовательский ресурс в шаблоне Cloudformation:
---
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: CustomResource Example Stack
Resources:
CustomResource:
Type: 'AWS::Serverless::Function'
Properties:
Runtime: nodejs8.10
Handler: index.handler
MemorySize: 128
Timeout: 15
Role: !GetAtt CustomResourceRole.Arn
CodeUri: ./CustomResource/
CustomResourceUser:
Type: 'Custom::CustomResourceUser'
Properties:
ServiceToken: !GetAtt CustomResource.Arn
...
Обратите внимание, что CodeUri относится к пути шаблона. Вам нужно будет определить роль и политики IAM для CustomResourceRole.
Теперь для лямбды CustomResource / index.js (вам также нужно запустить «npm install --save axios» в каталоге CustomResource):
'use strict';
const AWS = require('aws-sdk');
const axios = require('axios');
exports.handler = async (event, context) => {
try {
switch (event.RequestType) {
case 'Create':
await ApiMethodCustom.create(...);
break;
case 'Update':
await ApiMethodCustom.update(...);
break;
case 'Delete':
await ApiMethodCustom.delete(...);
break;
}
console.info('Success for request type ${event.RequestType}');
await sendResponse(event, context, 'SUCCESS', { } );
} catch (error) {
console.error('Error for request type ${event.RequestType}: ', error);
await sendResponse(event, context, 'FAILED', { } );
}
}
async function sendResponse (event, context, responseStatus, responseData, physicalResourceId) {
var reason = responseStatus == 'FAILED' ? ('See the details in CloudWatch Log Stream: ' + context.logStreamName) : undefined;
var responseBody = JSON.stringify({
StackId: event.StackId,
RequestId: event.RequestId,
Status: responseStatus,
Reason: reason,
PhysicalResourceId: physicalResourceId || context.logStreamName,
LogicalResourceId: event.LogicalResourceId,
Data: responseData
});
var responseOptions = {
headers: {
'Content-Type': '',
'Content-Length': responseBody.length
}
};
console.info('Response body:\n', responseBody);
try {
await axios.put(event.ResponseURL, responseBody, responseOptions);
console.info('CloudFormationSendResponse Success');
} catch (error) {
console.error('CloudFormationSendResponse Error:');
if (error.response) {
console.error(error.response.data);
console.error(error.response.status);
console.error(error.response.headers);
} else if (error.request) {
console.error(error.request);
} else {
console.error('Error', error.message);
}
console.error(error.config);
throw new Error('Could not send CloudFormation response');
}
}
Для получения дополнительной информации об использовании обратного вызова и асинхронного с AWS Lambda смотрите здесь .
Наконец, обратите внимание на использование Axios . Он основан на обещаниях и поэтому поддерживает ожидание вместо обратных вызовов.