AWSTemplateFormatVersion: '2010-09-09'
Description: 'Dynatrace Data Acquisition AWS monitoring role  (v1.0.4 2026-03-19 18:35)'

Metadata:
  Version:
    Number: v1.0.4
    ReleaseDate: 2026-03-19 18:35

Parameters:
  pDynatraceUrl:
    Type: String
    Description: 'Your Dynatrace environment url'
    AllowedPattern: '^https:\/\/.+[^\/]$'
  pRoleExternalId:
    Type: String
    MinLength: 1

Conditions:
  cProductionEnvironment: !Equals [ !Select [ 2, !Split [ ".", !Select [ 1, !Split [ "://", !Ref pDynatraceUrl ] ] ] ], 'dynatrace' ]

Resources:
  DynatraceMonitoringPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub
        - "DynatraceMonitoringPolicy-${DynatraceEnvironmentId}-${DeploymentHash}"
        - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
      Description: Dynatrace Data Acquisition Monitoring Policy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: TopologyInventory
            Effect: Allow
            Action:
              - 'account:GetAccountInformation'
              - 'acm-pca:ListCertificateAuthorities'
              - 'aoss:BatchGetCollection'
              - 'aoss:ListCollections'
              - 'apigateway:GET'
              - 'apprunner:DescribeAutoScalingConfiguration'
              - 'apprunner:DescribeService'
              - 'apprunner:DescribeVpcConnector'
              - 'apprunner:DescribeVpcIngressConnection'
              - 'apprunner:ListAutoScalingConfigurations'
              - 'apprunner:ListServices'
              - 'apprunner:ListTagsForResource'
              - 'apprunner:ListVpcConnectors'
              - 'apprunner:ListVpcIngressConnections'
              - 'athena:GetWorkGroup'
              - 'athena:ListWorkGroups'
              - 'autoscaling:DescribeAutoScalingGroups'
              - 'autoscaling:DescribeWarmPool'
              - 'bedrock:GetAgent'
              - 'bedrock:GetAgentAlias'
              - 'bedrock:GetGuardrail'
              - 'bedrock:GetKnowledgeBase'
              - 'bedrock:ListAgentAliases'
              - 'bedrock:ListAgents'
              - 'bedrock:ListGuardrails'
              - 'bedrock:ListKnowledgeBases'
              - 'cloudfront:GetDistribution'
              - 'cloudfront:ListDistributions'
              - 'cloudfront:ListTagsForResource'
              - 'cloudhsm:DescribeClusters'
              - 'cloudtrail:GetEventSelectors'
              - 'cloudtrail:GetTrail'
              - 'cloudtrail:GetTrailStatus'
              - 'cloudtrail:ListTrails'
              - 'cloudtrail:LookupEvents'
              - 'dax:DescribeClusters'
              - 'dax:DescribeSubnetGroups'
              - 'directconnect:DescribeConnections'
              - 'directconnect:DescribeVirtualInterfaces'
              - 'dms:DescribeReplicationInstances'
              - 'dynamodb:DescribeContinuousBackups'
              - 'dynamodb:DescribeKinesisStreamingDestination'
              - 'dynamodb:DescribeTable'
              - 'dynamodb:DescribeTimeToLive'
              - 'dynamodb:ListTables'
              - 'ec2:DescribeAddresses'
              - 'ec2:DescribeAvailabilityZones'
              - 'ec2:DescribeClientVpnEndpoints'
              - 'ec2:DescribeCustomerGateways'
              - 'ec2:DescribeDhcpOptions'
              - 'ec2:DescribeEgressOnlyInternetGateways'
              - 'ec2:DescribeIamInstanceProfileAssociations'
              - 'ec2:DescribeInstances'
              - 'ec2:DescribeInternetGateways'
              - 'ec2:DescribeLaunchTemplateVersions'
              - 'ec2:DescribeLaunchTemplates'
              - 'ec2:DescribeNatGateways'
              - 'ec2:DescribeNetworkAcls'
              - 'ec2:DescribeNetworkInterfaces'
              - 'ec2:DescribeRegions'
              - 'ec2:DescribeRouteTables'
              - 'ec2:DescribeSecurityGroups'
              - 'ec2:DescribeSnapshots'
              - 'ec2:DescribeSubnets'
              - 'ec2:DescribeTransitGatewayAttachments'
              - 'ec2:DescribeTransitGatewayConnects'
              - 'ec2:DescribeTransitGatewayMulticastDomains'
              - 'ec2:DescribeTransitGatewayRouteTables'
              - 'ec2:DescribeTransitGateways'
              - 'ec2:DescribeVolumes'
              - 'ec2:DescribeVpcEndpointServiceConfigurations'
              - 'ec2:DescribeVpcEndpoints'
              - 'ec2:DescribeVpcPeeringConnections'
              - 'ec2:DescribeVpcs'
              - 'ec2:DescribeVpnConnections'
              - 'ec2:DescribeVpnGateways'
              - 'ecr-public:DescribeRepositories'
              - 'ecr-public:GetRepositoryPolicy'
              - 'ecr:DescribeRepositories'
              - 'ecr:GetRepositoryPolicy'
              - 'ecs:DescribeCapacityProviders'
              - 'ecs:DescribeClusters'
              - 'ecs:DescribeContainerInstances'
              - 'ecs:DescribeServices'
              - 'ecs:DescribeTaskDefinition'
              - 'ecs:DescribeTasks'
              - 'ecs:ListClusters'
              - 'ecs:ListContainerInstances'
              - 'ecs:ListServices'
              - 'ecs:ListTaskDefinitions'
              - 'ecs:ListTasks'
              - 'eks:DescribeCluster'
              - 'eks:DescribeNodegroup'
              - 'eks:ListClusters'
              - 'eks:ListNodegroups'
              - 'elasticache:DescribeCacheClusters'
              - 'elasticache:DescribeCacheParameterGroups'
              - 'elasticache:DescribeCacheParameters'
              - 'elasticache:DescribeCacheSubnetGroups'
              - 'elasticache:DescribeServerlessCaches'
              - 'elasticbeanstalk:DescribeApplications'
              - 'elasticbeanstalk:DescribeEnvironmentResources'
              - 'elasticbeanstalk:DescribeEnvironments'
              - 'elasticfilesystem:DescribeAccessPoints'
              - 'elasticfilesystem:DescribeFileSystems'
              - 'elasticfilesystem:DescribeMountTargets'
              - 'elasticloadbalancing:DescribeListeners'
              - 'elasticloadbalancing:DescribeLoadBalancerAttributes'
              - 'elasticloadbalancing:DescribeLoadBalancerPolicies'
              - 'elasticloadbalancing:DescribeLoadBalancers'
              - 'elasticloadbalancing:DescribeRules'
              - 'elasticloadbalancing:DescribeTargetGroupAttributes'
              - 'elasticloadbalancing:DescribeTargetGroups'
              - 'elasticloadbalancing:DescribeTargetHealth'
              - 'es:DescribeDomains'
              - 'es:ListDomainNames'
              - 'events:DescribeEventBus'
              - 'events:ListEventBuses'
              - 'firehose:DescribeDeliveryStream'
              - 'firehose:ListDeliveryStreams'
              - 'iam:GenerateCredentialReport'
              - 'iam:GetAccountAuthorizationDetails'
              - 'iam:GetAccountPasswordPolicy'
              - 'iam:GetAccountSummary'
              - 'iam:GetCredentialReport'
              - 'iam:ListAccountAliases'
              - 'iam:ListInstanceProfiles'
              - 'iam:ListMFADevices'
              - 'iam:ListServerCertificates'
              - 'kafka:ListClustersV2'
              - 'kms:DescribeKey'
              - 'kms:GetKeyRotationStatus'
              - 'kms:ListAliases'
              - 'kms:ListKeys'
              - 'lambda:GetAlias'
              - 'lambda:GetFunction'
              - 'lambda:GetPolicy'
              - 'lambda:ListAliases'
              - 'lambda:ListEventSourceMappings'
              - 'lambda:ListFunctions'
              - 'logs:DescribeLogGroups'
              - 'logs:DescribeSubscriptionFilters'
              - 'mq:DescribeBroker'
              - 'mq:DescribeConfiguration'
              - 'mq:ListBrokers'
              - 'mq:ListConfigurations'
              - 'organizations:DescribeOrganization'
              - 'rds:DescribeOptionGroups'
              - 'rds:ListTagsForResource'
              - 'redshift-serverless:ListNamespaces'
              - 'redshift-serverless:ListWorkgroups'
              - 'redshift:DescribeClusterSubnetGroups'
              - 'redshift:DescribeClusters'
              - 'redshift:DescribeLoggingStatus'
              - 'route53:GetHostedZone'
              - 'route53:ListHealthChecks'
              - 'route53:ListHostedZones'
              - 's3:GetAccelerateConfiguration'
              - 's3:GetBucketAcl'
              - 's3:GetBucketLogging'
              - 's3:GetBucketNotification'
              - 's3:GetBucketPolicy'
              - 's3:GetBucketPublicAccessBlock'
              - 's3:GetBucketRequestPayment'
              - 's3:GetBucketVersioning'
              - 's3:GetEncryptionConfiguration'
              - 's3:ListAllMyBuckets'
              - 'sns:GetTopicAttributes'
              - 'sns:ListTopics'
              - 'sqs:GetQueueAttributes'
              - 'sqs:ListQueues'
              - 'states:DescribeStateMachine'
              - 'states:ListStateMachines'
              - 'storagegateway:DescribeGatewayInformation'
              - 'storagegateway:ListGateways'
              - 'tag:GetResources'
              - 'tag:GetTagKeys'
              - 'tag:GetTagValues'
              - 'wafv2:GetWebACL'
              - 'wafv2:ListWebACLs'
            Resource: '*'
          - Sid: CloudWatchMonitoring
            Effect: Allow
            Action:
              - 'cloudwatch:GetMetricData'
              - 'cloudwatch:ListMetrics'
            Resource: '*'
  DynatraceMonitoringPolicy2:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: !Sub
        - "DynatraceMonitoringPolicy-${DynatraceEnvironmentId}-${DeploymentHash}-2"
        - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
      Description: Dynatrace Data Acquisition Monitoring Policy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CassandraTopologyPrerequisites
            # See https://docs.aws.amazon.com/keyspaces/latest/devguide/security_iam_id-based-policy-examples.html#security_iam_id-based-policy-examples-access-one-table
            Effect: Allow
            Action:
              - 'cassandra:Select'
            Resource:
              - !Sub 'arn:aws:cassandra:*:${AWS::AccountId}:/keyspace/system*'
          - Sid: CassandraTopology
            Effect: Allow
            Action:
              - 'cassandra:GetKeyspace'
              - 'cassandra:GetTable'
              - 'cassandra:ListKeyspaces'
              - 'cassandra:ListTables'
              - 'cassandra:ListTagsForResource'  # this permission will not be most likely used since 333, consider removing it from the template when smartscape poller version 333 is not in production anymore'
            Resource: '*'
          - Sid: TopologyInventory
            Effect: Allow
            Action:
              - 'acm:ListCertificates'
              - 'airflow:GetEnvironment'
              - 'airflow:ListEnvironments'
              - 'apigateway:GetStage'
              - 'appstream:DescribeFleets'
              - 'appstream:ListTagsForResource'
              - 'appsync:ListGraphqlApis'
              - 'backup:GetBackupPlan'
              - 'backup:GetBackupVaultNotifications'
              - 'backup:ListBackupPlans'
              - 'backup:ListBackupVaults'
              - 'codebuild:BatchGetProjects'
              - 'codebuild:ListProjects'
              - 'cognito-idp:DescribeUserPool'
              - 'cognito-idp:ListUserPools'
              - 'connect:DescribeInstance'
              - 'connect:ListInstanceStorageConfigs'
              - 'connect:ListInstances'
              - 'datasync:DescribeTask'
              - 'datasync:ListLocations'
              - 'datasync:ListTasks'
              - 'ec2:DescribeFlowLogs'
              - 'ec2:DescribeIpamPools'
              - 'ec2:DescribeIpamScopes'
              - 'ec2:DescribeIpams'
              - 'elasticmapreduce:DescribeCluster'
              - 'emr-containers:DescribeManagedEndpoint'
              - 'emr-containers:DescribeVirtualCluster'
              - 'emr-serverless:GetApplication'
              - 'events:ListRules'
              - 'execute-api:GetStage'
              - 'fsx:DescribeFileSystems'
              - 'fsx:DescribeStorageVirtualMachines'
              - 'fsx:DescribeVolumes'
              - 'globalaccelerator:DescribeListener'
              - 'globalaccelerator:ListAccelerators'
              - 'globalaccelerator:ListListeners'
              - 'globalaccelerator:ListTagsForResource'
              - 'glue:GetJobs'
              - 'kafka:DescribeClusterV2'
              - 'kafka:DescribeConfiguration'
              - 'kafka:DescribeVpcConnection'
              - 'kafka:ListConfigurations'
              - 'kafka:ListVpcConnections'
              - 'kafkaconnect:DescribeConnector'
              - 'kafkaconnect:ListConnectors'
              - 'kinesis:DescribeStreamSummary'
              - 'kinesis:ListStreams'
              - 'kinesisanalytics:DescribeApplication'
              - 'kinesisanalytics:ListApplications'
              - 'kinesisanalytics:ListTagsForResource' # this permission is not used starting from version 331, consider removing it from the template when smartscape poller version 331 is not in production anymore'
              - 'logs:DescribeDeliveryDestinations'
              - 'network-firewall:DescribeFirewall'
              - 'network-firewall:DescribeFirewallPolicy'
              - 'network-firewall:ListFirewallPolicies'
              - 'network-firewall:ListFirewalls'
              - 'rds:DescribeDBClusterSnapshots'
              - 'rds:DescribeDBClusters'
              - 'rds:DescribeDBInstances'
              - 'rds:DescribeDBSnapshots'
              - 'rds:DescribeDBSubnetGroups'
              - 'redshift-serverless:GetNamespace'
              - 'route53:GetHealthCheck'
              - 'route53resolver:ListResolverEndpoints'
              - 's3:ListBuckets'
              - 'sagemaker:DescribeEndpoint'
              - 'sagemaker:DescribeFeatureGroup'
              - 'sagemaker:DescribeInferenceComponent'
              - 'sagemaker:DescribeLabelingJob'
              - 'sagemaker:DescribePipeline'
              - 'sagemaker:ListEndpoints'
              - 'sagemaker:ListFeatureGroups'
              - 'sagemaker:ListInferenceComponents'
              - 'sagemaker:ListLabelingJobs'
              - 'sagemaker:ListPipelines'
              - 'secretsmanager:ListSecrets'
              - 'ssm:DescribeParameters'
            Resource: '*'

  DynatraceMonitoringRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - 'DynatraceMonitoringRole-${DynatraceEnvironmentId}-${DeploymentHash}'
        - { DeploymentHash: !Select [ 0, !Split [ '-', !Select [ 2, !Split [ '/', !Ref AWS::StackId ] ] ] ], DynatraceEnvironmentId: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ] }
      Description: 'Dynatrace Data Acquisition Monitoring Role'
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS:
                Fn::If:
                  - cProductionEnvironment
                  - '314146291599'
                  - '476114158034'
            Action: 'sts:AssumeRole'
            Condition:
              StringEquals:
                sts:ExternalId: !Ref pRoleExternalId
      ManagedPolicyArns:
        - !Ref DynatraceMonitoringPolicy
        - !Ref DynatraceMonitoringPolicy2
      Tags:
        - Key: 'dt:CreatedBy'
          Value: 'Dynatrace'
        - Key: 'dt:EnvironmentID'
          Value: !Select [ 0, !Split [ ".", !Select [ 1, !Split [ "https://", !Ref pDynatraceUrl ] ] ] ]

Outputs:
  DynatraceMonitoringRoleArn:
    Value: !GetAtt DynatraceMonitoringRole.Arn
