AWSTemplateFormatVersion: "2010-09-09"

Description: Monte Carlo OpenTelemetry storage bucket and external access role

Metadata:
  License: >
    Copyright 2023 Monte Carlo Data, Inc.

    The Software contained herein (the "Software") is the intellectual property of Monte Carlo Data, Inc. ("Licensor"),
    and Licensor retains all intellectual property rights in the Software, including any and all derivatives, changes and
    improvements thereto. Only customers who have entered into a commercial agreement with Licensor for use or
    purchase of the Software ("Licensee") are licensed or otherwise authorized to use the Software, and any Licensee
    agrees that it obtains no copyright or other intellectual property rights to the Software, except for the license
    expressly granted below or in accordance with the terms of their commercial agreement with Licensor (the
    "Agreement"). Subject to the terms and conditions of the Agreement, Licensor grants Licensee a non-exclusive,
    non-transferable, non-sublicensable, revocable, limited right and license to use the Software, in each case solely
    internally within Licensee's organization for non-commercial purposes and only in connection with the service
    provided by Licensor pursuant to the Agreement, and in object code form only. Without Licensor's express prior
    written consent, Licensee may not, directly or indirectly, (i) distribute the Software, any portion thereof, or any
    modifications, enhancements, or derivative works of any of the foregoing (collectively, the "Derivatives") to any
    third party, (ii) license, market, sell, offer for sale or otherwise attempt to commercialize any Software, Derivatives,
    or portions thereof, (iii) use the Software, Derivatives, or any portion thereof for the benefit of any third party, (iv)
    use the Software, Derivatives, or any portion thereof in any manner or with respect to any commercial activity
    which competes, or is reasonably likely to compete, with any business that Licensor conducts, proposes to conduct
    or demonstrably anticipates conducting, at any time; or (v) seek any patent or other intellectual property rights or
    protections over or in connection with any Software of Derivatives.

Parameters:
  OpenTelemetryCollectorExternalNotificationChannelArn:
    Description: SQS Queue ARN or SNS Topic ARN to receive S3 event notifications for telemetry data. If left empty, no notifications will be configured.
    Type: String
    Default: "N/A"
    AllowedPattern: '^(|N/A|arn:aws:(sqs|sns):[^:]+:[0-9]{12}:[^:]+)$'
    ConstraintDescription: Must be either empty, N/A, a valid SQS ARN (arn:aws:sqs:region:account:queue-name), or a valid SNS ARN (arn:aws:sns:region:account:topic-name)
  ExternalID:
    Description: External ID to access the S3 bucket.
    Type: String
    Default: "N/A"
  ExternalAccessPrincipal:
    Description: Principal (AWS ARN/account ID or Federated identifier) allowed to assume the external access role. If left empty, will use the current AWS account ID.
    Type: String
    Default: "N/A"
  ExternalAccessPrincipalType:
    Description: Type of principal for external access role
    Type: String
    Default: "AWS"
    AllowedValues: ["AWS", "Federated"]
  ExternalAccessRoleName:
    Description: Custom name of the external access role. If left empty, will use the default name of "{AWS::StackName}-EAR".
    Type: String
    Default: "N/A"
  McdOtelCollectorTaskRoleArn:
    Description: Task role ARN allowed to write OpenTelemetry data to the S3 bucket.
    Type: String
    AllowedPattern: '^arn:aws:iam::[0-9]{12}:role\/.+$'
    ConstraintDescription: Must be a valid IAM role ARN.
  VpcExternalId:
    Description: Optional VPC endpoint ID that must be used for bucket writes.
    Type: String
    Default: "N/A"
    AllowedPattern: '^(|N/A|vpce-[0-9a-f]+)$'
    ConstraintDescription: Must be either empty, N/A, or a valid VPC endpoint ID (vpce-xxxxxxxx).

Conditions:
  HasNotificationChannel: !And
    - !Not [!Equals [!Ref OpenTelemetryCollectorExternalNotificationChannelArn, "N/A"]]
    - !Not [!Equals [!Ref OpenTelemetryCollectorExternalNotificationChannelArn, ""]]
  IsSQSNotification: !And
    - !Condition HasNotificationChannel
    - !Not [!Equals [!Select [0, !Split ["arn:aws:sqs", !Ref OpenTelemetryCollectorExternalNotificationChannelArn]], !Ref OpenTelemetryCollectorExternalNotificationChannelArn]]
  UseCurrentAccount: !Equals [!Ref ExternalAccessPrincipal, "N/A"]
  UseAWSPrincipal: !Equals [!Ref ExternalAccessPrincipalType, "AWS"]
  UseCustomExternalAccessRoleName: !Not [!Equals [!Ref ExternalAccessRoleName, "N/A"]]
  UseVpcEndpoint: !And
    - !Not [!Equals [!Ref VpcExternalId, "N/A"]]
    - !Not [!Equals [!Ref VpcExternalId, ""]]

Resources:
  Storage:
    Properties:
      Tags:
        - Key: Service
          Value: "mcd-otel-collector"
        - Key: Provider
          Value: "monte-carlo"
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 90
            Prefix: "mcd/"
            Status: Enabled
          - ExpirationInDays: 2
            Prefix: "mcd/responses/"
            Status: Enabled
          - ExpirationInDays: 30
            Prefix: "mcd/otel-collector/"
            Status: Enabled
      NotificationConfiguration: !If
        - HasNotificationChannel
        - !If
          - IsSQSNotification
          - QueueConfigurations:
              - Event: s3:ObjectCreated:*
                Queue: !Ref OpenTelemetryCollectorExternalNotificationChannelArn
                Filter:
                  S3Key:
                    Rules:
                      - Name: prefix
                        Value: mcd/otel-collector/
              - Event: s3:ObjectRemoved:*
                Queue: !Ref OpenTelemetryCollectorExternalNotificationChannelArn
                Filter:
                  S3Key:
                    Rules:
                      - Name: prefix
                        Value: mcd/otel-collector/
          - TopicConfigurations:
              - Event: s3:ObjectCreated:*
                Topic: !Ref OpenTelemetryCollectorExternalNotificationChannelArn
                Filter:
                  S3Key:
                    Rules:
                      - Name: prefix
                        Value: mcd/otel-collector/
              - Event: s3:ObjectRemoved:*
                Topic: !Ref OpenTelemetryCollectorExternalNotificationChannelArn
                Filter:
                  S3Key:
                    Rules:
                      - Name: prefix
                        Value: mcd/otel-collector/
        - !Ref AWS::NoValue
    Type: AWS::S3::Bucket
  StorageSSLPolicy:
    Properties:
      Bucket: !Ref Storage
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: DenyActionsWithoutSSL
            Effect: Deny
            Principal:
              AWS: "*"
            Action:
              - "*"
            Resource:
              - !GetAtt Storage.Arn
              - !Sub "${Storage.Arn}/*"
            Condition:
              Bool:
                aws:SecureTransport:
                  - false
          - Sid: AllowTaskRoleWrite
            Effect: Allow
            Principal:
              AWS: !Ref McdOtelCollectorTaskRoleArn
            Action:
              - s3:PutObject
              - s3:PutObjectAcl
              - s3:GetBucketLocation
            Resource:
              - !GetAtt Storage.Arn
              - !Sub "${Storage.Arn}/mcd/otel-collector/*"
            Condition: !If
              - UseVpcEndpoint
              - StringEquals:
                  aws:SourceVpce: !Ref VpcExternalId
              - !Ref AWS::NoValue
    Type: AWS::S3::BucketPolicy
  ExternalAccessS3ReadOnlyPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: OpenTelemetryS3ExternalAccessReadOnly
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - s3:GetBucketLocation
              - s3:GetObject
              - s3:ListBucket
              - s3:GetObjectVersion
            Resource:
              - !GetAtt Storage.Arn
              - !Sub "${Storage.Arn}/*"
      Roles:
        - !Ref ExternalAccessRole
  ExternalAccessRole:
    # This role is used to access the S3 bucket containing the telemetry data
    # by an external warehouse, such as Snowflake.
    Type: AWS::IAM::Role
    Properties:
      RoleName: !If
        - UseCustomExternalAccessRoleName
        - !Ref ExternalAccessRoleName
        - !Sub "${AWS::StackName}-EAR"
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: !If
              - UseCurrentAccount
              - AWS: !Ref AWS::AccountId
              - !If
                - UseAWSPrincipal
                - AWS:
                  - !Ref ExternalAccessPrincipal
                  - !If
                    - UseCustomExternalAccessRoleName
                    - !Sub "arn:aws:iam::${AWS::AccountId}:role/${ExternalAccessRoleName}"
                    - !Sub "arn:aws:iam::${AWS::AccountId}:role/${AWS::StackName}-EAR"
                - Federated: !Ref ExternalAccessPrincipal
            Action: sts:AssumeRole
            Condition:
              StringEquals:
                sts:ExternalId: !Ref ExternalID
      Tags:
        - Key: Service
          Value: "mcd-otel-collector"
        - Key: Provider
          Value: "monte-carlo"

Outputs:
  StorageBucketName:
    Description: Name of the S3 bucket used to store OpenTelemetry data.
    Value: !Ref Storage
  StorageBucketArn:
    Description: ARN of the S3 bucket used to store OpenTelemetry data.
    Value: !GetAtt Storage.Arn
  ExternalAccessRoleArn:
    Description: The ARN of the IAM role for external access to the OpenTelemetry S3 bucket.
    Value: !GetAtt ExternalAccessRole.Arn
