AWSTemplateFormatVersion: '2010-09-09'
Description: >
  Creates an S3 bucket, IAM role, and CloudTrail audit trail for dx0.
  dx0 uses the bucket to store your Salesforce metadata index and (if BYO S3 is
  enabled) encrypted Salesforce credentials. CloudTrail logs every read and write
  dx0 makes to your bucket so you have a tamper-evident audit record.
  For more information, visit https://intercom.help/dx0

Parameters:
  ExternalId:
    Type: String
    Description: Your dx0 Workspace ID (found in Settings → Storage)
    MinLength: 1
  BucketName:
    Type: String
    Description: >
      Name for the S3 bucket. Must be globally unique across all AWS accounts.
      Example: dx0-metadata-your-company
    MinLength: 3
    MaxLength: 63

Resources:
  MetadataBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref BucketName
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - Id: AbortIncompleteMultipartUploads
            Status: Enabled
            AbortIncompleteMultipartUpload:
              DaysAfterInitiation: 7

  # Separate bucket that receives CloudTrail logs.
  # Kept distinct from MetadataBucket so audit logs can't be overwritten by dx0.
  AuditLogsBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${BucketName}-audit'
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - Id: RetainAuditLogsOneYear
            Status: Enabled
            ExpirationInDays: 365

  # CloudTrail requires an explicit bucket policy before it will accept log delivery.
  AuditLogsBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref AuditLogsBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudTrailAclCheck
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:GetBucketAcl
            Resource: !GetAtt AuditLogsBucket.Arn
            Condition:
              StringEquals:
                aws:SourceArn: !Sub 'arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/dx0-${ExternalId}-trail'
          - Sid: CloudTrailWrite
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub '${AuditLogsBucket.Arn}/AWSLogs/${AWS::AccountId}/*'
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control
                aws:SourceArn: !Sub 'arn:aws:cloudtrail:${AWS::Region}:${AWS::AccountId}:trail/dx0-${ExternalId}-trail'

  # Trail logs all S3 data events (GetObject, PutObject, DeleteObject) on MetadataBucket.
  # This gives you a tamper-evident record of every time dx0 reads or writes your data.
  Dx0AuditTrail:
    Type: AWS::CloudTrail::Trail
    DependsOn: AuditLogsBucketPolicy
    Properties:
      TrailName: !Sub 'dx0-${ExternalId}-trail'
      S3BucketName: !Ref AuditLogsBucket
      IsLogging: true
      IsMultiRegionTrail: false
      IncludeGlobalServiceEvents: false
      EventSelectors:
        - ReadWriteType: All
          IncludeManagementEvents: false
          DataResources:
            - Type: AWS::S3::Object
              Values:
                - !Sub '${MetadataBucket.Arn}/'

  Dx0AccessRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: dx0-metadata-access
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              # dx0's AWS account - this allows dx0 to request temporary access
              AWS: arn:aws:iam::584164024172:root
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalId
      Policies:
        - PolicyName: dx0-bucket-access
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Sid: ListBucket
                Effect: Allow
                Action:
                  - s3:ListBucket
                Resource: !GetAtt MetadataBucket.Arn
              - Sid: ReadWriteObjects
                Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:DeleteObject
                Resource: !Sub '${MetadataBucket.Arn}/*'

Outputs:
  RoleArn:
    Description: IAM Role ARN — paste this into dx0 Settings
    Value: !GetAtt Dx0AccessRole.Arn
  BucketName:
    Description: S3 Bucket name — paste this into dx0 Settings
    Value: !Ref MetadataBucket
  Region:
    Description: AWS Region — paste this into dx0 Settings
    Value: !Ref AWS::Region
  AuditLogsBucketName:
    Description: S3 bucket where CloudTrail logs every dx0 read and write
    Value: !Ref AuditLogsBucket
  AuditTrailName:
    Description: CloudTrail trail name
    Value: !Sub 'dx0-${ExternalId}-trail'
