CloudFormation добавляет триггер для существующей корзины s3

Моя цель — упаковать мой лямбда-код, который вызывается при каждой загрузке изображения в корзину, в шаблон CloudFormation. До сих пор я добился создания новых ресурсов и запуска с нуля, но у меня есть существующая корзина, в которую мне нужно добавить триггер и получить ошибки в 2 случаях:

  1. когда я помещаю создание лямбды и конфигурации триггера в один шаблон и пытаюсь создать стек как новые ресурсы - он говорит, что ведро уже существует
  2. когда я перемещаю триггер в новый файл - и сначала создаю новые ресурсы, как в 1., затем я импортирую существующие ресурсы в стек - я получаю:

При создании этого набора изменений произошла ошибка.

Вы изменили ресурсы [ScaleImages, ScaleImagesRole] в своем шаблоне, которые не импортируются. Операции обновления, создания или удаления не могут выполняться во время операций импорта.

Мои шаблоны выглядят так:

  • создание лямбды - новая лямбда и роль - создать стек с новыми ресурсами
    {
      "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",
          "DeletionPolicy": "Retain",
          "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",
                "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                    {
                      "Effect": "Allow",
                      "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/*"
                      ]
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    }
  • добавление триггера - ведро существует - импорт ресурсов
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "PutOriginalImage": {
      "Type": "AWS::S3::Bucket",
      "DeletionPolicy": "Retain",
      "Properties": {
        "BucketName": "example-test",
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:Put",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "prefix",
                      "Value": "original2/"
                    }
                  ]
                }
              },
              "Function": {
                "Fn::GetAtt": [
                  "ScaleImages",
                  "Arn"
                ]
              }
            }
          ]
        }
      }
    }
  }
}

В последнем я также пробовал "Function": "ScaleImages", но в обоих случаях у меня была одна и та же ошибка:

измененные ресурсы [ScaleImages, ScaleImagesRole] в вашем шаблоне

Может кто-нибудь пролить свет на то, что я делаю неправильно?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
3 018
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Делать это нужно поэтапно:

1. Создайте новый стек

Пока нет ведра, просто сложите с вашей функцией и лямбда-разрешениями, которых вам не хватает.

    {
      "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",
                "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                    {
                      "Effect": "Allow",
                      "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",
      "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",
                "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                    {
                      "Effect": "Allow",
                      "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"
        }
    }
}
,
    "PutOriginalImage": {
      "Type": "AWS::S3::Bucket",
      "DeletionPolicy": "Retain",
      "Properties": {
        "BucketName": "example-test"
      }
    }

      }
    }

3. Обновление стека

Обновите стек, добавив уведомления в корзину. Используйте следующий шаблон:

    {
      "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",
                "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                    {
                      "Effect": "Allow",
                      "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"
        }
    }
},



            "PutOriginalImage": {
      "Type": "AWS::S3::Bucket",
      "DeletionPolicy": "Retain",
      "Properties": {
        "BucketName": "example-test",

        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:Put",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "prefix",
                      "Value": "original2/"
                    }
                  ]
                }
              },
              "Function": {
                "Fn::GetAtt": [
                  "ScaleImages",
                  "Arn"
                ]
              }
            }
          ]
        }


      }
    }



      }
    }

Спасибо! Это очень хорошо объясненное решение, и теперь оно работает как шарм.

Garion S. 22.12.2020 11:22

@ГарионС. Без проблем. Рад, что это работает. Одна мысль, которую я забыл написать, заключается в том, что определение корзины на шаге 2 должно максимально точно соответствовать существующей корзине. Например, если у вас включено управление версиями, определение должно отражать это.

Marcin 22.12.2020 11:28

Немного другой подход, который позволяет вам работать одним выстрелом, не выполняя 3 шага. У меня были трудные времена при импорте существующих ресурсов в Cloudformation, я бы справился со сложностью лямбда-выражения через пользовательский ресурс.

                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....")

Полный шаблон и решение можно найти здесь

Примечание. Уже есть открытая проблема в AWS CloudFormation Repo на github . Изначально исходил от бессерверных людей

Другие вопросы по теме