Can't get S3 notification yaml/stack to work

Viewed 174

Everything works perfectly in the code below if run without the 4 lines starting NotificationConfiguration . I thought this might be because of needing the topic policy before setting notification on the bucket. So have tried to do the initial create without the NotificationConfiguration lines and then add these in and update the stack. But get the error Unable to validate the following destination configurations (Service: Amazon S3; Status Code: 400; Error Code: InvalidArgument; . I've tried things like putting the actual topic arn not using !Ref but no joy. Thanks!

Resources:
  DeletionSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
       DisplayName: 
         !Join [" ",[Data has been deleted from,!Sub '${ServiceName}-${Stage}-${AWS::AccountId}']
               ]
       Subscription:
           - Endpoint: !Sub '${DeleteNotifyEmail}'
             Protocol: email
       TopicName: !Sub 'delete-from-${ServiceName}-bucket'

  DataBucket:
    Type: AWS::S3::Bucket
    DependsOn: DeletionSNSTopic
    Description: Create Amazon S3 bucket from CloudFormation
    Properties:
      BucketName: !Sub '${ServiceName}-${Stage}-${AWS::AccountId}' 
      AccessControl: Private
      BucketEncryption: 
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      NotificationConfiguration:
        TopicConfigurations:
          - Topic: !Ref DeletionSNSTopic
            Event: 's3:ObjectRemoved:*'
             
  BucketToSNSPermission:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Id: 'deletionTopicPolicy'
        Version: '2012-10-17'
        Statement:
        - Sid: 'deletionTopic-statement-id'
          Effect: Allow
          Principal: 
             Service: s3.amazonaws.com
          Action: sns:Publish
          Resource: !Ref DeletionSNSTopic
          Condition: 
             StringEquals: 
                aws:SourceAccount: !Sub '${AWS::AccountId}'
             ArnLike: 
                aws:SourceArn: !Ref DataBucket
      Topics:
      - !Ref DeletionSNSTopic
1 Answers

You have circular dependency in your code. You create bucket with notifications, before topic policy is applied. Obviously the policy can't be created before the bucket because the bucket must already exist due to !Ref DataBucket.

To solve that the bucket name must be known first, which in your case is possible:

Resources:
  DeletionSNSTopic:
    Type: AWS::SNS::Topic
    Properties:
       DisplayName: 
         !Join [" ",[Data has been deleted from,!Sub '${ServiceName}-${Stage}-${AWS::AccountId}']
               ]
       Subscription:
           - Endpoint: !Sub '${DeleteNotifyEmail}'
             Protocol: email
       TopicName: !Sub 'delete-from-${ServiceName}-bucket'

  DataBucket:
    Type: AWS::S3::Bucket
    DependsOn: BucketToSNSPermission
    Description: Create Amazon S3 bucket from CloudFormation
    Properties:
      BucketName: !Sub '${ServiceName}-${Stage}-${AWS::AccountId}' 
      AccessControl: Private
      BucketEncryption: 
        ServerSideEncryptionConfiguration: 
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      NotificationConfiguration:
        TopicConfigurations:
          - Topic: !Ref DeletionSNSTopic
            Event: 's3:ObjectRemoved:*'
             
  BucketToSNSPermission:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Id: 'deletionTopicPolicy'
        Version: '2012-10-17'
        Statement:
        - Sid: 'deletionTopic-statement-id'
          Effect: Allow
          Principal: 
             Service: s3.amazonaws.com
          Action: SNS:Publish
          Resource: !Ref DeletionSNSTopic
          Condition: 
             StringEquals: 
                aws:SourceAccount: !Ref AWS::AccountId
             ArnLike: 
                aws:SourceArn: !Sub "arn:aws:s3:::${ServiceName}-${Stage}-${AWS::AccountId}"
      Topics:
      - !Ref DeletionSNSTopic

For general case check in:

Related