AWSTemplateFormatVersion: "2010-09-09"
Description: Dynatrace Data Acquisition - AWS logs ingest via Firehose (v0.8.3)

Metadata:
  Version:
    Number: v0.8.3

Parameters:
  DtEnvironmentId:
    Description: Dynatrace environment ID
    Type: String
  DtIngestToken:
    Description: Secret with Dynatrace platform token for logs ingest
    Type: String
    NoEcho: true
  DacUrl:
    Description: Data Acquisition Controller URL
    Type: String
  UseCMK:
    Type: String
    Default: 'FALSE'
    AllowedValues: ['TRUE', 'FALSE']
    Description: Create and use a Customer Managed Key (CMK) for encryption

Conditions:
  UseCMK: !Equals [ !Ref UseCMK, 'TRUE' ]

Resources:
  DynatraceLogsKMSKey:
    Type: AWS::KMS::Key
    Condition: UseCMK
    Properties:
      Description: "Dynatrace Data Acquisition logs encryption key"
      KeyPolicy:
        Version: '2012-10-17'
        Id: dynatrace-logs-key-policy
        Statement:
          - Sid: Enable IAM User Permissions
            Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
            Action: 'kms:*'
            Resource: '*'
          - Sid: Allow KMS in CloudWatch Logs
            Effect: Allow
            Principal:
              Service: !Sub 'logs.${AWS::Region}.amazonaws.com'
            Action:
              - 'kms:Encrypt'
              - 'kms:Decrypt'
              - 'kms:ReEncrypt*'
              - 'kms:GenerateDataKey*'
              - 'kms:CreateGrant'
              - 'kms:DescribeKey'
            Resource: '*'
            Condition:
              ArnLike:
                'kms:EncryptionContext:aws:logs:arn': !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:*'
          - Sid: Allow KMS in Kinesis Firehose
            Effect: Allow
            Principal:
              Service: firehose.amazonaws.com
            Action:
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey'
            Resource: '*'
            Condition:
              StringEquals:
                'kms:ViaService': !Sub 'firehose.${AWS::Region}.amazonaws.com'
          - Sid: Allow KMS in S3
            Effect: Allow
            Principal:
              Service: s3.amazonaws.com
            Action:
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey'
            Resource: '*'
          - Sid: Allow KMS in Secrets Manager
            Effect: Allow
            Principal:
              Service: secretsmanager.amazonaws.com
            Action:
              - 'kms:Decrypt'
              - 'kms:GenerateDataKey'
              - 'kms:CreateGrant'
            Resource: '*'

  DynatraceLogsIngestTokenSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Description: Dynatrace ingest platform token secret
      Name: !Join
        - "_"
        - - "DynatraceLogsIngestPlatformToken"
          - !Ref DtEnvironmentId
          - !Select [ 2, !Split [ "/", !Ref AWS::StackId ] ]
      SecretString: !Sub '{"api_key": "${DtIngestToken}"}'
      KmsKeyId: !If [ UseCMK, !GetAtt DynatraceLogsKMSKey.Arn, !Ref AWS::NoValue ]

  CloudWatchLogsRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Role for subscription filters (to write to Firehose)
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: "logs.amazonaws.com"
            Action:
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'firehose:PutRecord'
                  - 'firehose:PutRecordBatch'
                Resource: !GetAtt DynatraceLogsDeliveryStream.Arn

  DynatraceLogsDeliveryStream:
    Type: AWS::KinesisFirehose::DeliveryStream
    Properties:
      DeliveryStreamType: "DirectPut"
      DeliveryStreamEncryptionConfigurationInput: !If
        - UseCMK
        - KeyType: CUSTOMER_MANAGED_CMK
          KeyARN: !GetAtt DynatraceLogsKMSKey.Arn
        - !Ref AWS::NoValue
      HttpEndpointDestinationConfiguration:
        EndpointConfiguration:
          Url: !Join
            - ""
            - - "https://"
              - !Ref DtEnvironmentId
              - "."
              - !Ref DacUrl
              - "/api/aws/firehose/v1/logs"
          Name: Dynatrace delivery endpoint
        SecretsManagerConfiguration:
          Enabled: true
          SecretARN: !Ref DynatraceLogsIngestTokenSecret
        RequestConfiguration:
          ContentEncoding: GZIP
        BufferingHints:
          IntervalInSeconds: 60
          SizeInMBs: 1
        RetryOptions:
          DurationInSeconds: 7200
        CloudWatchLoggingOptions:
          Enabled: true
          LogGroupName: !Ref FirehoseLogGroup
          LogStreamName: !Ref FirehoseLogStream
        RoleARN: !GetAtt DeliveryStreamRole.Arn
        S3BackupMode: FailedDataOnly
        S3Configuration:
          BucketARN: !GetAtt FirehoseBackupS3Bucket.Arn
          RoleARN: !GetAtt DeliveryStreamRole.Arn

  FirehoseLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Join [ "/", [ '/aws/datafirehose', !Ref AWS::StackName ] ]
      RetentionInDays: 14
      KmsKeyId: !If [ UseCMK, !GetAtt DynatraceLogsKMSKey.Arn, !Ref AWS::NoValue ]

  FirehoseLogStream:
    Type: AWS::Logs::LogStream
    Properties:
      LogGroupName: !Ref FirehoseLogGroup

  FirehoseBackupS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      LifecycleConfiguration:
        Rules:
          - ExpirationInDays: 14
            Status: 'Enabled'
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: aws:kms
              KMSMasterKeyID: !If [ UseCMK, !GetAtt DynatraceLogsKMSKey.Arn, 'alias/aws/s3' ]

  DeliveryStreamRole:
    Type: AWS::IAM::Role
    Properties:
      Description: Firehose delivery stream role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: firehose.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: FirehoseSecretAccessPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'secretsmanager:GetSecretValue'
                Resource:
                  - !Ref DynatraceLogsIngestTokenSecret
        - PolicyName: FirehoseLoggingPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'logs:PutLogEvents'
                Resource:
                  - !GetAtt FirehoseLogGroup.Arn
        - PolicyName: FirehoseDeliveryPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 's3:AbortMultipartUpload'
                  - 's3:GetBucketLocation'
                  - 's3:GetObject'
                  - 's3:ListBucket'
                  - 's3:ListBucketMultipartUploads'
                  - 's3:PutObject'
                Resource:
                  - !GetAtt FirehoseBackupS3Bucket.Arn
                  - !Join [ '/', [ !GetAtt FirehoseBackupS3Bucket.Arn, '*' ] ]
        - !If
          - UseCMK
          - PolicyName: KMSAccessPolicy
            PolicyDocument:
              Version: 2012-10-17
              Statement:
                - Effect: Allow
                  Action:
                    - 'kms:Decrypt'
                    - 'kms:GenerateDataKey'
                    - 'kms:CreateGrant'
                  Resource:
                    - !GetAtt DynatraceLogsKMSKey.Arn
          - !Ref AWS::NoValue
Outputs:
  FirehoseDeliveryStreamArn:
    Value: !GetAtt DynatraceLogsDeliveryStream.Arn
  CloudWatchLogsRoleArn:
    Value: !GetAtt CloudWatchLogsRole.Arn
  DynatraceLogsIngestTokenSecret:
    Value: !Ref DynatraceLogsIngestTokenSecret
    Description: ARN of SecretsManager secret storing Dynatrace logs ingest platform token
