Home > Back-end >  S3 NotificationConfiguration - unable to validate destination configuration
S3 NotificationConfiguration - unable to validate destination configuration

Time:10-06

I'm trying to setup an S3 bucket which notifies a Lambda function when a new object is created.

The stack below works fine but I want to added the SourceArn to the Lambda permission, following best practices.

There's some literature on this which suggests the way to do it is via a string rather than Fn::GetAtt/Arn -

https://aws.amazon.com/premiumsupport/knowledge-center/unable-validate-circular-dependency-cloudformation/

But if I uncomment the relevant SourceArn line and redeploy, I get -

Unable to validate the following destination configurations (Service: Amazon S3; Status Code: 400; Error Code: InvalidArgument; Request ID: TBZDEVZ1HVD9EN0Q; S3 Extended Request ID: gL3CRz6UayvHup5i5oC4 /RMm0p1oRaRrPVtfZrykeAaJ1BVuhNSKkqxQ8TL5sy749d9PtbMOEQ=; Proxy: null) 

Ho hum. Looking at the article again, I see that MyBucket needs to depend on MyBucketFunctionPermission - but if I uncomment that and redeploy I now get -

An error occurred (ValidationError) when calling the CreateChangeSet operation: Circular dependency between resources: [MyBucketFunctionPermission, MyBucket]

This is some fresh circle of hell. Am I missing something from the article or is there some other combination of SourceArn format DependsOn that would get this to work ?

TIA.

AWSTemplateFormatVersion: '2010-09-09'
Outputs: {}
Parameters: {}
Resources:
  MyBucket:
    #  DependsOn:
    #  - MyBucketFunctionPermission
    Properties:
      NotificationConfiguration:
        LambdaConfigurations:
        - Event: s3:ObjectCreated:*
          Function:
            Fn::GetAtt:
            - MyBucketFunction
            - Arn
    Type: AWS::S3::Bucket
  MyBucketFunction:
    Properties:
      Code:
        ZipFile: "def handler(event, context):\n  print (event)"
      Handler: index.handler
      Role:
        Fn::GetAtt:
        - MyBucketRole
        - Arn
      Runtime: "python3.8"
    Type: AWS::Lambda::Function
  MyBucketFunctionPermission:
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: MyBucketFunction
      Principal: s3.amazonaws.com
      #  SourceArn:
      #    Fn::Sub: arn:aws:s3:::${MyBucket}
    Type: AWS::Lambda::Permission
  MyBucketRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: '2012-10-17'
      Policies:
      - PolicyDocument:
          Statement:
          - Action: logs:*
            Effect: Allow
            Resource: '*'
          Version: '2012-10-17'
        PolicyName:
          Fn::Sub: my-bucket-role-policy-1234567890
    Type: AWS::IAM::Role

CodePudding user response:

There is nothing seemingly wrong with your template. It works fine, at least for me. But a race condition is possible between MyBucket and MyBucketFunctionPermission. Thus, protect against this, you have to use DependsOn. But for that to work you have to explicitly set your bucket name. For example:

AWSTemplateFormatVersion: '2010-09-09'
Outputs: {}
Parameters: {}
Resources:
  MyBucket:
    DependsOn:
     - MyBucketFunctionPermission
    Properties:
      BucketName: !Sub "my-bucket-323323-${AWS::StackName}-${AWS::Region}"
      NotificationConfiguration:
        LambdaConfigurations:
        - Event: s3:ObjectCreated:*
          Function:
            Fn::GetAtt:
            - MyBucketFunction
            - Arn
    Type: AWS::S3::Bucket
  MyBucketFunction:
    Properties:
      Code:
        ZipFile: "def handler(event, context):\n  print (event)"
      Handler: index.handler
      Role:
        Fn::GetAtt:
        - MyBucketRole
        - Arn
      Runtime: "python3.8"
    Type: AWS::Lambda::Function
  MyBucketFunctionPermission:
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: MyBucketFunction
      Principal: s3.amazonaws.com
      SourceArn: !Sub "arn:aws:s3:::my-bucket-323323-${AWS::StackName}-${AWS::Region}"
    Type: AWS::Lambda::Permission
  MyBucketRole:
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
        Version: '2012-10-17'
      Policies:
      - PolicyDocument:
          Statement:
          - Action: logs:*
            Effect: Allow
            Resource: '*'
          Version: '2012-10-17'
        PolicyName:
          Fn::Sub: my-bucket-role-policy-1234567890
    Type: AWS::IAM::Role
  • Related