---
AWSTemplateFormatVersion: "2010-09-09"

Description: Scanner S3 Indexing Integration

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Scanner Auth Parameters
        Parameters:
          - ScannerExternalID
    ParameterLabels:
      ScannerExternalID:
        default: Scanner External ID
      ScannerAWSAccountID:
        default: Scanner AWS Account ID
      S3BucketToIndex:
        default: S3 Bucket to Index
      S3BucketKmsKeyArn:
        default: KMS Key for "S3 Bucket To Index"
      S3CollectDestinationBucket:
        default: S3 Collect Destination Bucket

Parameters:
  ScannerExternalID:
    Description: Scanner provides an External ID to use here.
    Type: String
  ScannerAWSAccountID:
    Description: Scanner provides its AWS Account ID to use here.
    Type: String
  S3BucketToIndex:
    Description: Enter the name of the S3 bucket that you would like Scanner to index.
    Type: String
  S3BucketKmsKeyArn:
    Default: ""
    Description: Enter the ARN of the KMS key used to encrypt "S3 Bucket To Index". Not needed if the bucket does not use a KMS key.
    Type: String
  S3CollectDestinationBucket:
    Default: ""
    Description: Enter the name of the S3 bucket that Scanner Collect will write logs into.
    Type: String

Conditions:
  HasS3BucketKmsKeyArn: !Not [!Equals [!Ref S3BucketKmsKeyArn, ""]]
  HasS3CollectDestinationBucket: !Not [!Equals [!Ref S3CollectDestinationBucket, ""]]

Resources:
  ScannerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "scnr-ScannerRole"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                Ref: ScannerAWSAccountID
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId:
                  Ref: ScannerExternalID

  ScannerIndexFilesBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: "Retain"
    Properties:
      BucketName: !Sub
        - "scanner-index-files-${Suffix}"
        - Suffix: !Ref ScannerExternalID
      AccessControl: Private
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - BucketKeyEnabled: true
            ServerSideEncryptionByDefault:
              SSEAlgorithm: "aws:kms"
      LifecycleConfiguration:
        Rules:
          - Id: ExpireTagging
            Status: Enabled
            TagFilters:
              - Key: "Scnr-Lifecycle"
                Value: "expire"
            ExpirationInDays: 1
          - Id: AbortIncompleteMultiPartUploads
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 1

  LogsBucketEventNotificationTopic:
    Type: AWS::SNS::Topic

  LogsBucketEventNotificationTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: s3.amazonaws.com
            Action: sns:Publish
            Resource: "*"
      Topics:
        - !Ref LogsBucketEventNotificationTopic

  LogsBucketEventNotificationTopicSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref LogsBucketEventNotificationTopic
      Endpoint: !Sub
        - "arn:aws:sqs:${InstanceRegion}:${ScannerAWSAccountID}:scnr-S3ObjectCreatedNotificationsQueue"
        - InstanceRegion: !Ref "AWS::Region"
          ScannerAWSAccountID: !Ref ScannerAWSAccountID
      Protocol: "sqs"
      RawMessageDelivery: true

  ScannerPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: ScannerPolicy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - s3:ListAllMyBuckets
              - s3:GetBucketLocation
              - s3:GetBucketTagging
            Resource: "*"
          - Effect: Allow
            Action:
              - s3:GetBucketEncryption
              - s3:GetBucketNotification
              - s3:ListBucket
              - s3:GetObject
              - s3:GetObjectTagging
            Resource:
              - Fn::Join:
                  - ""
                  - - "arn:aws:s3:::"
                    - Ref: S3BucketToIndex
              - Fn::Join:
                  - ""
                  - - "arn:aws:s3:::"
                    - Ref: S3BucketToIndex
                    - "/*"
          - Effect: Allow
            Action:
              - s3:GetLifecycleConfiguration
              - s3:ListBucket
              - s3:GetObject
              - s3:GetObjectTagging
              - s3:PutObject
              - s3:PutObjectTagging
              - s3:DeleteObject
              - s3:DeleteObjectTagging
              - s3:DeleteObjectVersion
              - s3:DeleteObjectVersionTagging
            Resource:
              - Fn::Join:
                  - ""
                  - - "arn:aws:s3:::scanner-index-files-"
                    - Ref: ScannerExternalID
              - Fn::Join:
                  - ""
                  - - "arn:aws:s3:::scanner-index-files-"
                    - Ref: ScannerExternalID
                    - "/*"
          - !If
            - HasS3CollectDestinationBucket
            - # Add Scanner Collect permissions for read/write to the destination bucket
              Effect: Allow
              Action:
                - s3:GetBucketNotification
                - s3:GetEncryptionConfiguration
                - s3:GetLifecycleConfiguration
                - s3:ListBucket
                - s3:GetObject
                - s3:GetObjectTagging
                - s3:PutObject
                - s3:PutObjectTagging
              Resource:
                - Fn::Join:
                    - ""
                    - - "arn:aws:s3:::"
                      - Ref: S3CollectDestinationBucket
                - Fn::Join:
                    - ""
                    - - "arn:aws:s3:::"
                      - Ref: S3CollectDestinationBucket
                      - "/*"
            - # Otherwise, omit this statement
              !Ref "AWS::NoValue"
          - !If
            - HasS3CollectDestinationBucket
            - # Add Scanner Collect permissions for deleting test files only
              Effect: Allow
              Action:
                - s3:DeleteObject
                - s3:DeleteObjectTagging
                - s3:DeleteObjectVersion
                - s3:DeleteObjectVersionTagging
              Resource:
                - Fn::Join:
                    - ""
                    - - "arn:aws:s3:::"
                      - Ref: S3CollectDestinationBucket
                      - "/*ScannerTestFile"
            - # Otherwise, omit this statement
              !Ref "AWS::NoValue"
          - !If
            - HasS3BucketKmsKeyArn
            - # Add KMS permissions if there is a key
              Effect: Allow
              Action:
                - kms:Decrypt
                - kms:DescribeKey
              Resource: !Ref S3BucketKmsKeyArn
            - # Otherwise, omit this statement
              !Ref "AWS::NoValue"
      Roles:
        - Ref: ScannerRole

Outputs:
  ScannerRoleARN:
    Description: The ARN of the new Scanner IAM Role
    Value:
      Fn::GetAtt:
        - ScannerRole
        - Arn

  ScannerIndexFilesBucketName:
    Description: The name of the new S3 bucket that will store Scanner Index Files
    Value: !Ref ScannerIndexFilesBucket
