使用CloudFormation子堆栈时`package`命令未压缩Lambda代码的问题
问题场景
单独使用aws cloudformation package处理子堆栈模板(auth/auth.yml)时,Lambda函数可正常部署:
# Filename: auth/auth.yml # Lambda JS file: auth/lambda-pre-signup.js Resources: ## Other resources here MyPreSignupLambda: Type: AWS::Lambda::Function Properties: Architectures: - arm64 Code: 'lambda-pre-signup.js' Handler: 'lambda-pre-signup.handler' Runtime: nodejs16.x PackageType: Zip Role: !GetAtt MyRole.Arn
执行命令:
aws cloudformation package --template-file auth.yml --s3-bucket my-bucket --output-template-file generated-auth.yml aws cloudformation deploy --template-file generated-auth.yml --stack-name test-stack --capabilities CAPABILITY_IAM
但通过根堆栈(root.yml)引用该子堆栈时,部署报错:
Resource handler returned message: "Could not unzip uploaded file. Please check your file, then try to upload again. (Service: Lambda, Status Code: 400, Request ID: xxxxx)"
查看S3桶发现,Lambda源代码已上传但未被压缩,根模板内容:
# Filename: root.yml Resources: MyAuth: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./auth/auth.yml
执行命令:
aws cloudformation package --template-file root.yml --s3-bucket my-bucket --output-template-file generated-root.yml aws cloudformation deploy --template-file generated-root.yml --stack-name test-root-stack --capabilities CAPABILITY_IAM
问题原因
aws cloudformation package默认仅处理当前模板中的资源,不会递归解析嵌套堆栈(AWS::CloudFormation::Stack类型资源)内的模板内容。处理根模板时,命令仅将子模板auth/auth.yml上传到S3,不会处理子模板里的Lambda代码路径,导致部署时直接上传了未压缩的单个JS文件,而Lambda要求PackageType: Zip对应压缩包格式,因此报错。
解决方法
方法1:先打包子堆栈,再在根模板中引用打包后的子模板
- 先单独打包子堆栈,生成处理后的子模板:
aws cloudformation package --template-file auth/auth.yml --s3-bucket my-bucket --output-template-file auth/generated-auth.yml
- 修改根模板,指向打包后的子模板:
# Filename: root.yml Resources: MyAuth: Type: AWS::CloudFormation::Stack Properties: TemplateURL: ./auth/generated-auth.yml
- 打包根模板并部署:
aws cloudformation package --template-file root.yml --s3-bucket my-bucket --output-template-file generated-root.yml aws cloudformation deploy --template-file generated-root.yml --stack-name test-root-stack --capabilities CAPABILITY_IAM
方法2:使用--include参数递归处理子模板资源
处理根模板时,通过--include指定子模板所在目录,让package命令递归处理其中的Lambda资源:
aws cloudformation package --template-file root.yml --s3-bucket my-bucket --output-template-file generated-root.yml --include "auth/*"
该命令会扫描auth目录下的所有文件,处理子模板中的Lambda代码路径,生成正确的压缩包上传到S3。
补充说明
aws cloudformation package没有专门的"强制压缩"选项,关键是确保命令能识别并处理Lambda资源的本地代码路径。对于嵌套堆栈场景,要么预先处理子堆栈,要么通过--include参数让命令递归解析子模板内容,才能保证Lambda代码被正确压缩上传。
内容的提问来源于stack exchange,提问作者Gasim

