升级Lambda至Python3.9后CloudFormation自定义资源响应异常排查
问题原因及解决办法
你遇到的错误核心是:Python 3.9及以上版本的AWS Lambda运行时不再默认内置cfn-response模块。
在Python 3.6的Lambda运行时中,AWS默认把cfn-response模块放在了运行环境的搜索路径里,所以你的代码能直接导入并调用它发送响应给CloudFormation。但升级到3.9后,这个模块被移除了,代码执行时会触发ModuleNotFoundError,导致函数中断,根本没机会向CloudFormation发送响应,最终出现“未收到自定义资源响应”的错误提示。
两种可行的修复方案
方案1:手动嵌入cfn-response模块代码
cfn-response本质是一个轻量的Python脚本,你可以直接把官方提供的源码复制到你的Lambda函数代码中,不用依赖内置模块。源码如下:
import json import urllib3 SUCCESS = "SUCCESS" FAILED = "FAILED" http = urllib3.PoolManager() def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False): responseUrl = event['ResponseURL'] responseBody = { 'Status' : responseStatus, 'Reason' : 'See the details in CloudWatch Log Stream: ' + 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) 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(..):", e)
把这段代码放在你的Lambda函数开头,之后就可以像之前一样调用send()方法发送响应了。
方案2:改用boto3直接调用CloudFormation API
放弃依赖cfn-response,直接用boto3调用CloudFormation的send_response接口,这种方式更稳定,也不依赖Lambda运行时的内置模块。示例代码:
import boto3 import json def lambda_handler(event, context): # 构造响应内容 response_data = { # 这里可以放你需要返回给CloudFormation的自定义数据 } cf_client = boto3.client('cloudformation') try: # 发送成功响应 cf_client.send_response( StackName=event['StackId'], RequestId=event['RequestId'], LogicalResourceId=event['LogicalResourceId'], Status='SUCCESS', Reason='资源处理完成', PhysicalResourceId=context.log_stream_name, Data=response_data ) except Exception as e: # 发送失败响应 cf_client.send_response( StackName=event['StackId'], RequestId=event['RequestId'], LogicalResourceId=event['LogicalResourceId'], Status='FAILED', Reason=f'处理失败:{str(e)}', PhysicalResourceId=context.log_stream_name, Data={} )
验证建议
修复后可以先在Lambda控制台测试函数,模拟CloudFormation的自定义资源请求事件,查看函数日志确认是否成功发送响应,再重新部署CloudFormation模板。
内容的提问来源于stack exchange,提问作者L Xandor
相关产品推荐
相关产品推荐

