Search code examples
amazon-web-servicesamazon-s3aws-cloudformationamazon-sns

Can't get S3 notification yaml/stack to work


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

Solution

  • 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: