AWSTemplateFormatVersion: '2010-09-09'
Description: 'Dynatrace Data Acquisition AWS monitoring activation - enable metrics, smartscape, logs and events (v1.0.3 2026-03-18 11:30)'

Metadata:
  Version:
    Number: v1.0.3
    ReleaseDate: 2026-03-18 11:30

  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: 'Dynatrace tokens'
        Parameters:
          - pDtApiToken
          - pDtIngestToken
      - Label:
          default: 'Log ingest'
        Parameters:
          - pDtLogsIngestEnabled
          - pDtLogsIngestRegions
      - Label:
          default: 'Event ingest'
        Parameters:
          - pDtEventsIngestEnabled
          - pDtEventsIngestRegions
          - pEventBridgeBusName
          - pEventSources
      - Label:
          default: 'Dynatrace environment'
        Parameters:
          - pDynatraceUrl
          - pMonitoringConfigId
      - Label:
          default: 'Encryption'
        Parameters:
          - pUseCMK
    ParameterLabels:
      pDtApiToken:
        default: 'Dynatrace settings platform token'
      pDtIngestToken:
        default: 'Dynatrace ingest platform token'
      pDtLogsIngestEnabled:
        default: 'Enable logs ingest'
      pDtLogsIngestRegions:
        default: 'Regions to ingest logs from'
      pDtEventsIngestEnabled:
        default: 'Enable events ingest'
      pDtEventsIngestRegions:
        default: 'Regions to ingest events from'
      pEventBridgeBusName:
        default: 'Amazon EventBridge bus name'
      pEventSources:
        default: 'EventBridge sources list'
      pDynatraceUrl:
        default: 'Dynatrace environment url'
      pMonitoringConfigId:
        default: 'Dynatrace monitoring configuration Id - do not modify'
      pUseCMK:
        default: 'Use Customer Managed Key (CMK)'

Parameters:
  pDynatraceUrl:
    Type: String
    Description: 'Your Dynatrace environment url'
    AllowedPattern: '^https:\/\/.+[^\/]$'
  pMonitoringConfigId:
    Type: String
    Description: 'AWS monitoring extension configuration ID'
  pDtApiToken:
    Type: String
    Description: 'Copy this link and open it in a new browser tab to access the Dynatrace documentation about how to create a settings and ingest token: https://dt-url.net/d1035yz'
    AllowedPattern: '(^dt0s16\.[A-Z0-9]{8}\.[A-Z0-9]{64}$)'
    MinLength: 1
    NoEcho: true
  pDtIngestToken:
    Type: String
    Description: 'Copy this link and open it in a new browser tab to access the Dynatrace documentation about how to create a settings and ingest token: https://dt-url.net/d1035yz'
    AllowedPattern: '(^$)|(^dt0s16\.[A-Z0-9]{8}\.[A-Z0-9]{64}$)'
    Default: ''
    NoEcho: true
  pDtLogsIngestEnabled:
    Type: String
    Description: 'Enable log ingestion'
    AllowedValues: [ 'TRUE', 'FALSE' ]
    Default: 'FALSE'
  pDtLogsIngestRegions:
    Type: CommaDelimitedList
    Description: 'Comma-separated list of regions to ingest logs from (e.g. us-east-1,eu-central-1)'
    Default: ''
  pDtEventsIngestEnabled:
    Type: String
    Description: 'Enable event ingestion'
    AllowedValues: [ 'TRUE', 'FALSE' ]
    Default: 'FALSE'
  pDtEventsIngestRegions:
    Type: CommaDelimitedList
    Description: 'Comma-separated list of regions e.g. us-west-2,eu-north-1'
    Default: ''
  pEventBridgeBusName:
    Type: String
    Description: 'Name of the Amazon EventBridge bus from which to consume the events from'
    AllowedPattern: '^[a-zA-Z0-9._-]*$'
    MinLength: 1
    MaxLength: 256
    Default: 'default'
  pEventSources:
    Type: CommaDelimitedList
    Description: 'Comma-separated list of values of event sources to forward to Dynatrace (e.g. aws.ec2,aws.s3,aws.lambda)'
    Default: 'aws.health'
  pUseCMK:
    Type: String
    Description: 'Create and use a Customer Managed Key (CMK) for encryption'
    AllowedValues: [ 'TRUE', 'FALSE' ]
    Default: 'FALSE'

Rules:
  LogIngestParameters:
    RuleCondition: !Equals [ !Ref pDtLogsIngestEnabled, 'TRUE' ]
    Assertions:
      - Assert:
          Fn::EachMemberIn:
            - !Ref pDtLogsIngestRegions
            - [ 'af-south-1', 'ap-east-1', 'ap-east-2', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-south-1', 'ap-south-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-southeast-3', 'ap-southeast-4', 'ap-southeast-5', 'ap-southeast-6', 'ap-southeast-7', 'ca-central-1', 'ca-west-1', 'eu-central-1', 'eu-central-2', 'eu-north-1', 'eu-south-1', 'eu-south-2', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'il-central-1', 'me-central-1', 'me-south-1', 'mx-central-1', 'sa-east-1', 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2' ]
        AssertDescription: 'Log ingest regions parameter needs to contain comma-separated list of valid AWS regions'
      - Assert: !Not [ !Equals [ !Ref pDtIngestToken, '' ] ]
        AssertDescription: 'Dynatrace ingest platform token must be set'

  EventIngestParameters:
    RuleCondition: !Equals [ !Ref pDtEventsIngestEnabled, 'TRUE' ]
    Assertions:
      - Assert:
          Fn::EachMemberIn:
            - !Ref pDtEventsIngestRegions
            - [ 'af-south-1', 'ap-east-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-south-1', 'ap-south-2', 'ap-southeast-1', 'ap-southeast-2', 'ap-southeast-3', 'ca-central-1', 'eu-central-1', 'eu-central-2', 'eu-north-1', 'eu-south-1', 'eu-south-2', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'me-central-1', 'me-south-1', 'sa-east-1', 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2' ]
        AssertDescription: "Event ingest regions parameter needs to contain comma-separated list of valid AWS regions, excluding unsupported regions for EventBridge ('ap-east-2', 'ap-southeast-4', 'ap-southeast-5', 'ap-southeast-6', 'ap-southeast-7', 'ca-west-1', 'il-central-1', 'mx-central-1')"
      - Assert: !Not [ !Equals [ !Ref pDtIngestToken, '' ] ]
        AssertDescription: 'Dynatrace ingest platform token must be set'
      - Assert: !Not [ !Contains [ [ 'ap-east-2', 'ap-southeast-4', 'ap-southeast-5', 'ap-southeast-6', 'ap-southeast-7', 'ca-west-1', 'il-central-1', 'mx-central-1' ], !Ref AWS::Region ] ]
        AssertDescription: 'Event ingest is not supported in current deployment region'

Conditions:
  cIngestEnabled: !Or
    - !Condition cLogIngestEnabled
    - !Condition cEventIngestEnabled
  cLogIngestEnabled: !And
    - !Equals [ !Ref pDtLogsIngestEnabled, 'TRUE' ]
    - !Not [ !Condition cEmptyLogRegions ]
  cEventIngestEnabled: !And
    - !Equals [ !Ref pDtEventsIngestEnabled, 'TRUE' ]
    - !Not [ !Condition cEmptyEventRegions ]
  cEmptyLogRegions: !Equals
    - !Join [ '', !Ref pDtLogsIngestRegions ]
    - ''
  cEmptyEventRegions: !Equals
    - !Join [ '', !Ref pDtEventsIngestRegions ]
    - ''

Resources:
  DynatraceApiClientStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://dynatrace-data-acquisition.s3.amazonaws.com/aws/deployment/cfn/v1.0.3/da-aws-nested-dt-api-function.yaml
      Parameters:
        pDynatraceUrl: !Ref pDynatraceUrl
        pDtApiToken: !Ref pDtApiToken
        pUseCMK: !Ref pUseCMK
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

  ReportStartedStatusResource:
    Type: Custom::DynatraceApiAccessFunction
    Properties:
      ServiceToken: !GetAtt DynatraceApiClientStack.Outputs.DynatraceApiClientFunctionArn
      ServiceTimeout: 120
      Step: REPORT_STATUS
      DynatraceApiKeySecret: !GetAtt DynatraceApiClientStack.Outputs.DynatraceSettingsTokenSecretArn
      MonitoringConfigurationId: !Ref pMonitoringConfigId
      # Pass rest of stack parameters (ignored) to trigger status reporting on every stack update
      pDynatraceUrl: !Ref pDynatraceUrl
      pDtLogsIngestEnabled: !Ref pDtLogsIngestEnabled
      pDtLogsIngestRegions: !Ref pDtLogsIngestRegions
      pDtEventsIngestEnabled: !Ref pDtEventsIngestEnabled
      pDtEventsIngestRegions: !Ref pDtEventsIngestRegions
      pEventBridgeBusName: !Ref pEventBridgeBusName
      pEventSources: !Ref pEventSources
      TemplateVersion: v1.0.3

  DynatraceIntegrationStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: ReportStartedStatusResource
    Properties:
      TemplateURL: https://dynatrace-data-acquisition.s3.amazonaws.com/aws/deployment/cfn/v1.0.3/da-aws-nested-integration.yaml
      Parameters:
        pDynatraceUrl: !Ref pDynatraceUrl
        pMonitoringConfigId: !Ref pMonitoringConfigId
        pDynatraceApiClientFunctionArn: !GetAtt DynatraceApiClientStack.Outputs.DynatraceApiClientFunctionArn
        pDtApiTokenSecretArn: !GetAtt DynatraceApiClientStack.Outputs.DynatraceSettingsTokenSecretArn
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

  DynatraceStackSetRoleStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: DynatraceIntegrationStack
    Condition: cIngestEnabled
    Properties:
      TemplateURL: https://dynatrace-data-acquisition.s3.amazonaws.com/aws/deployment/cfn/v1.0.3/da-aws-nested-stackset-role.yaml
      Parameters:
        pDynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

  # Log ingest StackSet
  DynatraceLogIngestStackSet:
    DependsOn:
      - DynatraceIntegrationStack
    Condition: cLogIngestEnabled
    Type: AWS::CloudFormation::StackSet
    Properties:
      Description: StackSet
      StackSetName: !Sub
        - 'DynatraceLogIngest-${DynatraceEnvironmentId}-${DeploymentHash}'
        - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
      StackInstancesGroup:
        - DeploymentTargets:
            Accounts:
              - !Ref AWS::AccountId
          Regions: !Ref pDtLogsIngestRegions
      OperationPreferences:
        FailureToleranceCount: 0
        MaxConcurrentCount: 1
        RegionConcurrencyType: PARALLEL
      Parameters:
        - ParameterKey: pDynatraceUrl
          ParameterValue: !Ref pDynatraceUrl
        - ParameterKey: pDtIngestToken
          ParameterValue: !Ref pDtIngestToken
        - ParameterKey: pUseCMK
          ParameterValue: !Ref pUseCMK
        - ParameterKey: pStackSetExecutionRoleName
          ParameterValue: !GetAtt DynatraceStackSetRoleStack.Outputs.StackSetExecutionRoleName
        - ParameterKey: pMonitoringConfigId
          ParameterValue: !Ref pMonitoringConfigId
      PermissionModel: SELF_MANAGED
      AdministrationRoleARN: !GetAtt DynatraceStackSetRoleStack.Outputs.StackSetAdministrationRoleArn
      ExecutionRoleName: !GetAtt DynatraceStackSetRoleStack.Outputs.StackSetExecutionRoleName
      Capabilities:
        - CAPABILITY_NAMED_IAM
      TemplateURL: https://dynatrace-data-acquisition.s3.amazonaws.com/aws/deployment/cfn/v1.0.3/da-aws-stack-logs.yaml
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

  # Event ingest StackSet
  DynatraceEventIngestStackSet:
    DependsOn:
      - DynatraceIntegrationStack
    Condition: cEventIngestEnabled
    Type: AWS::CloudFormation::StackSet
    Properties:
      Description: StackSet
      StackSetName: !Sub
        - 'DynatraceEventIngest-${DynatraceEnvironmentId}-${DeploymentHash}'
        - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
      StackInstancesGroup:
        - DeploymentTargets:
            Accounts:
              - !Ref AWS::AccountId
          Regions: !Ref pDtEventsIngestRegions
      OperationPreferences:
        FailureToleranceCount: 0
        MaxConcurrentCount: 1
        RegionConcurrencyType: PARALLEL
      Parameters:
        - ParameterKey: pDynatraceUrl
          ParameterValue: !Ref pDynatraceUrl
        - ParameterKey: pDtIngestToken
          ParameterValue: !Ref pDtIngestToken
        - ParameterKey: pEventBridgeBusName
          ParameterValue: !Ref pEventBridgeBusName
        - ParameterKey: pEventSources
          ParameterValue: !Join [ ',', !Ref pEventSources ]
        - ParameterKey: pMonitoringConfigId
          ParameterValue: !Ref pMonitoringConfigId
      PermissionModel: SELF_MANAGED
      AdministrationRoleARN: !GetAtt DynatraceStackSetRoleStack.Outputs.StackSetAdministrationRoleArn
      ExecutionRoleName: !GetAtt DynatraceStackSetRoleStack.Outputs.StackSetExecutionRoleName
      Capabilities:
        - CAPABILITY_NAMED_IAM
      TemplateURL: https://dynatrace-data-acquisition.s3.amazonaws.com/aws/deployment/cfn/v1.0.3/da-aws-stack-events.yaml
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

  ReportCompleteStatusResource:
    Type: Custom::DynatraceApiAccessFunction
    DependsOn: DynatraceIntegrationStack
    Metadata:
      ConditionalLogStackSetDependency: !If [ cLogIngestEnabled, !Ref DynatraceLogIngestStackSet, !Ref AWS::NoValue ]
      ConditionalEventStackSetDependency: !If [ cEventIngestEnabled, !Ref DynatraceEventIngestStackSet, !Ref AWS::NoValue ]
    Properties:
      ServiceToken: !GetAtt DynatraceApiClientStack.Outputs.DynatraceApiClientFunctionArn
      ServiceTimeout: 120
      Step: REPORT_COMPLETE
      DynatraceApiKeySecret: !GetAtt DynatraceApiClientStack.Outputs.DynatraceSettingsTokenSecretArn
      MonitoringConfigurationId: !Ref pMonitoringConfigId
      # Pass rest of stack parameters (ignored) to trigger status reporting on every stack update
      pDynatraceUrl: !Ref pDynatraceUrl
      pDtLogsIngestEnabled: !Ref pDtLogsIngestEnabled
      pDtLogsIngestRegions: !Ref pDtLogsIngestRegions
      pDtEventsIngestEnabled: !Ref pDtEventsIngestEnabled
      pDtEventsIngestRegions: !Ref pDtEventsIngestRegions
      pEventBridgeBusName: !Ref pEventBridgeBusName
      pEventSources: !Ref pEventSources
      TemplateVersion: v1.0.3

Outputs:
  IAMRoleExternalId:
    Value: !GetAtt DynatraceIntegrationStack.Outputs.IAMRoleExternalId
  DynatraceSettingsTokenSecretArn:
    Value: !GetAtt DynatraceApiClientStack.Outputs.DynatraceSettingsTokenSecretArn
    Description: ARN of SecretsManager secret storing Dynatrace settings platform token
  DynatraceLogIngestStackSetName:
    Condition: cLogIngestEnabled
    Value: !Sub
      - 'DynatraceLogIngest-${DynatraceEnvironmentId}-${DeploymentHash}'
      - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
  DynatraceEventIngestStackSetName:
    Condition: cEventIngestEnabled
    Value: !Sub
      - 'DynatraceEventIngest-${DynatraceEnvironmentId}-${DeploymentHash}'
      - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
  DynatraceActivationTemplateVersion:
    Value: v1.0.3
