问题描述
我的目标是将每次上传到存储桶的图像时调用的 lambda 代码打包到 CloudFormation 模板中。到目前为止,我实现了创建新资源并从头开始触发,但我有现有的存储桶,我需要向其中添加触发器并在两种情况下出现错误:
- 当我将 lambda 的创建和触发器配置放在一个模板中并尝试将堆栈创建为新资源时 - 它表示该存储桶已存在
- 当我将触发器移动到新文件时 - 首先像 1. 一样创建新资源,然后将现有资源导入堆栈 - 我得到:
创建此更改集时出错
您在模板中修改了未导入的资源 [ScaleImages,ScaleImagesRole]。导入操作期间无法执行更新、创建或删除操作。
我的模板看起来像:
- lambda 创建 - 新 lambda 和角色 - 使用新资源创建堆栈
# A tibble: 4 x 2
# id score
# <int> <int>
#1 2 10
#2 2 85
#3 3 80
#4 3 100
- 添加触发器 - 存储桶存在 - 导入资源
example_df <- structure(list(id = c(1L,1L,2L,3L,3L),score = c(50L,90L,10L,85L,80L,100L)),class = "data.frame",row.names = c("1:","2:","3:","4:","5:","6:"))
在最后一个我也尝试了 {
"AWstemplateFormatVersion": "2010-09-09","Resources": {
"ScaleImages": {
"Type": "AWS::Lambda::Function","DeletionPolicy": "Retain","Properties": {
"FunctionName": "ScaleImages","Handler": "index.handler","Role": {
"Fn::GetAtt": [
"ScaleImagesRole","Arn"
]
},"Code": {
"S3Bucket": "example-test","S3Key": "example-resize.zip"
},"Runtime": "nodejs12.x","MemorySize": 1024,"Timeout": 300
}
},"ScaleImagesRole": {
"Type": "AWS::IAM::Role","Properties": {
"RoleName": "ScaleImagesRole","AssumeRolePolicyDocument": {
"Version": "2012-10-17","Statement": [
{
"Effect": "Allow","Principal": {
"Service": [
"lambda.amazonaws.com"
]
},"Action": [
"sts:AssumeRole"
]
}
]
},"Path": "/","Policies": [
{
"PolicyName": "AWSLambdaBasicExecutionRole","PolicyDocument": {
"Version": "2012-10-17","Statement": [
{
"Effect": "Allow","Action": [
"logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"
],"Resource": "*"
}
]
}
},{
"PolicyName": "AmazonS3FullAccess","Action": "s3:*","Resource": [
"arn:aws:s3:::example-test","arn:aws:s3:::example-test/*","arn:aws:s3:::example-test-output","arn:aws:s3:::example-test-output/*"
]
}
]
}
}
]
}
}
}
}
,但在这两种情况下我都有同样的错误:
修改模板中的资源 [ScaleImages,ScaleImagesRole]
有人能解释一下我做错了什么吗?
解决方法
你必须在阶段:
1.创建新堆栈
尚无存储桶,只需与您缺少的函数和 lambda 权限 叠加即可。
{
"AWSTemplateFormatVersion": "2010-09-09","Resources": {
"ScaleImages": {
"Type": "AWS::Lambda::Function","Properties": {
"FunctionName": "ScaleImages","Handler": "index.handler","Role": {
"Fn::GetAtt": [
"ScaleImagesRole","Arn"
]
},"Code": {
"S3Bucket": "example-test","S3Key": "example-resize.zip"
},"Runtime": "nodejs12.x","MemorySize": 1024,"Timeout": 300
}
},"ScaleImagesRole": {
"Type": "AWS::IAM::Role","Properties": {
"RoleName": "ScaleImagesRole","AssumeRolePolicyDocument": {
"Version": "2012-10-17","Statement": [
{
"Effect": "Allow","Principal": {
"Service": [
"lambda.amazonaws.com"
]
},"Action": [
"sts:AssumeRole"
]
}
]
},"Path": "/","Policies": [
{
"PolicyName": "AWSLambdaBasicExecutionRole","PolicyDocument": {
"Version": "2012-10-17","Statement": [
{
"Effect": "Allow","Action": [
"logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"
],"Resource": [
"arn:aws:s3:::example-test","arn:aws:s3:::example-test/*","arn:aws:s3:::example-test-output","arn:aws:s3:::example-test-output/*"
]
}
]
}
},{
"PolicyName": "AmazonS3FullAccess","Action": "s3:*","Resource": "*"
}
]
}
}
]
}
},"s3Permission": {
"Type": "AWS::Lambda::Permission","Properties": {
"FunctionName": {
"Fn::GetAtt": [
"ScaleImages","Arn"
]
},"Action": "lambda:InvokeFunction","Principal": "s3.amazonaws.com","SourceAccount": {
"Ref": "AWS::AccountId"
}
}
}
}
}
2.将存储桶导入现有堆栈
使用 Import resources into stack
选项并使用此模板上传堆栈。它添加了存储桶,但还没有通知
{
"AWSTemplateFormatVersion": "2010-09-09","SourceAccount": {
"Ref": "AWS::AccountId"
}
}
},"PutOriginalImage": {
"Type": "AWS::S3::Bucket","DeletionPolicy": "Retain","Properties": {
"BucketName": "example-test"
}
}
}
}
3.更新堆栈
通过向存储桶添加通知来更新堆栈。使用以下模板:
{
"AWSTemplateFormatVersion": "2010-09-09","Properties": {
"BucketName": "example-test","NotificationConfiguration": {
"LambdaConfigurations": [
{
"Event": "s3:ObjectCreated:Put","Filter": {
"S3Key": {
"Rules": [
{
"Name": "prefix","Value": "original2/"
}
]
}
},"Function": {
"Fn::GetAtt": [
"ScaleImages","Arn"
]
}
}
]
}
}
}
}
}
,
一种略有不同的方法,让您无需遵循 3 个步骤即可一次性完成。在 Cloudformation 中导入现有资源时,我经历了一些艰难时期,我将通过自定义资源处理 lambda 中的复杂性
s3 = boto3.resource('s3')
def lambda_handler(event,context):
print("Received event: " + json.dumps(event,indent=2))
responseData={}
try:
if event['RequestType'] == 'Delete':
print("Request Type:",event['RequestType'])
Bucket=event['ResourceProperties']['Bucket']
delete_notification(Bucket)
print("Sending response to custom resource after Delete")
elif event['RequestType'] == 'Create' or event['RequestType'] == 'Update':
print("Request Type:",event['RequestType'])
LambdaArn=event['ResourceProperties']['LambdaArn']
Bucket=event['ResourceProperties']['Bucket']
add_notification(LambdaArn,Bucket)
responseData={'Bucket':Bucket}
print("Sending response to custom resource")
responseStatus = 'SUCCESS'
except Exception as e:
print('Failed to process:',e)
responseStatus = 'FAILURE'
responseData = {'Failure': 'Something bad happened.'}
cfnresponse.send(event,context,responseStatus,responseData)
def add_notification(LambdaArn,Bucket):
bucket_notification = s3.BucketNotification(Bucket)
response = bucket_notification.put(
NotificationConfiguration={
'LambdaFunctionConfigurations': [
{
'LambdaFunctionArn': LambdaArn,'Events': [
's3:ObjectCreated:*'
]
}
]
}
)
print("Put request completed....")
def delete_notification(Bucket):
bucket_notification = s3.BucketNotification(Bucket)
response = bucket_notification.put(
NotificationConfiguration={}
)
print("Delete request completed....")
可以找到完整的模板和解决方案here
注意:AWS CloudFormation Repo on github 上已经存在一个未解决的问题。最初来自serverless folks