
Dt Obs Aws
- 1.4k installs
- 119 repo stars
- Updated July 29, 2026
- dynatrace/dynatrace-for-ai
dt-obs-aws is an agent skill that analyzes AWS infrastructure inventory, security, capacity, and cost optimization using Dynatrace Smartscape and DQL.
About
The dt-obs-aws skill monitors AWS infrastructure through Dynatrace Smartscape and DQL queries. It covers compute resources like EC2, Lambda, ECS, and EKS, networking with VPC, subnets, security groups, and load balancers, databases including RDS and DynamoDB, storage such as S3 and EBS volumes, and messaging with SQS and SNS. Common tasks include inventory by region, VPC topology mapping, unattached EBS cost savings, publicly accessible database detection, missing Environment tag compliance, subnet IP utilization, and correlating load balancers to target groups and instances. AWS entities expose fields like aws.account.id, aws.region, aws.arn, and aws.vpc.id for filtering. The skill routes problem analysis, workload context, and recent AWS events queries while directing host CPU metrics to dt-obs-hosts, tracing to dt-obs-tracing, and logs to dt-obs-logs. Trigger phrases include show EC2 instances, AWS cost optimization, and security group analysis.
- Queries AWS_* Smartscape entities via Dynatrace DQL inventory patterns.
- Covers EC2, RDS, Lambda, VPC, ELB, S3, DynamoDB, SQS, and SNS resources.
- Supports cost, security, compliance, capacity, and troubleshooting task routes.
- Documents common aws.account.id, aws.region, aws.arn, and vpc fields.
- Routes host, tracing, and log questions to sibling dt-obs skills.
Dt Obs Aws by the numbers
- 1,415 all-time installs (skills.sh)
- +77 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #289 of 1,039 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
dt-obs-aws capabilities & compatibility
- Capabilities
- aws smartscape entity inventory · vpc and load balancer topology analysis · cost and compliance tagging checks · problem and event correlation for aws resources · service specific dql reference routing
- Works with
- aws · datadog
- Use cases
- devops · security audit
What dt-obs-aws says it does
AWS cloud resource monitoring including EC2, RDS, Lambda, ECS/EKS, VPC networking
npx skills add https://github.com/dynatrace/dynatrace-for-ai --skill dt-obs-awsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.4k |
|---|---|
| repo stars | ★ 119 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 29, 2026 |
| Repository | dynatrace/dynatrace-for-ai ↗ |
What AWS resources, risks, or topology details exist in Dynatrace for EC2, RDS, Lambda, VPC, or cost cleanup tasks?
Query Dynatrace Smartscape and DQL for AWS EC2, RDS, Lambda, VPC, cost, and security inventory tasks.
Who is it for?
Operators using Dynatrace to inspect AWS accounts, networking, databases, and cost waste signals.
Skip if: Skip for generic host CPU metrics, application traces, or log-only analysis covered by other dt-obs skills.
When should I use this skill?
User asks to show EC2 instances, find unattached EBS volumes, map VPC resources, or analyze AWS problems in Dynatrace.
What you get
DQL-backed AWS inventory, security findings, capacity notes, or troubleshooting context from Smartscape entities.
- DQL capacity queries
- Regional instance summaries
- Scaling plan inputs
Files
AWS Cloud Infrastructure
Monitor and analyze AWS resources using Dynatrace Smartscape and DQL. Query AWS services, optimize costs, manage security, and plan capacity across your AWS infrastructure.
When to Use This Skill
Use this skill when the user needs to work with AWS resources in Dynatrace. Load the reference file for the task type:
- Inventory: "Show me all EC2 instances in us-east-1"
- Network: "Find all resources in VPC vpc-abc123"
- Database: "List all RDS instances with Multi-AZ enabled"
- Serverless: "Show Lambda functions with VPC access"
- Cost: "Find unattached EBS volumes for cost savings"
- Security: "Identify publicly accessible databases"
- Compliance: "Find resources missing Environment tags"
- Capacity: "Analyze subnet IP utilization"
- Troubleshoot: "Map load balancer to instances through target groups"
- Problem Analysis: "What changed before this AWS problem?" / "What events affected this resource?"
- Workload Context: "Is this instance behind a load balancer, in an EKS cluster, or managed by ECS?"
- Events: "Have there been any recent events in AWS affecting this resource?"
---
Core Concepts
Entity Types
AWS resources use the AWS_* prefix and can be queried using the smartscapeNodes function. All AWS entities are automatically discovered and modeled in Dynatrace Smartscape.
Compute: AWS_EC2_INSTANCE, AWS_LAMBDA_FUNCTION, AWS_ECS_CLUSTER, AWS_ECS_SERVICE, AWS_EKS_CLUSTER Networking: AWS_EC2_VPC, AWS_EC2_SUBNET, AWS_EC2_SECURITYGROUP, AWS_EC2_NATGATEWAY, AWS_EC2_VPCENDPOINT Database: AWS_RDS_DBINSTANCE, AWS_RDS_DBCLUSTER, AWS_DYNAMODB_TABLE, AWS_ELASTICACHE_CACHECLUSTER Storage: AWS_S3_BUCKET, AWS_EC2_VOLUME, AWS_EFS_FILESYSTEM Load Balancing: AWS_ELASTICLOADBALANCINGV2_LOADBALANCER, AWS_ELASTICLOADBALANCINGV2_TARGETGROUP Messaging: AWS_SQS_QUEUE, AWS_SNS_TOPIC, AWS_EVENTS_EVENTBUS, AWS_MSK_CLUSTER
Common AWS Fields
All AWS entities include:
aws.account.id- AWS account identifieraws.region- AWS region (e.g., us-east-1)aws.resource.id- Unique resource identifieraws.resource.name- Resource nameaws.arn- Amazon Resource Nameaws.vpc.id- VPC identifier (for VPC-attached resources)aws.subnet.id- Subnet identifieraws.availability_zone- Availability zoneaws.security_group.id- Security group IDs (array)tags- Resource tags (usetags[TagName])
AWS Fields on Logs and Bizevents
AWS-originated logs (fetch logs) carry these fields — no exploration needed:
aws.region,aws.account.id,aws.service,aws.log_group,aws.log_stream- Plus standard log fields:
content,loglevel,timestamp,k8s.*,dt.smartscape.*
AWS-originated bizevents (fetch bizevents) carry:
aws.region,aws.account.id,event.type,event.provider
Use filter isNotNull(aws.region) to scope to AWS-originated records.
Relationship Types
AWS entities use these relationship types:
is_attached_to- Exclusive attachment (e.g., volume to instance)uses- Dependency relationship (e.g., instance uses security group)runs_on- Vertical relationship (e.g., instance runs on AZ)is_part_of- Composition (e.g., instance in cluster)belongs_to- Aggregation (e.g., service belongs to cluster)balances- Load balancing (e.g., target group balances instances)balanced_by- Inverse load-balancing relationship (e.g., load balancer balanced by target group)
AWS Metric Key Naming Convention
Dynatrace ingests AWS CloudWatch metrics using this pattern:
cloud.aws.<service>.<MetricName>.By.<DimensionName>The <service> is the lowercase AWS service name, <MetricName> is the CloudWatch metric name (case-preserved), and <DimensionName> is the CloudWatch dimension.
Examples: cloud.aws.ec2.CPUUtilization.By.InstanceId, cloud.aws.lambda.Invocations.By.FunctionName, cloud.aws.rds.CPUUtilization.By.DBInstanceIdentifier
Use timeseries, not fetch, for these metrics. Group by dt.smartscape_source.id to split by entity.
→ See references/metrics-performance.md for the complete metric catalog by service with DQL query templates.
---
Key Workflows
1. AWS Resource Discovery
Get all AWS resources by type:
smartscapeNodes "AWS_*"
| summarize count = count(), by: {type}
| sort count descFilter by account and region:
smartscapeNodes "AWS_*"
| filter aws.account.id == "123456789012" and aws.region == "us-east-1"
| fields type, name, aws.resource.idUsing tags for filtering:
smartscapeNodes "AWS_*"
| filter tags[Environment] == "production"
| summarize count = count(), by: {type, aws.region}→ For complete resource inventory patterns, see references/resource-management.md
2. VPC Networking Analysis
List all VPCs:
smartscapeNodes "AWS_EC2_VPC"
| fields name, aws.account.id, aws.region, aws.vpc.idFind resources in a VPC:
smartscapeNodes "AWS_*"
| filter aws.vpc.id == "vpc-0be61db7c5d2d1bd1"
| summarize resource_count = count(), by: {type, aws.subnet.id}
| sort resource_count descAnalyze security group usage:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter contains(aws.security_group.id, "sg-abc123")
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.id→ For VPC networking, see references/vpc-networking-security.md → For security group patterns, see references/security-compliance.md
3. Database Monitoring
List all RDS instances:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| fields name, aws.account.id, aws.region, aws.vpc.id, aws.availability_zoneFind Multi-AZ databases:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd multiAZ = awsjson[configuration][multiAZ]
| filter multiAZ == true
| fields name, aws.resource.id, aws.regionGroup by engine type:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd engine = awsjson[configuration][engine]
| summarize db_count = count(), by: {engine, aws.region}
| sort db_count desc→ For database monitoring, see references/database-monitoring.md
4. Serverless and Container Workloads
List Lambda functions:
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| fields name, aws.account.id, aws.region, aws.vpc.idFind ECS services in a cluster:
smartscapeNodes "AWS_ECS_SERVICE"
| traverse "belongs_to", "AWS_ECS_CLUSTER"
| fields name, aws.resource.id, aws.regionList EKS clusters:
smartscapeNodes "AWS_EKS_CLUSTER"
| fields name, aws.account.id, aws.region, aws.vpc.id→ For serverless, see references/serverless-containers.md → For containers, see references/serverless-containers.md
5. Load Balancer Topology
Complete load balancer to instance mapping:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| parse aws.object, "JSON:awsjson"
| fieldsAdd dnsName = awsjson[configuration][dnsName], scheme = awsjson[configuration][scheme]
| filter scheme == "internet-facing"
| traverse "balanced_by", "AWS_ELASTICLOADBALANCINGV2_TARGETGROUP", direction:backward, fieldsKeep:{dnsName, id}
| fieldsAdd targetGroupName = aws.resource.name
| traverse "balances", "AWS_EC2_INSTANCE", fieldsKeep: {targetGroupName, id}
| fieldsAdd loadBalancerDnsName = dt.traverse.history[-2][dnsName],
loadBalancerId = dt.traverse.history[-2][id],
targetGroupId = dt.traverse.history[-1][id]→ For load balancing, see references/load-balancing-api.md
6. Cost Optimization
Find unattached EBS volumes:
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd state = awsjson[configuration][state]
| filter state == "available"
| fields name, aws.resource.id, aws.availability_zone, aws.account.idAnalyze EBS costs by type:
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd volumeType = awsjson[configuration][volumeType],
size = awsjson[configuration][size],
state = awsjson[configuration][state]
| summarize total_volumes = count(), total_size_gb = sum(size), by: {volumeType, state}
| sort total_size_gb desc→ For cost optimization, see references/cost-optimization.md
7. Security and Compliance
Find publicly accessible databases:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd publiclyAccessible = awsjson[configuration][publiclyAccessible]
| filter publiclyAccessible == true
| fields name, aws.resource.id, aws.vpc.id, aws.account.idSecurity group blast radius:
smartscapeNodes "AWS_EC2_INSTANCE"
| traverse "uses", "AWS_EC2_SECURITYGROUP"
| summarize instance_count = count(), by: {aws.resource.name, aws.vpc.id}
| sort instance_count desc
| limit 20→ For security, see references/security-compliance.md
8. Resource Ownership and Tagging
Find untagged resources:
smartscapeNodes "AWS_*"
| filter isNull(tags)
| fields type, name, aws.resource.id, aws.account.id, aws.regionCost allocation by cost center:
smartscapeNodes "AWS_*"
| filter isNotNull(tags[CostCenter])
| summarize resource_count = count(), by: {tags[CostCenter], type}
| sort resource_count desc→ For resource ownership, see references/resource-ownership.md
---
Common Query Patterns
| Pattern | Template |
|---|---|
| Discovery | `smartscapeNodes "AWS_*" \ |
| Config parsing | `smartscapeNodes "AWS_<T>" \ |
| Traversal | `smartscapeNodes "AWS_<SRC>" \ |
| Multi-type | `smartscapeNodes "AWS_T1", "AWS_T2" \ |
---
Best Practices
Query Optimization
1. Filter early by account and region 2. Use specific entity types (avoid "AWS_*" wildcards when possible) 3. Limit results with | limit N for exploration 4. Use isNotNull() checks before accessing nested fields
Configuration Parsing
1. Always parse aws.object with JSON parser: parse aws.object, "JSON:awsjson" 2. Use consistent field naming: fieldsAdd configField = awsjson[configuration][field] 3. Check for null values after parsing 4. Use toString() for complex nested objects
Security Fields
1. Security group IDs are arrays - use contains() or expand 2. Parse aws.object for detailed security context 3. Check publiclyAccessible, storageEncrypted, and similar flags 4. Validate IAM role assumptions
Tagging Strategy
1. Use tags[TagName] for filtering by specific tag value 2. tags is a JSON object, not an array — use isNull(tags) for untagged resources, never arraySize(tags) 3. Use isNull(tags[TagName]) to find resources missing a specific tag 4. Implement consistent tag naming conventions 5. Track tag coverage with summarize operations
---
Limitations and Notes
Smartscape Limitations
- AWS object configuration requires parsing with
parse aws.object, "JSON:awsjson" - AWS metrics are available as Dynatrace metrics using the
cloud.aws.*naming convention (see AWS Metric Naming Convention) - Resource discovery depends on AWS integration configuration
- Tag synchronization may have slight delays
Relationship Traversal
- Use
direction:backwardfor reverse relationships (e.g., target group → load balancer) - Use
fieldsKeepto maintain important fields through traversal - Access traversal history with
dt.traverse.history[-N] - Complex topologies may require multiple traverse operations
General Tips
- Use
getNodeName()for human-readable resource names - Handle null values gracefully with
isNotNull()andisNull() - Combine region and account filters for large environments
- Use
countDistinct()for unique resource counts
---
When to Load References
This skill uses progressive disclosure. Start here for 80% of use cases. Load reference files for detailed specifications when needed.
Load vpc-networking-security.md when:
- Analyzing VPC topology and connectivity
- Investigating security group configurations
- Finding resources by security group
- Troubleshooting network interface issues
Load database-monitoring.md when:
- Managing RDS instances and clusters
- Analyzing database engine distributions
- Checking Multi-AZ configurations
- Monitoring cache clusters
Load serverless-containers.md when:
- Working with Lambda functions
- Analyzing ECS/EKS deployments
- Investigating container networking
- Planning serverless migrations
Load load-balancing-api.md when:
- Mapping load balancer topologies
- Analyzing target group health
- Working with API Gateway
- Configuring CloudFront
Load messaging-event-streaming.md when:
- Managing SQS queues and SNS topics
- Analyzing EventBridge event buses
- Working with Kinesis or MSK
- Monitoring Step Functions
Load resource-management.md when:
- Conducting resource audits
- Analyzing tag compliance
- Finding unattached resources
- Planning regional distribution
Load cost-optimization.md when:
- Identifying cost savings opportunities
- Analyzing storage costs
- Finding unused resources
- Optimizing instance types
Load capacity-planning.md when:
- Planning capacity expansions
- Analyzing resource utilization
- Monitoring subnet IP usage
- Sizing auto-scaling groups
Load security-compliance.md when:
- Conducting security audits
- Checking encryption status
- Analyzing IAM roles
- Finding public resources
Load resource-ownership.md when:
- Implementing chargeback
- Tracking resource ownership
- Allocating costs by team
- Managing multi-account environments
Load events.md when:
- Investigating what changed before or during a problem
- Checking for recent CloudFormation stack deployments
- Reviewing AWS Auto Scaling activity (scale-in/scale-out)
- Checking AWS Health service events affecting a resource
Load workload-detection.md when:
- Determining how an EC2 instance is orchestrated (ECS, EKS, Batch, ASG, standalone)
- Following a resolution path that depends on the workload pattern
- Understanding the blast radius of an instance failure
Check health alerts when:
- Verifying whether Dynatrace health alerts are configured for an AWS resource type
- Confirming alert coverage before or after a problem
Use dtctl to query the builtin:health-experience.cloud-alert settings schema. Replace CpuUtilization with the metric name relevant to the resource type being investigated:
dtctl get settings --schema builtin:health-experience.cloud-alert -o json --plain \
| jq '[.[] | select(.value.alertKey | test("CpuUtilization"))]'---
References
- vpc-networking-security.md - VPC infrastructure, security groups, and network connectivity
- database-monitoring.md - RDS, DynamoDB, ElastiCache, and Redshift monitoring
- serverless-containers.md - Lambda, ECS, EKS, and App Runner workloads
- load-balancing-api.md - Load balancers, API Gateway, and CloudFront
- messaging-event-streaming.md - SQS, SNS, EventBridge, Kinesis, and MSK
- resource-management.md - Resource inventory and lifecycle management
- cost-optimization.md - Cost savings and spending optimization
- capacity-planning.md - Capacity analysis and growth planning
- security-compliance.md - Security configurations and compliance monitoring
- resource-ownership.md - Cost allocation and ownership tracking
- events.md - AWS AutoScaling, Health, and CloudFormation events for problem timeline analysis
- workload-detection.md - Identify how an EC2 instance is orchestrated (LB, ASG, ECS, EKS, Batch)
---
AWS Capacity Planning
Analyze resource capacity and plan for growth.
Table of Contents
- Compute Capacity
- Network Capacity
- Container & Serverless Capacity
- Database & Storage Capacity
- Infrastructure Capacity
Compute Capacity
Instance type distribution across regions:
smartscapeNodes "AWS_EC2_INSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd instanceType = awsjson[configuration][instanceType],
state = awsjson[configuration][state][name]
| summarize instance_count = count(), by: {instanceType, state, aws.region}
| sort instance_count descAuto Scaling group capacity and headroom:
smartscapeNodes "AWS_AUTOSCALING_AUTOSCALINGGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd minSize = awsjson[configuration][minSize],
maxSize = awsjson[configuration][maxSize],
desiredCapacity = awsjson[configuration][desiredCapacity]
| fields name, minSize, maxSize, desiredCapacity, aws.region
| sort desiredCapacity descNetwork Capacity
Subnet IP address utilization (critical for capacity planning):
smartscapeNodes "AWS_EC2_SUBNET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd availableIpCount = awsjson[configuration][availableIpAddressCount],
cidrBlock = awsjson[configuration][cidrBlock]
| fields name, cidrBlock, availableIpCount, aws.vpc.id, aws.availability_zone
| sort availableIpCount ascNetwork interface usage by type:
smartscapeNodes "AWS_EC2_NETWORKINTERFACE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd interfaceType = awsjson[configuration][interfaceType],
status = awsjson[configuration][status]
| summarize eni_count = count(), by: {interfaceType, status}
| sort eni_count descRoute tables per VPC:
smartscapeNodes "AWS_EC2_ROUTETABLE"
| summarize route_table_count = count(), by: {aws.vpc.id, aws.region}
| sort route_table_count descContainer & Serverless Capacity
ECS service desired vs running counts:
smartscapeNodes "AWS_ECS_SERVICE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd desiredCount = awsjson[configuration][desiredCount],
runningCount = awsjson[configuration][runningCount]
| fields name, desiredCount, runningCount, aws.regionLambda function memory allocations:
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| parse aws.object, "JSON:awsjson"
| fieldsAdd memory = awsjson[configuration][memorySize]
| summarize function_count = count(), by: {memory, aws.region}
| sort memory descEKS node groups, ECR repositories, and launch templates can be counted with the standard discovery pattern using their respective entity types: AWS_EKS_NODEGROUP, AWS_ECR_REPOSITORY, AWS_EC2_LAUNCHTEMPLATE.
Database & Storage Capacity
RDS storage type distribution:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd storageType = awsjson[configuration][storageType]
| summarize db_count = count(), by: {storageType, aws.region}
| sort db_count descInfrastructure Capacity
Transit gateways for multi-VPC connectivity:
smartscapeNodes "AWS_EC2_TRANSITGATEWAY"
| fields name, aws.account.id, aws.region, aws.resource.idAWS Cost Optimization
Identify cost savings opportunities and optimize AWS spending.
Table of Contents
- Compute Costs
- Storage Costs
- Network Costs
- Database Costs
- Serverless & Cache Costs
- Infrastructure Management Costs
- Idle Resource Detection
Compute Costs
Analyze running instance types for right-sizing:
smartscapeNodes "AWS_EC2_INSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd instanceType = awsjson[configuration][instanceType],
state = awsjson[configuration][state][name]
| filter state == "running"
| summarize instance_count = count(), by: {instanceType, aws.region}
| sort instance_count descFind recently terminated instances:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter aws.state == "terminated"
| fields name, aws.resource.id, aws.region, aws.account.id, id
| limit 20Storage Costs
Analyze EBS volumes by type and state (identify unattached volumes):
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd volumeType = awsjson[configuration][volumeType],
size = awsjson[configuration][size],
state = awsjson[configuration][state]
| summarize total_volumes = count(), total_size_gb = sum(size), by: {volumeType, state}
| sort total_size_gb descCheck S3 bucket versioning for storage cost analysis:
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd versioning = awsjson[configuration][versioningConfiguration][status]
| summarize bucket_count = count(), by: {versioning, aws.region}Count RDS cluster snapshots for backup cost analysis:
smartscapeNodes "AWS_RDS_DBCLUSTERSNAPSHOT"
| parse aws.object, "JSON:awsjson"
| fieldsAdd snapshotType = awsjson[configuration][snapshotType]
| summarize snapshot_count = count(), by: {snapshotType, aws.region}
| sort snapshot_count descNetwork Costs
Analyze NAT gateway costs by VPC:
smartscapeNodes "AWS_EC2_NATGATEWAY"
| parse aws.object, "JSON:awsjson"
| fieldsAdd state = awsjson[configuration][state]
| filter state == "available"
| summarize nat_count = count(), by: {aws.vpc.id, aws.availability_zone}
| sort nat_count descAnalyze VPC endpoint types for cost optimization:
smartscapeNodes "AWS_EC2_VPCENDPOINT"
| parse aws.object, "JSON:awsjson"
| fieldsAdd vpcEndpointType = awsjson[configuration][vpcEndpointType],
serviceName = awsjson[configuration][serviceName]
| summarize endpoint_count = count(), by: {vpcEndpointType, serviceName, aws.vpc.id}
| sort endpoint_count descDatabase Costs
Analyze RDS instance costs by class:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd instanceClass = awsjson[configuration][dbInstanceClass]
| summarize db_count = count(), by: {instanceClass, aws.region}
| sort db_count descServerless & Cache Costs
Identify Lambda runtime distribution (for upgrade planning):
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| parse aws.object, "JSON:awsjson"
| fieldsAdd runtime = awsjson[configuration][runtime]
| summarize function_count = count(), by: {runtime, aws.region}
| sort function_count descReview ElastiCache node types:
smartscapeNodes "AWS_ELASTICACHE_REPLICATIONGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd nodeType = awsjson[configuration][cacheNodeType]
| summarize cluster_count = count(), by: {nodeType, aws.region}
| sort cluster_count descInfrastructure Management Costs
Find KMS keys pending deletion:
smartscapeNodes "AWS_KMS_KEY"
| parse aws.object, "JSON:awsjson"
| fieldsAdd keyState = awsjson[configuration][keyState]
| filter keyState == "PendingDeletion"
| fields name, aws.resource.id, aws.region, aws.account.idReview CloudFormation stack states:
smartscapeNodes "AWS_CLOUDFORMATION_STACK"
| parse aws.object, "JSON:awsjson"
| fieldsAdd stackStatus = awsjson[configuration][stackStatus]
| summarize stack_count = count(), by: {stackStatus, aws.region}
| sort stack_count descIdle Resource Detection
Idle resources are running and incurring charges but performing no useful work. Detecting them is one of the highest-impact cost optimization activities because idle resources represent pure waste — they can be terminated, downsized, or cleaned up with no service impact.
The queries below use metric thresholds and entity state to surface idle candidates across common AWS resource types.
EC2 — Low CPU Utilization
Instances averaging less than 5% CPU over 14 days are likely idle or significantly over-provisioned. These are candidates for downsizing to a smaller instance type or termination if no longer needed.
timeseries avg_cpu = avg(cloud.aws.ec2.CPUUtilization.By.InstanceId),
by: { dt.smartscape_source.id },
from: now()-14d
| filter isNotNull(avg_cpu)
| fieldsAdd avg_cpu_val = arrayAvg(avg_cpu)
| filter avg_cpu_val < 5.0
| sort avg_cpu_val asc
| fields dt.smartscape_source.id, avg_cpu_valWhat to look for:
- Instances at 0% CPU are almost certainly unused and safe to terminate after confirming no attached services depend on them.
- Instances between 1–5% may be running scheduled jobs or health checks — verify with network I/O metrics before acting.
- Cross-reference with the Compute Costs query to see the instance types involved; large instance types with low CPU yield the biggest savings.
Lambda — Zero Invocations
Functions with zero invocations over 30 days are likely abandoned or replaced. They still consume storage for deployment packages and may hold reserved concurrency that blocks other functions.
timeseries total_invocations = sum(cloud.aws.lambda.Invocations.By.FunctionName),
by: { dt.smartscape_source.id },
from: now()-30d
| fieldsAdd total = arraySum(total_invocations)
| filter total == 0 or isNull(total)
| fields dt.smartscape_source.id, totalWhat to look for:
- Functions with zero invocations for 30 days are strong candidates for deletion.
- Check whether the function is triggered by a schedule that runs less frequently than 30 days (e.g., quarterly reports) before removing.
- Review CloudFormation or IaC ownership to avoid deleting functions that would be recreated on the next deployment.
DynamoDB — Zero Consumed Capacity
Tables with no read or write activity over 30 days are likely unused. DynamoDB tables in provisioned mode incur charges for allocated capacity even with zero traffic.
timeseries {
reads = sum(cloud.aws.dynamodb.ConsumedReadCapacityUnits.By.TableName),
writes = sum(cloud.aws.dynamodb.ConsumedWriteCapacityUnits.By.TableName)
},
by: { dt.smartscape_source.id },
from: now()-30d
| fieldsAdd total_reads = arraySum(reads), total_writes = arraySum(writes)
| filter (total_reads == 0 or isNull(total_reads)) and (total_writes == 0 or isNull(total_writes))
| fields dt.smartscape_source.id, total_reads, total_writesWhat to look for:
- Tables in provisioned mode with zero consumed capacity are paying for unused read/write units — switch to on-demand or delete.
- Tables in on-demand mode with zero traffic have minimal cost but still incur storage charges if they hold data.
- Check for DynamoDB Streams or global table replicas that may justify keeping the table even without direct application traffic.
EBS — Unattached Volumes
Volumes in the available state are not attached to any instance. They still incur storage charges based on size and volume type.
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd volumeType = awsjson[configuration][volumeType],
size = awsjson[configuration][size],
state = awsjson[configuration][state]
| filter state == "available"
| fields name, aws.resource.id, aws.region, volumeType, size
| sort size descWhat to look for:
- Large
gp3orio2volumes inavailablestate are the most expensive idle resources — prioritize these. - Volumes left behind after instance termination are a common source of waste. Create a snapshot before deleting if the data may be needed.
- Cross-reference with snapshots to confirm backup exists before cleanup.
EBS — Attached but Zero I/O
Volumes that are attached to instances but have had no read or write operations over 14 days. These may be leftover data volumes or misconfigured mounts.
timeseries {
reads = sum(cloud.aws.ebs.VolumeReadOps.By.VolumeId),
writes = sum(cloud.aws.ebs.VolumeWriteOps.By.VolumeId)
},
by: { dt.smartscape_source.id },
from: now()-14d
| fieldsAdd total_reads = arraySum(reads), total_writes = arraySum(writes)
| filter (total_reads == 0 or isNull(total_reads)) and (total_writes == 0 or isNull(total_writes))
| fields dt.smartscape_source.id, total_reads, total_writesWhat to look for:
- Volumes with zero I/O that are attached to running instances may be mounted but unused — check the instance OS for unmounted or orphaned block devices.
- Some volumes serve as infrequently-accessed archives; confirm the access pattern before detaching.
- Detaching and snapshotting zero-I/O volumes can recover ongoing storage costs while preserving data.
Summary
| Resource | Idle Signal | Lookback | Recommended Action |
|---|---|---|---|
| EC2 Instance | Avg CPU < 5% | 14 days | Downsize instance type or terminate |
| Lambda Function | Zero invocations | 30 days | Delete function and deployment package |
| DynamoDB Table | Zero consumed read/write capacity | 30 days | Switch to on-demand or delete table |
| EBS Volume | State is available (unattached) | Current | Snapshot and delete |
| EBS Volume | Zero read/write ops (attached) | 14 days | Detach, snapshot, and delete |
AWS Database Monitoring
Monitor and analyze AWS database services including RDS, DynamoDB, ElastiCache, and Redshift.
Table of Contents
Database Entity Types
All these types support the standard discovery pattern: smartscapeNodes "<TYPE>" | fields name, aws.account.id, aws.region, ...
| Entity type | Description |
|---|---|
AWS_RDS_DBINSTANCE | RDS database instances |
AWS_RDS_DBCLUSTER | RDS Aurora clusters |
AWS_RDS_DBSUBNETGROUP | RDS subnet groups |
AWS_RDS_OPTIONGROUP | RDS option groups |
AWS_RDS_DBCLUSTERSNAPSHOT | RDS cluster snapshots |
AWS_DYNAMODB_TABLE | DynamoDB tables |
AWS_ELASTICACHE_CACHECLUSTER | ElastiCache clusters |
AWS_ELASTICACHE_SUBNETGROUP | ElastiCache subnet groups |
AWS_REDSHIFT_CLUSTER | Redshift clusters |
AWS_REDSHIFTSERVERLESS_WORKGROUP | Redshift Serverless workgroups |
RDS Monitoring
Find Multi-AZ databases:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd multiAZ = awsjson[configuration][multiAZ]
| filter multiAZ == true
| fields name, aws.resource.id, aws.region, aws.availability_zoneAnalyze by engine type:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd engine = awsjson[configuration][engine]
| summarize db_count = count(), by: {engine, aws.region}
| sort db_count descFind RDS cluster members (instance → cluster relationship):
smartscapeNodes "AWS_RDS_DBINSTANCE"
| traverse "is_part_of", "AWS_RDS_DBCLUSTER"
| fields name, aws.resource.id, aws.regionOther Database Services
Find ElastiCache clusters by engine:
smartscapeNodes "AWS_ELASTICACHE_CACHECLUSTER"
| parse aws.object, "JSON:awsjson"
| fieldsAdd engine = awsjson[configuration][engine]
| summarize cluster_count = count(), by: {engine, aws.region}Database Security
For public access detection on RDS databases, see the Public Access Detection section in security-compliance.md.
Analyze database security groups:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| fields name, aws.resource.id, aws.vpc.id, aws.security_group.id
| expand aws.security_group.idFind what security groups a specific database uses:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| filter aws.resource.id == "<AWS_RDS_DBINSTANCE_ID>"
| traverse "uses", "AWS_EC2_SECURITYGROUP"
| fields name, aws.resource.idCross-Service Analysis
Find all databases in a specific VPC:
smartscapeNodes "AWS_RDS_DBINSTANCE", "AWS_ELASTICACHE_CACHECLUSTER", "AWS_REDSHIFT_CLUSTER"
| filter aws.vpc.id == "<VPC_ID>"
| fields type, name, aws.resource.id, aws.subnet.idCount databases across regions:
smartscapeNodes "AWS_RDS_DBINSTANCE", "AWS_DYNAMODB_TABLE", "AWS_ELASTICACHE_CACHECLUSTER"
| summarize db_count = count(), by: {type, aws.region}
| sort aws.region, db_count descAWS Events Reference
Event queries for problem timeline analysis. Use these during incident investigation to determine what changed before or during a problem on an AWS resource.
Placeholder Reference
| Placeholder | Description |
|---|---|
<PROBLEM_START> | Problem start timestamp (e.g., now()-2h) |
<PROBLEM_END> | Problem end timestamp (e.g., now()) |
<ROOT_CAUSE_ENTITY_ID> | Dynatrace entity ID of the affected resource (e.g., AWS_EC2_INSTANCE-ABC123) |
<AWS_INSTANCE_ID> | AWS resource ID of the affected resource (e.g., i-0abc1234def56789) |
<AWS_RESOURCE_NAME> | AWS resource name or logical resource ID (e.g., my-web-server-asg) |
<CLOUD_ALERT_EVENT_TYPE> | Davis event type to filter (e.g., RESOURCE_CONTENTION_EVENT, AVAILABILITY_EVENT) |
---
Auto Scaling Events
List recent Auto Scaling activity. Run this first during any EC2 instance problem to detect scale-in/scale-out events, lifecycle hooks, or capacity changes that may have caused or contributed to the issue.
fetch events
| filter source == "aws.autoscaling"
| fields timestamp, event.type, event.name, data
| sort timestamp desc
| limit 50Note: This query returns the most recent 50 events globally. For incident-scoped analysis, add a time range:
fetch events, from: <PROBLEM_START>, to: <PROBLEM_END>
| filter source == "aws.autoscaling"
| fields timestamp, event.type, event.name, data
| sort timestamp desc---
AWS Health Events
Query for AWS Health service events affecting the specific resource. AWS Health events indicate service disruptions, scheduled maintenance, or account-level notifications from AWS.
fetch events, from: <PROBLEM_START - 1h>, to: <PROBLEM_END + 1h>
| filter source == "aws.health"
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")
| fieldsAdd event.description = jsonData[`eventDescription`][0][`latestDescription`]
| fieldsAdd event.name = jsonData[`eventTypeCode`]
| fieldsAdd event.category = jsonData[actionability]
| fieldsAdd affected_entity_ids = dt.smartscape_source.id
| fields timestamp, event.name, event.description, event.category, affected_entity_ids
| sort timestamp descWhat to look for:
event.category != "INFORMATIONAL"— active service disruption from AWS or planned maintenance that may be impacting your resource
---
CloudFormation Events
Check for recent CloudFormation stack deployments or changes. Infrastructure changes via CloudFormation are a common cause of problems — correlate stack events with the problem timeline.
fetch events, from: <PROBLEM_START - 1h>, to: <PROBLEM_END + 1h>
| filter source == "aws.cloudformation"
| parse data, "JSON:jsonData"
| fieldsAdd event.name = jsonData[eventName]
| fieldsAdd event.errorCode = jsonData[errorCode]
| fieldsAdd event.errorMessage = jsonData[errorMessage]
| fieldsAdd event.status = jsonData[`status-details`][status]
| fields jsonData, event.name, event.errorCode, event.status
| limit 20Check for CloudFormation events related to the specific resource:
fetch events
| filter source == "aws.cloudformation"
| parse data, "JSON:jsonData"
| fieldsAdd event.name = jsonData[eventName]
| fieldsAdd event.errorCode = jsonData[errorCode]
| fieldsAdd event.errorMessage = jsonData[errorMessage]
| fieldsAdd event.status = jsonData[`status-details`][status]
| filter jsonData[`logical-resource-id`] == "<AWS_RESOURCE_NAME>"
| fields jsonData, event.name, event.errorCode, event.status, id, data
| limit 20What to look for:
- Stack updates that completed shortly before the problem started
- Failed stack operations that may have left resources in a degraded state
- Resource replacements (e.g., instance replaced due to a launch template change)
---
CloudTrail API Events
Query AWS CloudTrail events to audit API calls. Use this during security investigations or to correlate infrastructure changes with problems.
fetch events
| filter event.type == "AWS API Call via CloudTrail"
| fields timestamp, event.type, data
| sort timestamp desc
| limit 50Scope to a problem time window for incident analysis:
fetch events, from: <PROBLEM_START - 1h>, to: <PROBLEM_END + 1h>
| filter event.type == "AWS API Call via CloudTrail"
| fields timestamp, event.type, data
| sort timestamp descWhat to look for:
- API calls that modify infrastructure (RunInstances, TerminateInstances, ModifyDBInstance, etc.)
- API calls from unexpected IAM users or roles
- Failed API calls (error codes) that might indicate permission issues
---
EC2 Instance State Changes
Track EC2 instance launches and terminations. These events correlate with Auto Scaling activity, spot instance interruptions, or manual instance management.
fetch events
| filter event.type == "EC2 Instance Launch Successful"
or event.type == "EC2 Instance Terminate Successful"
| fields timestamp, event.type, data
| sort timestamp desc
| limit 50Scope to a problem time window:
fetch events, from: <PROBLEM_START - 1h>, to: <PROBLEM_END + 1h>
| filter event.type == "EC2 Instance Launch Successful"
or event.type == "EC2 Instance Terminate Successful"
| fields timestamp, event.type, data
| sort timestamp descWhat to look for:
- Instance terminations shortly before a problem (capacity reduction)
- Rapid launch/terminate cycles (instance instability)
- Launches in unexpected regions or availability zones
---
Cloud Alert Events (Davis)
Davis automatically detects anomalies on AWS resources monitored through the cloud integration. These events cover resource contention, availability issues, performance degradation, and errors.
fetch events
| filter event.provider == "CLOUD_ALERT" and event.kind == "DAVIS_EVENT"
| fields timestamp, event.type, event.name, dt.smartscape_source.id, aws.resource.name, data
| sort timestamp desc
| limit 50Filter by a specific event type:
fetch events
| filter event.provider == "CLOUD_ALERT" and event.kind == "DAVIS_EVENT"
| filter event.type == "<CLOUD_ALERT_EVENT_TYPE>"
| fields timestamp, event.type, event.name, dt.smartscape_source.id, aws.resource.name, data
| sort timestamp desc
| limit 50Scope to a specific affected entity:
fetch events, from: <PROBLEM_START - 1h>, to: <PROBLEM_END + 1h>
| filter event.provider == "CLOUD_ALERT" and event.kind == "DAVIS_EVENT"
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")
| fields timestamp, event.type, event.name, data
| sort timestamp descWhat to look for:
- RESOURCE_CONTENTION_EVENT — CPU, memory, or I/O saturation
- AVAILABILITY_EVENT — service or resource unreachable
- PERFORMANCE_EVENT — latency or throughput degradation
- ERROR_EVENT — error rate anomalies
---
Event Discovery
Use this query to discover all available event sources and types in your environment. This helps identify which AWS services are forwarding events to Dynatrace.
fetch events, from:-30d
| summarize count = count(), by: {event.kind, event.type, event.provider}
| sort count desc
| limit 50What to look for:
- New event sources that have started reporting recently
- Event types with high volume that may indicate recurring issues
- Missing event sources that should be configured but are not present
AWS Load Balancing & API Management
Monitor Application/Network Load Balancers, API Gateway, and CloudFront distributions.
Table of Contents
- Load Balancing Entity Types
- Load Balancer Topology Traversal
- Load Balancer Configuration
- Security & Networking
- API Gateway
- Cross-Service Analysis
Load Balancing Entity Types
All these types support the standard discovery pattern: smartscapeNodes "<TYPE>" | fields name, aws.account.id, aws.region, ...
| Entity type | Description |
|---|---|
AWS_ELASTICLOADBALANCINGV2_LOADBALANCER | ALB, NLB, GLB (modern) |
AWS_ELASTICLOADBALANCING_LOADBALANCER | Classic ELB |
AWS_ELASTICLOADBALANCINGV2_TARGETGROUP | Target groups |
AWS_ELASTICLOADBALANCINGV2_LISTENER | LB listeners |
AWS_APIGATEWAY_RESTAPI | REST APIs |
AWS_APIGATEWAY_STAGE | REST API stages |
AWS_APIGATEWAYV2_API | HTTP/WebSocket APIs |
AWS_APIGATEWAYV2_STAGE | V2 API stages |
AWS_CLOUDFRONT_DISTRIBUTION | CloudFront CDN distributions |
Load Balancer Topology Traversal
Complete LB → Target Group → Instance Mapping
This is the most important query — maps internet-facing load balancers through target groups to backend instances:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| parse aws.object, "JSON:awsjson"
| fieldsAdd dnsName = awsjson[configuration][dnsName], scheme = awsjson[configuration][scheme]
| traverse "balanced_by", "AWS_ELASTICLOADBALANCINGV2_TARGETGROUP", direction:backward, fieldsKeep:{dnsName, id}
| fieldsAdd targetGroupName = aws.resource.name
| traverse "balances", "AWS_EC2_INSTANCE", fieldsKeep: {targetGroupName, id}
| fieldsAdd loadBalancerDnsName = dt.traverse.history[-2][dnsName],
loadBalancerId = dt.traverse.history[-2][id],
targetGroupId = dt.traverse.history[-1][id]Simpler Traversals
LB to target groups:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| traverse "balanced_by", "AWS_ELASTICLOADBALANCINGV2_TARGETGROUP", direction:backward
| fields name, aws.resource.idTarget groups to instances:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_TARGETGROUP"
| traverse "balances", "AWS_EC2_INSTANCE"
| fields name, aws.resource.id, aws.availability_zoneLoad Balancer Configuration
Find internet-facing load balancers:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| parse aws.object, "JSON:awsjson"
| fieldsAdd scheme = awsjson[configuration][scheme], dnsName = awsjson[configuration][dnsName]
| filter scheme == "internet-facing"
| fields name, dnsName, aws.resource.id, aws.vpc.idCheck multi-AZ distribution:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| fields name, aws.resource.id, aws.availability_zone
| expand aws.availability_zoneSecurity & Networking
List security groups attached to load balancers:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| fields name, aws.resource.id, aws.security_group.id
| expand aws.security_group.idFilter LBs by VPC:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| filter aws.vpc.id == "<VPC_ID>"
| fields name, aws.resource.id, aws.subnet.idAPI Gateway
Count APIs across regions by type:
smartscapeNodes "AWS_APIGATEWAY_RESTAPI", "AWS_APIGATEWAYV2_API"
| summarize api_count = count(), by: {type, aws.region}
| sort api_count descCross-Service Analysis
Count all load balancers by type and region:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER", "AWS_ELASTICLOADBALANCING_LOADBALANCER"
| summarize lb_count = count(), by: {type, aws.region}
| sort lb_count descAWS Messaging & Event Streaming
Monitor SQS queues, SNS topics, EventBridge, Kinesis, and MSK clusters.
Table of Contents
- Messaging Entity Types
- Service-Specific Queries
- Name Pattern Matching
- MSK Multi-AZ Distribution
- Cross-Service Analysis
Messaging Entity Types
All these types support the standard discovery pattern: smartscapeNodes "<TYPE>" | fields name, aws.account.id, aws.region, aws.resource.id
| Entity type | Description |
|---|---|
AWS_SQS_QUEUE | SQS queues |
AWS_SNS_TOPIC | SNS topics |
AWS_EVENTS_EVENTBUS | EventBridge event buses |
AWS_KINESISFIREHOSE_DELIVERYSTREAM | Kinesis Firehose delivery streams |
AWS_MSK_CLUSTER | Managed Streaming for Kafka clusters |
AWS_STEPFUNCTIONS_STATEMACHINE | Step Functions state machines |
To filter any type by tag, region, or account, apply standard filters: | filter tags[Environment] == "production" or | filter aws.account.id == "<AWS_ACCOUNT_ID>".
To summarize by region: | summarize count = count(), by: {aws.region}.
Service-Specific Queries
Filter for non-default EventBridge event buses:
smartscapeNodes "AWS_EVENTS_EVENTBUS"
| filter name != "default"
| fields name, aws.resource.id, aws.regionFind MSK clusters in a specific VPC:
smartscapeNodes "AWS_MSK_CLUSTER"
| filter aws.vpc.id == "<VPC_ID>"
| fields name, aws.resource.id, aws.subnet.idName Pattern Matching
Find queues or topics by name pattern using matchesPhrase:
smartscapeNodes "AWS_SQS_QUEUE"
| filter contains(name, "Sqs")
| fields name, aws.resource.id, aws.regionReplace "AWS_SQS_QUEUE" with "AWS_SNS_TOPIC" or any other type. Replace "orders" with the relevant pattern.
MSK Multi-AZ Distribution
Check Kafka cluster availability zone distribution:
smartscapeNodes "AWS_MSK_CLUSTER"
| fields name, aws.resource.id, aws.availability_zone, aws.vpc.id
| expand aws.availability_zoneCross-Service Analysis
Count all messaging resources by type:
smartscapeNodes "AWS_SQS_QUEUE", "AWS_SNS_TOPIC", "AWS_EVENTS_EVENTBUS", "AWS_KINESISFIREHOSE_DELIVERYSTREAM", "AWS_MSK_CLUSTER", "AWS_STEPFUNCTIONS_STATEMACHINE"
| summarize total = count(), by: {type}
| sort total descFilter to a specific account:
smartscapeNodes "AWS_SQS_QUEUE", "AWS_SNS_TOPIC", "AWS_EVENTS_EVENTBUS", "AWS_MSK_CLUSTER"
| filter aws.account.id == "123456789012"
| fields type, name, aws.region, aws.resource.idAWS Metrics & Performance
DQL timeseries patterns for AWS CloudWatch-sourced metrics. Use during investigation to determine whether a resource is saturated, erroring, or slow.
Query Template
The timeseries command with by: { dt.smartscape_source.id} splits results by Dynatrace entity, and a pipe | filter stage scopes the series to a specific resource. The By.<DimensionName> suffix in the metric key is the CloudWatch dimension used to align the metric to the entity.
timeseries cpu = avg(cloud.aws.ec2.CPUUtilization.By.InstanceId), by: { dt.smartscape_source.id},
from: <PROBLEM_START - 30m>, to: <PROBLEM_END + 15m>
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")Replace cloud.aws.ec2.CPUUtilization.By.InstanceId with the relevant metric key, and <ROOT_CAUSE_ENTITY_ID> with the Dynatrace entity ID (e.g. AWS_EC2_INSTANCE-1F335452CC14B245). Omit the | filter clause to get all instances of that metric.
Time windows: The template above uses<PROBLEM_START>and<PROBLEM_END>for scoping queries to a specific incident window. The per-service examples below usefrom: now()-1hfor simplicity — substitute your incident timestamps when investigating a specific problem.
---
EC2 Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.ec2.CPUUtilization.By.InstanceId | CPU utilization | % | > 85% sustained |
cloud.aws.ec2.NetworkIn.By.InstanceId | Inbound network traffic | Bytes | Spike or drop vs baseline |
cloud.aws.ec2.NetworkOut.By.InstanceId | Outbound network traffic | Bytes | Spike or drop vs baseline |
cloud.aws.ec2.StatusCheckFailed.By.InstanceId | Instance or system status check failures | Count | > 0 |
cloud.aws.ec2.DiskReadOps.By.InstanceId | Disk read operations | Count | Spike vs baseline |
cloud.aws.ec2.DiskWriteOps.By.InstanceId | Disk write operations | Count | Spike vs baseline |
Check CPU utilization for a specific instance:
timeseries cpu = avg(cloud.aws.ec2.CPUUtilization.By.InstanceId), by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")Check status check failures (non-zero = instance-level problem):
timeseries checks = max(cloud.aws.ec2.StatusCheckFailed.By.InstanceId), by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")---
Lambda Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.lambda.Invocations.By.FunctionName | Total function invocations | Count | Drop vs baseline (may indicate upstream issue) |
cloud.aws.lambda.Errors.By.FunctionName | Function execution errors | Count | > 0 during incident |
cloud.aws.lambda.Duration.By.FunctionName | Execution duration | Milliseconds | Approaching timeout limit |
cloud.aws.lambda.Throttles.By.FunctionName | Throttled invocations | Count | > 0 (concurrency limit hit) |
cloud.aws.lambda.ConcurrentExecutions.By.FunctionName | Concurrent executions in flight | Count | Near account/function concurrency limit |
Check error rate and duration together:
timeseries {errors = sum(cloud.aws.lambda.Errors.By.FunctionName),
duration = avg(cloud.aws.lambda.Duration.By.FunctionName)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<LAMBDA_ROOT_CAUSE_ENTITY_ID>")Note:<LAMBDA_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the Lambda function (e.g.,AWS_LAMBDA_FUNCTION-ABC123).
---
RDS Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.rds.CPUUtilization.By.DBInstanceIdentifier | Database CPU utilization | % | > 85% sustained |
cloud.aws.rds.DatabaseConnections.By.DBInstanceIdentifier | Active database connections | Count | Near max_connections limit |
cloud.aws.rds.FreeStorageSpace.By.DBInstanceIdentifier | Free storage remaining | Bytes | Trending toward 0 |
cloud.aws.rds.ReadLatency.By.DBInstanceIdentifier | Average read I/O latency | Seconds | > 0.020s (20ms) for production workloads |
cloud.aws.rds.WriteLatency.By.DBInstanceIdentifier | Average write I/O latency | Seconds | > 0.020s (20ms) for production workloads |
Check CPU and connections for a specific RDS instance:
timeseries {cpu = avg(cloud.aws.rds.CPUUtilization.By.DBInstanceIdentifier),
connections = avg(cloud.aws.rds.DatabaseConnections.By.DBInstanceIdentifier)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<RDS_ROOT_CAUSE_ENTITY_ID>")Note:<RDS_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the RDS instance (e.g.,AWS_RDS_DBINSTANCE-ABC123).
---
SQS Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.sqs.ApproximateNumberOfMessagesVisible.By.QueueName | Messages waiting to be processed | Count | Growing over time (consumer lag) |
cloud.aws.sqs.NumberOfMessagesSent.By.QueueName | Messages sent per period | Count | Drop vs baseline |
cloud.aws.sqs.ApproximateAgeOfOldestMessage.By.QueueName | Age of oldest unprocessed message | Seconds | Exceeds your SLA threshold |
Check queue depth over time:
timeseries {depth = max(cloud.aws.sqs.ApproximateNumberOfMessagesVisible.By.QueueName),
age = max(cloud.aws.sqs.ApproximateAgeOfOldestMessage.By.QueueName)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SQS_ROOT_CAUSE_ENTITY_ID>")Note:<SQS_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the SQS queue (e.g.,AWS_SQS_QUEUE-ABC123).
---
ALB Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.applicationelb.RequestCount.By.LoadBalancer | Total requests processed | Count | Drop vs baseline |
cloud.aws.applicationelb.TargetResponseTime.By.LoadBalancer | Average response time from targets | Seconds | > p99 baseline |
cloud.aws.applicationelb.HTTPCode_ELB_5XX_Count.By.LoadBalancer | 5xx errors from targets | Count | > 0 during incident |
cloud.aws.applicationelb.HealthyHostCount.By.TargetGroup | Healthy targets per target group | Count | Drop (indicates unhealthy instances) |
Important:HealthyHostCount.By.TargetGroupis scoped to a target group entity — use the Dynatrace entity ID for the target group as<ROOT_CAUSE_ENTITY_ID>when running that query.
Check request count and 5xx errors for a load balancer:
timeseries {requests = sum(cloud.aws.applicationelb.RequestCount.By.LoadBalancer),
errors5xx = sum(cloud.aws.applicationelb.HTTPCode_ELB_5XX_Count.By.LoadBalancer)},
by: { dt.smartscape_source.id},
from: now()-1h
//| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")Important: If this returns empty the load balancer might not have any traffic during the selected time window.
Check healthy host count for a specific target group:
timeseries healthy = min(cloud.aws.applicationelb.HealthyHostCount.By.LoadBalancer.TargetGroup),
by: { dt.smartscape_source.id , TargetGroup},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ROOT_CAUSE_ENTITY_ID>")---
ECS Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.ecs.CPUUtilization.By.ClusterName.ServiceName | ECS service CPU utilization | % | > 85% sustained |
cloud.aws.ecs.MemoryUtilization.By.ClusterName.ServiceName | ECS service memory utilization | % | > 85% sustained |
cloud.aws.ecs_containerinsights.RunningTaskCount.By.ClusterName.ServiceName | Number of running tasks | Count | Drop vs desired count (task crash loop or placement failure) Important: This metric is part of ECS Container Insights and might not be available |
Check CPU and memory for an ECS service:
timeseries {cpu = avg(cloud.aws.ecs.CPUUtilization.By.ClusterName.ServiceName),
mem = avg(cloud.aws.ecs.MemoryUtilization.By.ClusterName.ServiceName)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ECS_CLUSTER_ROOT_CAUSE_ENTITY_ID>")Note:<ECS_CLUSTER_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the ECS cluster (e.g.,AWS_ECS_CLUSTER-ABC123).
---
DynamoDB Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.dynamodb.ConsumedReadCapacityUnits.By.TableName | Read capacity consumed | Count | Approaching provisioned RCU limit |
cloud.aws.dynamodb.ConsumedWriteCapacityUnits.By.TableName | Write capacity consumed | Count | Approaching provisioned WCU limit |
cloud.aws.dynamodb.SystemErrors.By.TableName | DynamoDB system errors | Count | > 0 |
cloud.aws.dynamodb.SuccessfulRequestLatency.By.Operation.TableName | Successful request latency | Milliseconds | > 10ms (single-digit millisecond expected) |
Check capacity consumption and latency for a table:
timeseries {reads = avg(cloud.aws.dynamodb.ConsumedReadCapacityUnits.By.TableName),
writes = avg(cloud.aws.dynamodb.ConsumedWriteCapacityUnits.By.TableName),
latency = avg(cloud.aws.dynamodb.SuccessfulRequestLatency.By.Operation.TableName)},
by: { dt.smartscape_source.id, TableName},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<DYNAMODB_TABLE_ROOT_CAUSE_ENTITY_ID>")Note:<DYNAMODB_TABLE_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the DynamoDB table (e.g.,AWS_DYNAMODB_TABLE-ABC123).
Check if provisioned capacity is being approached:
timeseries {readsProvisioned = avg(cloud.aws.dynamodb.ProvisionedReadCapacityUnits.By.TableName),
writesProvisioned = avg(cloud.aws.dynamodb.ProvisionedWriteCapacityUnits.By.TableName),
readsConsumed = avg(cloud.aws.dynamodb.ConsumedReadCapacityUnits.By.TableName),
writesConsumed = avg(cloud.aws.dynamodb.ConsumedWriteCapacityUnits.By.TableName)},
by: { dt.smartscape_source.id, TableName},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<DYNAMODB_TABLE_ROOT_CAUSE_ENTITY_ID>")Note:<DYNAMODB_TABLE_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the DynamoDB table (e.g.,AWS_DYNAMODB_TABLE-ABC123).
Note: This can be empty if the table is using on_demand instead of provisioned throughput).---
NLB Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.networkelb.ActiveFlowCount.By.LoadBalancer | Active concurrent TCP flows | Count | Spike vs baseline |
cloud.aws.networkelb.NewFlowCount.By.LoadBalancer | New TCP flows per period | Count | Spike vs baseline |
cloud.aws.networkelb.ProcessedBytes.By.LoadBalancer | Bytes processed by the NLB | Bytes | Spike or drop vs baseline |
cloud.aws.networkelb.TCP_Client_Reset_Count.By.LoadBalancer | Client-initiated TCP resets | Count | > 0 sustained (client-side issues) |
cloud.aws.networkelb.TCP_Target_Reset_Count.By.LoadBalancer | Target-initiated TCP resets | Count | > 0 sustained (target-side issues) |
cloud.aws.networkelb.TCP_ELB_Reset_Count.By.LoadBalancer | NLB-generated TCP resets | Count | > 0 (NLB idle timeout or config issue) |
cloud.aws.networkelb.HealthyHostCount.By.LoadBalancer | Healthy registered targets | Count | Drop vs expected target count |
cloud.aws.networkelb.UnHealthyHostCount.By.LoadBalancer | Unhealthy registered targets | Count | > 0 |
cloud.aws.networkelb.HealthyHostCount.By.LoadBalancer.TargetGroup | Healthy targets per target group | Count | Drop vs expected target count |
cloud.aws.networkelb.UnHealthyHostCount.By.LoadBalancer.TargetGroup | Unhealthy targets per target group | Count | > 0 |
cloud.aws.networkelb.ConsumedLCUs.By.LoadBalancer | Load balancer capacity units consumed | Count | Approaching account LCU limit |
Check TCP resets and flow counts for a specific NLB:
timeseries {clientResets = sum(cloud.aws.networkelb.TCP_Client_Reset_Count.By.LoadBalancer),
targetResets = sum(cloud.aws.networkelb.TCP_Target_Reset_Count.By.LoadBalancer),
elbResets = sum(cloud.aws.networkelb.TCP_ELB_Reset_Count.By.LoadBalancer)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<NLB_ROOT_CAUSE_ENTITY_ID>")Note:<NLB_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the Network Load Balancer (e.g.,AWS_ELASTICLOADBALANCINGV2_LOADBALANCER-ABC123).
Check healthy vs unhealthy targets per target group:
timeseries {healthy = min(cloud.aws.networkelb.HealthyHostCount.By.LoadBalancer.TargetGroup),
unhealthy = max(cloud.aws.networkelb.UnHealthyHostCount.By.LoadBalancer.TargetGroup)},
by: { dt.smartscape_source.id, TargetGroup},
from: now()-1h---
ElastiCache Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.elasticache.CPUUtilization.By.CacheClusterId | CPU utilization | % | > 90% sustained |
cloud.aws.elasticache.EngineCPUUtilization.By.CacheClusterId | Redis engine CPU utilization | % | > 90% sustained (single-threaded bottleneck) |
cloud.aws.elasticache.DatabaseMemoryUsagePercentage.By.CacheClusterId | Memory usage percentage | % | > 85% (eviction risk) |
cloud.aws.elasticache.CurrConnections.By.CacheClusterId | Current client connections | Count | Near maxclients limit |
cloud.aws.elasticache.CacheHits.By.CacheClusterId | Cache hit count | Count | Drop vs baseline (cache invalidation or cold start) |
cloud.aws.elasticache.CacheMisses.By.CacheClusterId | Cache miss count | Count | Spike vs baseline |
cloud.aws.elasticache.Evictions.By.CacheClusterId | Evicted items due to memory pressure | Count | > 0 sustained (memory pressure) |
cloud.aws.elasticache.ReplicationLag.By.CacheClusterId | Replica lag behind primary | Seconds | > 1s (read consistency risk) |
cloud.aws.elasticache.FreeableMemory.By.CacheClusterId | Available memory on the host | Bytes | Trending toward 0 |
cloud.aws.elasticache.NetworkBytesIn.By.CacheClusterId | Inbound network throughput | Bytes | Spike or near network limit |
cloud.aws.elasticache.NetworkBytesOut.By.CacheClusterId | Outbound network throughput | Bytes | Spike or near network limit |
cloud.aws.elasticache.SwapUsage.By.CacheClusterId | Swap space used | Bytes | > 0 sustained (memory exhaustion) |
Check CPU, memory, and evictions for a cache cluster:
timeseries {cpu = avg(cloud.aws.elasticache.EngineCPUUtilization.By.CacheClusterId),
memory = avg(cloud.aws.elasticache.DatabaseMemoryUsagePercentage.By.CacheClusterId),
evictions = sum(cloud.aws.elasticache.Evictions.By.CacheClusterId)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<ELASTICACHE_ROOT_CAUSE_ENTITY_ID>")Note:<ELASTICACHE_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the ElastiCache cluster (e.g.,AWS_ELASTICACHE_CLUSTER-ABC123).
Check cache hit ratio to detect cache effectiveness issues:
timeseries {hits = sum(cloud.aws.elasticache.CacheHits.By.CacheClusterId),
misses = sum(cloud.aws.elasticache.CacheMisses.By.CacheClusterId)},
by: { dt.smartscape_source.id},
from: now()-1h---
NAT Gateway Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.natgateway.ActiveConnectionCount.By.NatGatewayId | Active concurrent connections | Count | Near 55,000 limit per destination |
cloud.aws.natgateway.ConnectionAttemptCount.By.NatGatewayId | Connection attempts per period | Count | Spike vs baseline |
cloud.aws.natgateway.ConnectionEstablishedCount.By.NatGatewayId | Successfully established connections | Count | Drop vs attempt count (connection failures) |
cloud.aws.natgateway.ErrorPortAllocation.By.NatGatewayId | Port allocation errors | Count | > 0 (NAT Gateway capacity exhaustion!) |
cloud.aws.natgateway.BytesInFromSource.By.NatGatewayId | Bytes received from VPC sources | Bytes | Spike vs baseline |
cloud.aws.natgateway.BytesOutToDestination.By.NatGatewayId | Bytes sent to external destinations | Bytes | Spike vs baseline |
cloud.aws.natgateway.PacketsDropCount.By.NatGatewayId | Dropped packets | Count | > 0 (capacity or config issue) |
cloud.aws.natgateway.IdleTimeoutCount.By.NatGatewayId | Connections closed due to idle timeout | Count | Spike (upstream keep-alive issue) |
cloud.aws.natgateway.PeakBytesPerSecond.By.NatGatewayId | Peak bytes per second throughput | Bytes/s | Approaching NAT Gateway throughput limit |
cloud.aws.natgateway.PeakPacketsPerSecond.By.NatGatewayId | Peak packets per second throughput | Count/s | Approaching NAT Gateway packet limit |
Check for port allocation errors and dropped packets (critical capacity indicators):
timeseries {portErrors = sum(cloud.aws.natgateway.ErrorPortAllocation.By.NatGatewayId),
dropped = sum(cloud.aws.natgateway.PacketsDropCount.By.NatGatewayId),
activeConns = max(cloud.aws.natgateway.ActiveConnectionCount.By.NatGatewayId)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<NAT_GW_ROOT_CAUSE_ENTITY_ID>")Note:<NAT_GW_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the NAT Gateway (e.g.,AWS_EC2_NATGATEWAY-ABC123).ErrorPortAllocation > 0means the NAT Gateway cannot allocate more source ports — consider adding additional NAT Gateways or reducing connection volume.
Check traffic throughput for a NAT Gateway:
timeseries {bytesIn = sum(cloud.aws.natgateway.BytesInFromSource.By.NatGatewayId),
bytesOut = sum(cloud.aws.natgateway.BytesOutToDestination.By.NatGatewayId)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<NAT_GW_ROOT_CAUSE_ENTITY_ID>")---
API Gateway Metrics
API Gateway has two flavors with different metric keys: REST APIs (prefix cloud.aws.apigateway) use ApiName dimensions, while HTTP APIs use ApiId dimensions. Error metric names also differ: 4XXError/5XXError for REST, 4xx/5xx for HTTP APIs.
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.apigateway.Count.By.ApiName | Total REST API calls | Count | Drop vs baseline |
cloud.aws.apigateway.4XXError.By.ApiName | REST API 4xx client errors | Count | Spike vs baseline |
cloud.aws.apigateway.5XXError.By.ApiName | REST API 5xx server errors | Count | > 0 during incident |
cloud.aws.apigateway.Latency.By.ApiName | Overall REST API latency | Milliseconds | > p99 baseline |
cloud.aws.apigateway.IntegrationLatency.By.ApiName | Backend integration latency | Milliseconds | > p99 baseline (backend bottleneck) |
cloud.aws.apigateway.Count.By.ApiId | Total HTTP API calls | Count | Drop vs baseline |
cloud.aws.apigateway.4xx.By.ApiId | HTTP API 4xx client errors | Count | Spike vs baseline |
cloud.aws.apigateway.5xx.By.ApiId | HTTP API 5xx server errors | Count | > 0 during incident |
cloud.aws.apigateway.Latency.By.ApiId | Overall HTTP API latency | Milliseconds | > p99 baseline |
cloud.aws.apigateway.IntegrationLatency.By.ApiId | Backend integration latency | Milliseconds | > p99 baseline (backend bottleneck) |
cloud.aws.apigateway.DataProcessed.By.ApiId | Data processed by HTTP API | Bytes | Spike vs baseline |
Check REST API error rates and latency:
timeseries {calls = sum(cloud.aws.apigateway.Count.By.ApiName),
errors4xx = sum(cloud.aws.apigateway.4XXError.By.ApiName),
errors5xx = sum(cloud.aws.apigateway.5XXError.By.ApiName),
latency = avg(cloud.aws.apigateway.Latency.By.ApiName)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APIGW_ROOT_CAUSE_ENTITY_ID>")Note:<APIGW_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the API Gateway (e.g.,AWS_APIGATEWAY_RESTAPI-ABC123).
Check HTTP API error rates and latency:
timeseries {calls = sum(cloud.aws.apigateway.Count.By.ApiId),
errors4xx = sum(cloud.aws.apigateway.4xx.By.ApiId),
errors5xx = sum(cloud.aws.apigateway.5xx.By.ApiId),
latency = avg(cloud.aws.apigateway.Latency.By.ApiId)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<APIGW_ROOT_CAUSE_ENTITY_ID>")---
SNS Metrics
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.sns.NumberOfMessagesPublished.By.TopicName | Messages published to topic | Count | Drop vs baseline (producer issue) |
cloud.aws.sns.NumberOfNotificationsDelivered.By.TopicName | Notifications successfully delivered | Count | Drop vs published count (delivery failure) |
cloud.aws.sns.NumberOfNotificationsFailed.By.TopicName | Notifications that failed delivery | Count | > 0 (subscriber endpoint issue) |
cloud.aws.sns.PublishSize.By.TopicName | Size of published messages | Bytes | Spike vs baseline (unexpected payload growth) |
Check publish volume and delivery failures for a topic:
timeseries {published = sum(cloud.aws.sns.NumberOfMessagesPublished.By.TopicName),
delivered = sum(cloud.aws.sns.NumberOfNotificationsDelivered.By.TopicName),
failed = sum(cloud.aws.sns.NumberOfNotificationsFailed.By.TopicName)},
by: { dt.smartscape_source.id},
from: now()-1hCheck delivery health for a specific topic:
timeseries {published = sum(cloud.aws.sns.NumberOfMessagesPublished.By.TopicName),
delivered = sum(cloud.aws.sns.NumberOfNotificationsDelivered.By.TopicName),
failed = sum(cloud.aws.sns.NumberOfNotificationsFailed.By.TopicName)},
by: { dt.smartscape_source.id},
from: now()-1h
| filter dt.smartscape_source.id == toSmartscapeId("<SNS_TOPIC_ROOT_CAUSE_ENTITY_ID>")Note:<SNS_TOPIC_ROOT_CAUSE_ENTITY_ID>is the Dynatrace entity ID for the SNS topic (e.g.,AWS_SNS_TOPIC-A281598EF3D8B475). IfNumberOfNotificationsFailedis non-zero, check the subscriber endpoint (Lambda, SQS, HTTP) for errors.
---
S3 Metrics
S3 request metrics require a CloudWatch request metrics filter configured on the bucket. Without this filter, only storage-level metrics are available (via entity data — see security-compliance.md). All request metrics use the .By.BucketName.FilterId dimension pattern. The Dynatrace entity type is AWS_S3_BUCKET.
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.s3.AllRequests.By.BucketName.FilterId | Total requests to the bucket | Count | Drop vs baseline |
cloud.aws.s3.4xxErrors.By.BucketName.FilterId | Client error responses (403, 404, etc.) | Count | Spike vs baseline (access denied or missing objects) |
cloud.aws.s3.5xxErrors.By.BucketName.FilterId | Server error responses | Count | > 0 sustained (S3 service issue) |
cloud.aws.s3.GetRequests.By.BucketName.FilterId | GET requests | Count | Spike vs baseline |
cloud.aws.s3.PutRequests.By.BucketName.FilterId | PUT requests | Count | Spike vs baseline |
cloud.aws.s3.DeleteRequests.By.BucketName.FilterId | DELETE requests | Count | Unexpected spike (data loss risk) |
cloud.aws.s3.HeadRequests.By.BucketName.FilterId | HEAD requests | Count | Spike vs baseline |
cloud.aws.s3.ListRequests.By.BucketName.FilterId | LIST requests | Count | High volume (expensive operation, cost impact) |
cloud.aws.s3.FirstByteLatency.By.BucketName.FilterId | Time to first byte for requests | Milliseconds | > 200ms (S3 or network latency issue) |
cloud.aws.s3.TotalRequestLatency.By.BucketName.FilterId | Total request duration including transfer | Milliseconds | > p99 baseline |
cloud.aws.s3.BytesDownloaded.By.BucketName.FilterId | Bytes downloaded from the bucket | Bytes | Spike vs baseline (unexpected data egress, cost impact) |
cloud.aws.s3.BytesUploaded.By.BucketName.FilterId | Bytes uploaded to the bucket | Bytes | Spike vs baseline |
Check request volume and error rates for all buckets:
timeseries {
requests = sum(cloud.aws.s3.AllRequests.By.BucketName.FilterId),
errors4xx = sum(cloud.aws.s3.4xxErrors.By.BucketName.FilterId),
errors5xx = sum(cloud.aws.s3.5xxErrors.By.BucketName.FilterId)
},
by: { dt.smartscape_source.id },
from: now()-2hCheck latency for a specific bucket:
timeseries {
firstByte = avg(cloud.aws.s3.FirstByteLatency.By.BucketName.FilterId),
totalLatency = avg(cloud.aws.s3.TotalRequestLatency.By.BucketName.FilterId)
},
by: { dt.smartscape_source.id },
from: now()-24h
| filter dt.smartscape_source.id == toSmartscapeId("<S3_BUCKET_ENTITY_ID>")Note:<S3_BUCKET_ENTITY_ID>is the Dynatrace entity ID for the S3 bucket (e.g.,AWS_S3_BUCKET-8278109BE96166BE). Per-operation metrics (GetRequests,PutRequests, etc.) are only available if the bucket's CloudWatch request metrics filter is configured to report them. If these return empty, check the S3 bucket metrics configuration in AWS.
---
MSK (Managed Kafka) Metrics
MSK metrics are split across three dimension patterns: cluster-level (.By.Cluster_Name), broker-level (.By.Broker_ID.Cluster_Name), and consumer-group-level (.By.Cluster_Name.Consumer_Group.Topic). The Dynatrace entity type is AWS_MSK_CLUSTER.
Cluster Health
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.kafka.ActiveControllerCount.By.Cluster_Name | Active controller brokers | Count | Must be exactly 1 (0 = no leader, >1 = split-brain) |
cloud.aws.kafka.OfflinePartitionsCount.By.Cluster_Name | Partitions with no active leader | Count | > 0 (data unavailability!) |
cloud.aws.kafka.GlobalPartitionCount.By.Cluster_Name | Total partitions across all topics | Count | Monitor growth over time |
cloud.aws.kafka.GlobalTopicCount.By.Cluster_Name | Total topics in the cluster | Count | Monitor growth over time |
cloud.aws.kafka.KafkaDataLogsDiskUsed.By.Cluster_Name | Aggregate data log disk usage | % | > 85% (broker storage exhaustion risk) |
Check cluster-level health — offline partitions and controller count are the most critical MSK indicators:
timeseries {
offlinePartitions = max(cloud.aws.kafka.OfflinePartitionsCount.By.Cluster_Name),
activeControllers = max(cloud.aws.kafka.ActiveControllerCount.By.Cluster_Name),
globalPartitions = max(cloud.aws.kafka.GlobalPartitionCount.By.Cluster_Name),
globalTopics = max(cloud.aws.kafka.GlobalTopicCount.By.Cluster_Name)
},
by: { dt.smartscape_source.id },
from: now()-2h
| filter dt.smartscape_source.id == toSmartscapeId("<MSK_CLUSTER_ENTITY_ID>")Note:<MSK_CLUSTER_ENTITY_ID>is the Dynatrace entity ID for the MSK cluster (e.g.,AWS_MSK_CLUSTER-4B5BA4FE313B7C3A).OfflinePartitionsCount > 0means producers/consumers cannot read/write affected partitions — treat as a P1 incident.
Broker Resource Utilization
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.kafka.CpuUser.By.Broker_ID.Cluster_Name | User-space CPU utilization | % | > 60% sustained (Kafka is single-threaded per partition) |
cloud.aws.kafka.CpuIdle.By.Broker_ID.Cluster_Name | CPU idle percentage | % | < 20% (broker overloaded) |
cloud.aws.kafka.CpuSystem.By.Broker_ID.Cluster_Name | System CPU utilization | % | > 10% sustained (kernel overhead) |
cloud.aws.kafka.CpuIoWait.By.Broker_ID.Cluster_Name | CPU time waiting for I/O | % | > 10% (disk I/O bottleneck) |
cloud.aws.kafka.MemoryUsed.By.Broker_ID.Cluster_Name | Memory in use | Bytes | Near total memory (OOM risk) |
cloud.aws.kafka.MemoryFree.By.Broker_ID.Cluster_Name | Free memory | Bytes | Trending toward 0 |
cloud.aws.kafka.MemoryBuffered.By.Broker_ID.Cluster_Name | Buffered memory | Bytes | Monitor for drops (page cache pressure) |
cloud.aws.kafka.MemoryCached.By.Broker_ID.Cluster_Name | Cached memory | Bytes | Drop indicates page cache eviction (impacts read performance) |
cloud.aws.kafka.HeapMemoryAfterGC.By.Broker_ID.Cluster_Name | JVM heap after garbage collection | Bytes | > 60% of heap size (GC pressure) |
cloud.aws.kafka.BurstBalance.By.Broker_ID.Cluster_Name | EBS burst balance remaining | % | < 20% (I/O throttling imminent) |
cloud.aws.kafka.KafkaDataLogsDiskUsed.By.Broker_ID.Cluster_Name | Per-broker data log disk usage | % | > 85% (broker storage exhaustion risk) |
cloud.aws.kafka.KafkaAppLogsDiskUsed.By.Broker_ID.Cluster_Name | Application log disk usage | % | > 50% (log rotation issue) |
Check broker CPU and memory to identify overloaded brokers:
timeseries cpu = avg(cloud.aws.kafka.CpuUser.By.Broker_ID.Cluster_Name),
cpuIdle = avg(cloud.aws.kafka.CpuIdle.By.Broker_ID.Cluster_Name),
memUsed = avg(cloud.aws.kafka.MemoryUsed.By.Broker_ID.Cluster_Name),
heapAfterGC = avg(cloud.aws.kafka.HeapMemoryAfterGC.By.Broker_ID.Cluster_Name),
by: { dt.smartscape_source.id },
from: now()-2h
| filter dt.smartscape_source.id == toSmartscapeId("<MSK_CLUSTER_ENTITY_ID>")Check disk usage and burst balance for storage pressure:
timeseries diskUsed = avg(cloud.aws.kafka.KafkaDataLogsDiskUsed.By.Broker_ID.Cluster_Name),
burstBalance = min(cloud.aws.kafka.BurstBalance.By.Broker_ID.Cluster_Name),
by: { dt.smartscape_source.id },
from: now()-2h
| filter dt.smartscape_source.id == toSmartscapeId("<MSK_CLUSTER_ENTITY_ID>")Broker Throughput & Networking
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.kafka.BytesInPerSec.By.Broker_ID.Cluster_Name | Bytes received per second per broker | Bytes/s | Spike vs baseline or approaching throughput limit |
cloud.aws.kafka.BytesOutPerSec.By.Broker_ID.Cluster_Name | Bytes sent per second per broker | Bytes/s | Spike vs baseline |
cloud.aws.kafka.MessagesInPerSec.By.Broker_ID.Cluster_Name | Messages received per second per broker | Count/s | Spike vs baseline |
cloud.aws.kafka.ConnectionCount.By.Broker_ID.Cluster_Name | Current client connections per broker | Count | Near broker connection limit |
cloud.aws.kafka.ClientConnectionCount.By.Broker_ID.Cluster_Name | Client-initiated connections per broker | Count | Imbalanced across brokers |
cloud.aws.kafka.LeaderCount.By.Broker_ID.Cluster_Name | Partition leaders on this broker | Count | Imbalanced across brokers (hot broker) |
cloud.aws.kafka.PartitionCount.By.Broker_ID.Cluster_Name | Partitions on this broker | Count | Imbalanced across brokers |
cloud.aws.kafka.NetworkRxDropped.By.Broker_ID.Cluster_Name | Dropped inbound network packets | Count | > 0 (network saturation) |
cloud.aws.kafka.NetworkRxErrors.By.Broker_ID.Cluster_Name | Inbound network errors | Count | > 0 |
cloud.aws.kafka.NetworkTxDropped.By.Broker_ID.Cluster_Name | Dropped outbound network packets | Count | > 0 (network saturation) |
cloud.aws.kafka.NetworkTxErrors.By.Broker_ID.Cluster_Name | Outbound network errors | Count | > 0 |
Check throughput and connections across all brokers:
timeseries bytesIn = sum(cloud.aws.kafka.BytesInPerSec.By.Broker_ID.Cluster_Name),
bytesOut = sum(cloud.aws.kafka.BytesOutPerSec.By.Broker_ID.Cluster_Name),
connections = max(cloud.aws.kafka.ConnectionCount.By.Broker_ID.Cluster_Name),
by: { dt.smartscape_source.id },
from: now()-2hTopic-Level Throughput
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.kafka.BytesInPerSec.By.Broker_ID.Cluster_Name.Topic | Bytes received per second per topic per broker | Bytes/s | Identifies hot topics |
cloud.aws.kafka.BytesOutPerSec.By.Broker_ID.Cluster_Name.Topic | Bytes sent per second per topic per broker | Bytes/s | Identifies hot consumer topics |
Consumer Group Lag
| Metric key | Description | Unit | Investigation threshold |
|---|---|---|---|
cloud.aws.kafka.EstimatedMaxTimeLag.By.Cluster_Name.Consumer_Group.Topic | Estimated time lag of the slowest consumer | Seconds | > SLA threshold (consumer falling behind) |
cloud.aws.kafka.MaxOffsetLag.By.Cluster_Name.Consumer_Group.Topic | Maximum offset lag across consumer group partitions | Count | Growing over time (consumer not keeping up) |
Check consumer group lag by topic — critical for detecting consumer processing failures:
timeseries lag = max(cloud.aws.kafka.EstimatedMaxTimeLag.By.Cluster_Name.Consumer_Group.Topic),
offsetLag = max(cloud.aws.kafka.MaxOffsetLag.By.Cluster_Name.Consumer_Group.Topic),
by: { dt.smartscape_source.id, Consumer_Group, Topic },
from: now()-2hInterpretation: IfEstimatedMaxTimeLagis growing whileBytesInPerSecis stable, the consumer is falling behind and needs scaling or debugging. If both lag and ingest rate spike, the issue may be upstream traffic growth rather than consumer failure.
---
Metric Discovery
When investigating a service not listed above, or when you need to verify which metrics are actually available on the tenant, use these discovery queries.
Find all available metrics for a given AWS service prefix:
fetch metric.series
| filter startsWith(metric.key, "cloud.aws.<SERVICE_PREFIX>")
| summarize count = count(), by: { metric.key }
| sort count descReplace<SERVICE_PREFIX>with any prefix (e.g.,s3,kafka,networkelb,elasticache,natgateway,apigateway) to discover metrics for a specific service.
Find all AWS metrics available on the tenant:
fetch metric.series
| filter startsWith(metric.key, "cloud.aws.")
| summarize count = count(), by: { metric.key }
| sort count desc
| limit 200---
Combining Entity Queries with Metrics
Find a set of entities by filter, then query metrics for all of them. Example: are all EC2 instances in a VPC experiencing high CPU, or just one?
Step 1 — Find resource IDs for the group:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter aws.vpc.id == "<VPC_ID>"
| fields name, aws.resource.idStep 2 — Query metrics for all instances in the group (no filter = all series):
timeseries cpu = avg(cloud.aws.ec2.CPUUtilization.By.InstanceId),
by: { dt.smartscape_source.id},
from: now()-1hCross-reference the dt.smartscape_source.id dimension values against the entity IDs from Step 1 to identify which instances in the VPC are affected.
Cross-Service Correlation Patterns
When investigating an incident, a single service's metrics rarely tell the whole story. Use these patterns to correlate metrics across connected services and identify the actual bottleneck.
SQS + Lambda Consumer Health
When a Lambda function consumes from SQS, correlate queue depth growth with Lambda errors and throttles to identify consumer bottlenecks.
Query SQS queue depth and message age:
timeseries {
queue_depth = max(cloud.aws.sqs.ApproximateNumberOfMessagesVisible.By.QueueName),
queue_age = max(cloud.aws.sqs.ApproximateAgeOfOldestMessage.By.QueueName)
},
by: { dt.smartscape_source.id },
from: now()-6hQuery Lambda consumer errors and performance:
timeseries {
errors = sum(cloud.aws.lambda.Errors.By.FunctionName),
throttles = sum(cloud.aws.lambda.Throttles.By.FunctionName),
duration = avg(cloud.aws.lambda.Duration.By.FunctionName)
},
by: { dt.smartscape_source.id },
from: now()-6hRun both queries over the same time window. If queue depth grows while Lambda errors or throttles increase, the consumer is failing. If queue depth grows with no Lambda activity, the consumer may have been disconnected.
ALB + Backend Saturation
When ALB response time degrades, check whether backend targets are saturated.
Query ALB response time and error rates:
timeseries {
response_time = avg(cloud.aws.applicationelb.TargetResponseTime.By.LoadBalancer),
requests = sum(cloud.aws.applicationelb.RequestCount.By.LoadBalancer),
errors_5xx = sum(cloud.aws.applicationelb.HTTPCode_ELB_5XX_Count.By.LoadBalancer)
},
by: { dt.smartscape_source.id },
from: now()-2hQuery ECS backend resource utilization:
timeseries {
cpu = avg(cloud.aws.ecs.CPUUtilization.By.ClusterName.ServiceName),
mem = avg(cloud.aws.ecs.MemoryUtilization.By.ClusterName.ServiceName)
},
by: { dt.smartscape_source.id },
from: now()-2hIf ALB 5xx errors spike while ECS CPU or memory is at 85%+, the service needs scaling. If ALB response time degrades but the backend looks healthy, the issue may be network or DNS.
RDS + Application Performance
When application errors spike, check whether the database is the bottleneck.
Query RDS latency, connections, and CPU:
timeseries {
read_latency = avg(cloud.aws.rds.ReadLatency.By.DBInstanceIdentifier),
write_latency = avg(cloud.aws.rds.WriteLatency.By.DBInstanceIdentifier),
connections = avg(cloud.aws.rds.DatabaseConnections.By.DBInstanceIdentifier),
cpu = avg(cloud.aws.rds.CPUUtilization.By.DBInstanceIdentifier)
},
by: { dt.smartscape_source.id },
from: now()-2hIf read or write latency spikes above 20ms while connections are near the instance limit, the database is saturated. If CPU is low but latency is high, the bottleneck is likely I/O rather than compute — check FreeStorageSpace and IOPS metrics.
Correlation tip: Always query both sides of a dependency over the same time window (from/to). Mismatched windows make it impossible to confirm whether two signals are actually correlated.
---
Metric Availability Note
Not all metrics are ingested by default — depends on which services are enabled in the AWS integration configuration. If a timeseries query returns no data:
1. Verify the entity exists: run the smartscapeNodes query from Step 1 of rca-workflow.md 2. Confirm the metric is collected in the AWS integration settings
Do not interpret empty timeseries results as "no problem" — it may mean the metric is not configured for this resource type.
AWS Resource Management & Optimization
Analyze AWS resource usage, identify optimization opportunities, and manage resource tagging.
Table of Contents
- Resource Inventory
- Tag Compliance
- Resource Lifecycle
- Regional & VPC Distribution
- Storage & Security Resources
Resource Inventory
Count all AWS resources by type:
smartscapeNodes "AWS_*"
| summarize resource_count = count(), by: {type}
| sort resource_count descView resource distribution across accounts:
smartscapeNodes "AWS_*"
| summarize resource_count = count(), by: {aws.account.id, aws.region}
| sort resource_count descFind resource types spanning multiple regions:
smartscapeNodes "AWS_*"
| summarize
region_count = countDistinct(aws.region),
total_resources = count(),
by: {type}
| filter region_count > 1
| sort region_count descTag Compliance
Find completely untagged resources:
smartscapeNodes "AWS_*"
| filter tags == record()
| fields type, name, aws.resource.id, aws.account.id, aws.regionFind resources missing a specific required tag:
smartscapeNodes "AWS_*"
| filter isNull(tags[Environment]) or tags[Environment] == ""
| summarize count = count(), by: {type, aws.account.id}Calculate tag coverage percentages across resource types:
smartscapeNodes "AWS_*"
| fieldsAdd has_owner_tag = if(isNotNull(tags[Owner]), 1)
| fieldsAdd has_env_tag = if(isNotNull(tags[Environment]), 1)
| summarize
total = count(),
with_env = sum(has_env_tag),
with_owner = sum(has_owner_tag),
by: { type }
| fieldsAdd
env_coverage_pct = (with_env * 100.0) / total,
owner_coverage_pct = (with_owner * 100.0) / total
| sort env_coverage_pct asc
Find resources by tag value:
smartscapeNodes "AWS_*"
| filter tags[`<TAG_NAME>`] == "<TAG_VALUE>"
| summarize count = count(), by: {type, aws.region}Find resources by naming convention:
smartscapeNodes "AWS_*"
| filter matchesPhrase(name, "prod")
| fields type, name, aws.resource.id, aws.region, tags[Environment]Resource Lifecycle
Detect deleted resources:
smartscapeNodes "AWS_*"
| filter cloud.acquisition.status == "DELETED"
| fields type, name, aws.resource.id, aws.account.id, aws.regionFind resources with acquisition issues:
smartscapeNodes "AWS_*"
| filter cloud.acquisition.status != "OK"
| fields type, name, aws.resource.id, cloud.acquisition.status, aws.account.idFind unattached EBS volumes:
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd state = awsjson[configuration][state]
| filter state == "available"
| fields name, aws.resource.id, aws.availability_zone, aws.account.idFind unassociated Elastic IPs:
smartscapeNodes "AWS_EC2_EIP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd associationId = awsjson[configuration][associationId]
| filter isNull(associationId)
| fields name, aws.resource.id, aws.region, aws.account.idRegional & VPC Distribution
View resources by region:
smartscapeNodes "AWS_*"
| summarize resource_count = count(), by: {aws.region}
| sort resource_count descCount resources per VPC:
smartscapeNodes "AWS_*"
| filter isNotNull(aws.vpc.id)
| summarize resource_count = count(), by: {aws.vpc.id, type}
| sort resource_count descStorage & Security Resources
Count storage services:
smartscapeNodes "AWS_S3_BUCKET", "AWS_EC2_VOLUME", "AWS_EFS_FILESYSTEM"
| summarize count = count(), by: {type, aws.region}
| sort count descCount IAM and security resources:
smartscapeNodes "AWS_IAM_ROLE", "AWS_IAM_USER", "AWS_IAM_GROUP", "AWS_KMS_KEY", "AWS_EC2_SECURITYGROUP"
| summarize count = count(), by: {type}
| sort count descList S3 buckets:
smartscapeNodes "AWS_S3_BUCKET"
| fields name, aws.account.id, aws.region, aws.resource.idFind EC2 snapshots:
smartscapeNodes "AWS_EC2_SNAPSHOT"
| fields name, aws.resource.id, aws.region, aws.account.idAWS Resource Ownership & Chargeback
Track resource ownership and enable cost allocation across teams.
Table of Contents
- Tag-Based Ownership Pattern
- Common Ownership Tags
- Service-Specific Ownership
- Multi-Account Resource Summary
Tag-Based Ownership Pattern
All ownership queries follow the same pattern — filter by a tag, then summarize by that tag and a grouping dimension:
smartscapeNodes "AWS_*"
| filter isNotNull(tags[`<TAG_NAME>`])
| summarize resource_count = count(), by: {tags[`<TAG_NAME>`], type}
| sort resource_count descReplace <TAG_NAME> with any tag from the table below. Replace type with aws.region or aws.account.id for alternative groupings. Replace "AWS_*" with a specific entity type to scope to one service.
Common Ownership Tags
| Tag | Use case | Typical values |
|---|---|---|
CostCenter | Financial chargeback | Cost center codes |
Owner | Individual accountability | Email or username |
Team | Team-level allocation | Team names |
Project | Project-based grouping | Project identifiers |
Application | Application ownership | Application names |
Environment | Environment segmentation | production, staging, dev |
Department | Departmental allocation | Department names |
BusinessUnit | Business unit grouping | BU identifiers |
Service-Specific Ownership
To scope ownership queries to a specific AWS service, replace "AWS_*" with the entity type:
| Entity type | Example use case |
|---|---|
AWS_EC2_INSTANCE | Instance costs by department/team |
AWS_LAMBDA_FUNCTION | Serverless costs by application |
AWS_RDS_DBCLUSTER | Database ownership tracking |
AWS_EKS_CLUSTER | Kubernetes cluster ownership by business unit |
AWS_EC2_VOLUME | Storage costs by project |
AWS_S3_BUCKET | Bucket ownership by team |
For service-specific queries, you can also select detail fields instead of summarizing:
smartscapeNodes "AWS_RDS_DBCLUSTER"
| filter isNotNull(tags[`<TAG_NAME>`])
| fields name, aws.resource.id, tags[`<TAG_NAME>`], aws.regionMulti-Account Resource Summary
Summarize resources across accounts (independent of tags):
smartscapeNodes "AWS_*"
| summarize resource_count = count(), by: {aws.account.id, type}
| sort resource_count desc
| limit 50AWS Security & Compliance
Monitor security configurations and compliance across AWS resources.
Table of Contents
- Security Group Rule Analysis
- Overly Permissive Inbound Rules
- Security Group Blast Radius
- S3 Public Access & Encryption
- EBS Volume Encryption
- IAM & Key Management
- Public Access Detection
- Network Security
Security Group Rule Analysis
Security group rules are stored in aws.object as ipPermissions (ingress) and ipPermissionsEgress (egress) arrays. Convert these to strings with toString() and use contains() to search for risky patterns.
Find security groups with any ingress rule open to the internet (0.0.0.0/0 or ::/0):
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
ipPermissions = toString(awsjson[configuration][ipPermissions]),
groupName = awsjson[configuration][groupName]
| filter contains(ipPermissions, "0.0.0.0/0") or contains(ipPermissions, "::/0")
| fields groupName, aws.resource.id, aws.vpc.id, ipPermissionsCount open security groups per VPC:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd ipPermissions = toString(awsjson[configuration][ipPermissions])
| filter contains(ipPermissions, "0.0.0.0/0") or contains(ipPermissions, "::/0")
| summarize open_sg_count = count(), by: {aws.vpc.id}Find security groups that are wide open — all traffic allowed from the internet (protocol -1 or port range 0-65535 combined with 0.0.0.0/0):
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
ipPermissions = toString(awsjson[configuration][ipPermissions]),
groupName = awsjson[configuration][groupName]
| filter contains(ipPermissions, "0.0.0.0/0")
| filter contains(ipPermissions, "\"ipProtocol\":\"-1\"")
or contains(ipPermissions, "\"fromPort\":0")
| fields groupName, aws.resource.id, aws.vpc.id, ipPermissionsAudit egress rules — count security groups with unrestricted outbound traffic per VPC:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
ipPermissionsEgress = toString(awsjson[configuration][ipPermissionsEgress]),
groupName = awsjson[configuration][groupName]
| filter contains(ipPermissionsEgress, "0.0.0.0/0")
| summarize egress_open_count = count(), by: {aws.vpc.id}Tip: TheipPermissionsfield is a JSON array. To detect specific open ports (e.g., SSH 22, RDP 3389, PostgreSQL 5432), combine the 0.0.0.0/0 filter with a port-specific string match:contains(ipPermissions, "\"fromPort\":22"). For a full list of risky ports, repeat with3389(RDP),3306(MySQL),5432(PostgreSQL),6379(Redis),27017(MongoDB).
Overly Permissive Inbound Rules
The queries above use string-based contains() for quick detection. The queries below use expand to inspect individual rules, enabling per-rule filtering by port and protocol.
Find all security groups with any 0.0.0.0/0 inbound rule:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd ipPermissions = awsjson[configuration][ipPermissions]
| filter contains(toString(ipPermissions), "0.0.0.0/0")
| fields name, aws.resource.id, aws.vpc.idFind security groups allowing 0.0.0.0/0 on all traffic or all ports — the highest-risk configuration:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd ipPermissions = awsjson[configuration][ipPermissions]
| expand ipPermissions
| filter contains(toString(ipPermissions[ipRanges]), "0.0.0.0/0")
| filter ipPermissions[ipProtocol] == "-1"
or (ipPermissions[fromPort] == 0 and ipPermissions[toPort] == 65535)
| fieldsAdd protocol = ipPermissions[ipProtocol],
fromPort = ipPermissions[fromPort],
toPort = ipPermissions[toPort]
| fields name, aws.resource.id, aws.vpc.id, protocol, fromPort, toPortFind security groups allowing 0.0.0.0/0 on dangerous ports (SSH, RDP, databases). Adjust the port list to match your organization's policy:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd ipPermissions = awsjson[configuration][ipPermissions]
| expand ipPermissions
| filter contains(toString(ipPermissions[ipRanges]), "0.0.0.0/0")
| fieldsAdd protocol = ipPermissions[ipProtocol],
fromPort = ipPermissions[fromPort],
toPort = ipPermissions[toPort]
| filter in(toPort, array(22, 3389, 3306, 5432, 1433, 6379, 27017, 9200))
| fields name, aws.resource.id, aws.vpc.id, protocol, fromPort, toPort
| sort toPortDangerous ports reference: 22 (SSH), 3389 (RDP), 3306 (MySQL), 5432 (PostgreSQL), 1433 (MSSQL), 6379 (Redis), 27017 (MongoDB), 9200 (Elasticsearch).
Summarize open-to-internet inbound rules by port — useful for audit dashboards:
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| parse aws.object, "JSON:awsjson"
| fieldsAdd ipPermissions = awsjson[configuration][ipPermissions]
| expand ipPermissions
| filter contains(toString(ipPermissions[ipRanges]), "0.0.0.0/0")
| fieldsAdd protocol = ipPermissions[ipProtocol],
toPort = ipPermissions[toPort]
| summarize sg_count = count(), by: {protocol, toPort}
| sort sg_count descSecurity Group Blast Radius
Find security groups with the most instances (blast radius):
smartscapeNodes "AWS_EC2_INSTANCE"
| traverse "uses", "AWS_EC2_SECURITYGROUP"
| summarize instance_count = count(), by: {aws.resource.name, aws.vpc.id}
| sort instance_count desc
| limit 20Find instances with multiple security groups:
smartscapeNodes "AWS_EC2_INSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd sg_count = arraySize(awsjson[configuration][securityGroups])
| filter sg_count > 1
| fields name, aws.resource.id, aws.security_group.id, sg_count
| sort sg_count descList security groups (for finding unused ones, cross-reference with instance usage):
smartscapeNodes "AWS_EC2_SECURITYGROUP"
| fields name, aws.resource.id, aws.vpc.id
| limit 100S3 Public Access & Encryption
S3 bucket security configuration is in supplementary_configuration within aws.object. The four publicAccessBlockConfiguration booleans should all be true for a properly secured bucket.
Audit Public Access Block settings across all S3 buckets:
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
blockPublicAcls = awsjson[supplementary_configuration][publicAccessBlockConfiguration][blockPublicAcls],
ignorePublicAcls = awsjson[supplementary_configuration][publicAccessBlockConfiguration][ignorePublicAcls],
blockPublicPolicy = awsjson[supplementary_configuration][publicAccessBlockConfiguration][blockPublicPolicy],
restrictPublicBuckets = awsjson[supplementary_configuration][publicAccessBlockConfiguration][restrictPublicBuckets]
| fields name, aws.account.id, aws.region,
blockPublicAcls, ignorePublicAcls, blockPublicPolicy, restrictPublicBucketsFind S3 buckets missing any Public Access Block setting (potential public exposure):
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
blockPublicAcls = awsjson[supplementary_configuration][publicAccessBlockConfiguration][blockPublicAcls],
ignorePublicAcls = awsjson[supplementary_configuration][publicAccessBlockConfiguration][ignorePublicAcls],
blockPublicPolicy = awsjson[supplementary_configuration][publicAccessBlockConfiguration][blockPublicPolicy],
restrictPublicBuckets = awsjson[supplementary_configuration][publicAccessBlockConfiguration][restrictPublicBuckets]
| filter blockPublicAcls != true
or ignorePublicAcls != true
or blockPublicPolicy != true
or restrictPublicBuckets != true
| fields name, aws.account.id, aws.region,
blockPublicAcls, ignorePublicAcls, blockPublicPolicy, restrictPublicBucketsFind S3 buckets with no Public Access Block configuration at all:
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd pubBlock = awsjson[supplementary_configuration][publicAccessBlockConfiguration]
| filter isNull(pubBlock)
| fields name, aws.account.id, aws.regionDetect S3 buckets with public ACL grants (AllUsers or AuthenticatedUsers):
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd acl = toString(awsjson[supplementary_configuration][accessControlList][grants])
| filter contains(acl, "AllUsers") or contains(acl, "AuthenticatedUsers")
| fields name, aws.account.id, aws.region, aclSummarize S3 security posture — encryption algorithm and public access block status:
smartscapeNodes "AWS_S3_BUCKET"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
blockPublicAcls = awsjson[supplementary_configuration][publicAccessBlockConfiguration][blockPublicAcls],
sseAlgorithm = awsjson[supplementary_configuration][serverSideEncryptionConfiguration][rules][0][applyServerSideEncryptionByDefault][sseAlgorithm]
| summarize bucket_count = count(), by: {blockPublicAcls, sseAlgorithm}EBS Volume Encryption
Summarize EBS volume encryption status by volume type:
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
encrypted = awsjson[configuration][encrypted],
volumeType = awsjson[configuration][volumeType],
state = awsjson[configuration][state]
| summarize volume_count = count(), by: {encrypted, volumeType}
| sort volume_count descList unencrypted EBS volumes with size and location:
smartscapeNodes "AWS_EC2_VOLUME"
| parse aws.object, "JSON:awsjson"
| fieldsAdd
encrypted = awsjson[configuration][encrypted],
volumeType = awsjson[configuration][volumeType],
size = awsjson[configuration][size]
| filter encrypted != true
| fields name, aws.resource.id, aws.availability_zone, volumeType, sizeIAM & Key Management
Note: IAM analysis in Dynatrace is limited to role-level metadata. For policy-level analysis (overly permissive policies, unused access keys, MFA status), use AWS IAM Access Analyzer or Security Hub.
Identify IAM roles used by Lambda functions:
smartscapeNodes "AWS_IAM_ROLE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd assumeRolePolicyDocument = awsjson[configuration][assumeRolePolicyDocument]
| filter contains(toString(assumeRolePolicyDocument), "lambda")
| fields name, aws.account.idMonitor KMS encryption key states:
smartscapeNodes "AWS_KMS_KEY"
| parse aws.object, "JSON:awsjson"
| fieldsAdd keyState = awsjson[configuration][keyState],
keyUsage = awsjson[configuration][keyUsage]
| summarize key_count = count(), by: {keyState, keyUsage}
| sort key_count descCheck RDS database encryption:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd storageEncrypted = awsjson[configuration][storageEncrypted]
| summarize db_count = count(), by: {storageEncrypted, aws.region}Count IAM roles by account:
smartscapeNodes "AWS_IAM_ROLE"
| summarize role_count = count(), by: {aws.account.id}
| sort role_count descPublic Access Detection
Find publicly accessible RDS databases:
smartscapeNodes "AWS_RDS_DBINSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd publiclyAccessible = awsjson[configuration][publiclyAccessible]
| filter publiclyAccessible == true
| fields name, aws.resource.id, aws.vpc.id, aws.account.idIdentify internet-facing load balancers:
smartscapeNodes "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER"
| parse aws.object, "JSON:awsjson"
| fieldsAdd scheme = awsjson[configuration][scheme]
| filter scheme == "internet-facing"
| fields name, aws.resource.id, aws.vpc.id, aws.regionAudit VPC attachment coverage across Lambda functions — use to verify functions requiring private connectivity have VPC configured. For per-function networking detail, see references/serverless-containers.md.
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| filter isNotNull(aws.vpc.id)
| fields name, aws.resource.id, aws.vpc.id, aws.security_group.id
| summarize function_count = count(), by: {aws.vpc.id}Network Security
Analyze VPC endpoint private connectivity:
smartscapeNodes "AWS_EC2_VPCENDPOINT"
| parse aws.object, "JSON:awsjson"
| fieldsAdd serviceName = awsjson[configuration][serviceName]
| summarize endpoint_count = count(), by: {serviceName, aws.vpc.id}
| sort endpoint_count descList Route53 hosted zones:
smartscapeNodes "AWS_ROUTE53_HOSTEDZONE"
| summarize zone_count = count(), by: {aws.account.id}AWS Serverless & Container Workloads
Monitor AWS Lambda, ECS, EKS, and App Runner services.
Table of Contents
- Serverless & Container Entity Types
- Lambda Monitoring
- ECS Monitoring
- EKS Monitoring
- App Runner
- Cross-Service Analysis
Serverless & Container Entity Types
All these types support the standard discovery pattern: smartscapeNodes "<TYPE>" | fields name, aws.account.id, aws.region, ...
| Entity type | Description |
|---|---|
AWS_LAMBDA_FUNCTION | Lambda functions |
AWS_LAMBDA_EVENTSOURCEMAPPING | Lambda event source mappings |
AWS_ECS_CLUSTER | ECS clusters |
AWS_ECS_SERVICE | ECS services |
AWS_ECS_TASK | Running ECS tasks |
AWS_ECS_TASKDEFINITION | ECS task definitions |
AWS_ECS_CONTAINERINSTANCE | EC2 instances used by ECS |
AWS_EKS_CLUSTER | EKS clusters |
AWS_EKS_NODEGROUP | EKS node groups |
AWS_APPRUNNER_SERVICE | App Runner services |
AWS_APPRUNNER_VPCCONNECTOR | App Runner VPC connectors |
Lambda Monitoring
Find Lambda functions with VPC access — use for networking troubleshooting and dependency mapping. For a security audit of VPC attachment coverage, see security-compliance.md#public-access-detection.
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| filter isNotNull(aws.vpc.id)
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.id, aws.security_group.idFilter functions by tag:
smartscapeNodes "AWS_LAMBDA_FUNCTION"
| filter tags[Environment] == "production"
| fields name, aws.resource.id, aws.regionECS Monitoring
Map ECS services to their clusters using traversal:
smartscapeNodes "AWS_ECS_SERVICE"
| traverse "belongs_to", "AWS_ECS_CLUSTER"
| fields name, aws.resource.id, aws.regionFind which services use which task definitions:
smartscapeNodes "AWS_ECS_SERVICE"
| traverse "uses", "AWS_ECS_TASKDEFINITION"
| fields name, aws.resource.idAnalyze ECS service networking (subnet attachment):
smartscapeNodes "AWS_ECS_SERVICE"
| traverse "is_attached_to", "AWS_EC2_SUBNET"
| fields name, aws.resource.id, aws.vpc.idFind ECS service security groups:
smartscapeNodes "AWS_ECS_SERVICE"
| traverse "uses", "AWS_EC2_SECURITYGROUP"
| fields name, aws.resource.idEKS Monitoring
List EKS clusters with VPC context:
smartscapeNodes "AWS_EKS_CLUSTER"
| fields name, aws.account.id, aws.region, aws.vpc.idApp Runner
Find App Runner VPC connectors (for private connectivity):
smartscapeNodes "AWS_APPRUNNER_VPCCONNECTOR"
| fields name, aws.resource.id, aws.vpc.idCross-Service Analysis
Count all container platforms by type and region:
smartscapeNodes "AWS_ECS_CLUSTER", "AWS_EKS_CLUSTER", "AWS_APPRUNNER_SERVICE"
| summarize count = count(), by: {type, aws.region}
| sort count descAWS VPC Networking & Security
Monitor and troubleshoot AWS VPC network infrastructure, security groups, and connectivity.
Table of Contents
- VPC Discovery
- Security Group Analysis
- Subnet & Instance Distribution
- Internet-Facing Resources
- Network Infrastructure
- Availability Zone Distribution
VPC Discovery
List all VPCs:
smartscapeNodes "AWS_EC2_VPC"
| fields name, aws.account.id, aws.region, aws.resource.id, aws.vpc.idGet all resources in a VPC grouped by type:
smartscapeNodes "AWS_*"
| filter aws.vpc.id == "<VPC_ID>"
| summarize resource_count = count(), by: {type, aws.subnet.id}
| sort resource_count descSecurity Group Analysis
Find all instances and their security groups:
smartscapeNodes "AWS_EC2_INSTANCE"
| fields name, aws.resource.id, aws.vpc.id, aws.security_group.id
| expand aws.security_group.idLocate all resources using a specific security group:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter contains(aws.security_group.id, "<EC2_SECURITY_GROUP>")
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.idFind instances with multiple security groups:
smartscapeNodes "AWS_EC2_INSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd sg_count = arraySize(awsjson[configuration][securityGroups])
| filter sg_count > 0
| fields name, aws.resource.id, aws.security_group.id, sg_count
| sort sg_count descSubnet & Instance Distribution
Count instances per subnet:
smartscapeNodes "AWS_EC2_INSTANCE"
| summarize instance_count = count(), by: {aws.vpc.id, aws.subnet.id}
| sort instance_count descFind EC2 instances in a specific VPC:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter aws.vpc.id == "<VPC_ID>"
| fields name, aws.resource.id, aws.subnet.id, aws.availability_zoneInternet-Facing Resources
Locate instances with public IPs:
smartscapeNodes "AWS_EC2_INSTANCE"
| parse aws.object, "JSON:awsjson"
| fieldsAdd publicIp = awsjson[configuration][networkInterfaces][0][association][publicIp]
| filter isNotNull(publicIp)
| fields name, aws.resource.id, publicIp, aws.vpc.idNetwork Infrastructure
List network interfaces:
smartscapeNodes "AWS_EC2_NETWORKINTERFACE"
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.id, aws.security_group.idNAT gateways by VPC:
smartscapeNodes "AWS_EC2_NATGATEWAY"
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.id
| summarize nat_count = count(), by: {aws.vpc.id}VPN gateways:
smartscapeNodes "AWS_EC2_VPNGATEWAY"
| fields name, aws.resource.id, aws.vpc.id, aws.regionVPC endpoints:
smartscapeNodes "AWS_EC2_VPCENDPOINT"
| fields name, aws.resource.id, aws.vpc.id, aws.subnet.idVPC peering connections:
smartscapeNodes "AWS_EC2_VPCPEERINGCONNECTION"
| fields name, aws.resource.id, aws.vpc.id, aws.regionAvailability Zone Distribution
View instance distribution across AZs:
smartscapeNodes "AWS_EC2_INSTANCE"
| summarize
instance_count = count(),
by: {aws.region, aws.availability_zone, aws.vpc.id}
| sort aws.region, instance_count descWorkload Detection Reference
Identify how an EC2 instance is orchestrated. Run these detection queries during problem analysis to determine which orchestration system manages the affected instance — this determines the correct resolution path.
Table of Contents
- Overview
- 1. Load Balancer Detection
- 2. Auto Scaling Group Detection
- 3. ECS Detection
- 4. EKS / Kubernetes Node Detection
- 5. AWS Batch Detection
Overview
EC2 instances can be managed by different orchestration systems, each requiring a different resolution approach. Run the detection queries below to identify the workload pattern before following a resolution path.
Workload Pattern Summary
| Tag Present | Workload Pattern | Resolution Path |
|---|---|---|
AmazonECSManaged | ECS container instance | Drain container instance via ECS; let ECS reschedule tasks |
kubernetes.io/cluster/<name> | EKS / Kubernetes node | Cordon + drain node; Karpenter provisions replacement or CA scales node group |
AWSBatchServiceTag | AWS Batch compute node | Disable compute environment; drain job queue |
aws:autoscaling:groupName only | Generic ASG / web tier | Trigger scale-out if headroom available; replace degraded instance |
| None of the above | Standalone instance | Direct remediation (restart, resize, or replace) |
Replace <WORKLOAD_EC2_INSTANCE> with the Dynatrace entity ID of the affected instance (e.g., AWS_EC2_INSTANCE-ABC123).
---
1. Load Balancer Detection
Determine if the instance is registered in a target group behind an Application or Network Load Balancer.
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == "<WORKLOAD_EC2_INSTANCE>"
| traverse "balanced_by", "AWS_ELASTICLOADBALANCINGV2_TARGETGROUP", direction:backward
| fieldsAdd targetGroupName = aws.resource.name, targetGroupId = id
| traverse "balanced_by", "AWS_ELASTICLOADBALANCINGV2_LOADBALANCER", fieldsKeep:{targetGroupName, targetGroupId}
| parse aws.object, "JSON:awsjson"
| fieldsAdd
lbDnsName = awsjson[configuration][dnsName],
lbScheme = awsjson[configuration][scheme],
lbType = awsjson[configuration][type]
| fields name, lbDnsName, lbScheme, lbType, targetGroupName,
dt.traverse.history[-1][targetGroupId]If results returned: The instance is behind a load balancer. Record the ALB/NLB name and lbScheme (internet-facing vs internal) — this is critical for resolution (e.g., traffic can be shifted away before remediation).
---
2. Auto Scaling Group Detection
Determine if the instance is part of an Auto Scaling Group and check current capacity vs configured limits.
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == toSmartscapeId("<WORKLOAD_EC2_INSTANCE>")
| traverse "*", "AWS_AUTOSCALING_AUTOSCALINGGROUP", direction:"backward"
| parse aws.object, "JSON:awsjson"
| fieldsAdd asgMin = awsjson[`configuration`][`minSize`]
| fieldsAdd asgMax = awsjson[`configuration`][`maxSize`]
| fieldsAdd asgDesired = awsjson[`configuration`][`desiredCapacity`]
| fields name, id, asgMin, asgMax, asgDesiredIf results returned: Record name, asgMin, asgMax, and asgDesired — if asgDesired < asgMax, scale-out is possible. If asgDesired == asgMax, the ASG is at capacity and cannot scale further without a limit change.
---
3. ECS Detection
Detect whether the instance is an ECS container instance, managed by ECS for running containerized tasks.
Step 1 — Detect by tag:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == "<WORKLOAD_EC2_INSTANCE>"
| filter isNotNull(tags[`AmazonECSManaged`])
| fields name, id, tags[`aws:autoscaling:groupName`]Step 2 — If matched, find the ECS cluster:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == "<WORKLOAD_EC2_INSTANCE>"
| traverse {"*"}, {"AWS_ECS_CONTAINERINSTANCE"}, direction:backward
| traverse {"*"}, {"AWS_ECS_CLUSTER"}
| fields name, id, aws.resource.idRecord: ECS cluster name and ID — needed for resolution guidance (e.g., draining the container instance, scaling the ECS service).
---
4. EKS / Kubernetes Node Detection
Detect whether the instance is a Kubernetes worker node managed by EKS, and identify the node management mechanism (Karpenter, Cluster Autoscaler, EKS Managed Node Group, or eksctl).
Step 1 — Detect by tag and extract node manager details:
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == "<WORKLOAD_EC2_INSTANCE>"
| fieldsAdd tagStr = toString(tags)
| filter matchesPhrase(tagStr, "kubernetes.io/cluster")
| fieldsAdd
eksClusterName = tags[`aws:eks:cluster-name`],
nodegroupName = tags[`eks:nodegroup-name`],
karpenterManagedBy = tags[`karpenter.sh/managed-by`],
karpenterNodepool = tags[`karpenter.sh/nodepool`],
casEnabled = tags[`k8s.io/cluster-autoscaler/enabled`],
eksctlNodegroupName = tags[`alpha.eksctl.io/nodegroup-name`],
eksctlNodegroupType = tags[`alpha.eksctl.io/nodegroup-type`]
| fields name, id, eksClusterName, nodegroupName, karpenterManagedBy,
karpenterNodepool, casEnabled, eksctlNodegroupName, eksctlNodegroupTypeStep 2 — Find the EKS cluster entity:
smartscapeNodes "AWS_EKS_CLUSTER"
| filter aws.resource.name == "<EKS_CLUSTER_NAME>"
| fields name, id, aws.resource.id, aws.regionReplace <EKS_CLUSTER_NAME> with the value from eksClusterName in Step 1.
Step 3 — Determine the node manager from the extracted fields:
| Field present | Node manager |
|---|---|
karpenter.sh/managed-by is set | Karpenter |
k8s.io/cluster-autoscaler/enabled is set | Cluster Autoscaler + ASG |
eks:nodegroup-name is set (no eksctl prefix) | EKS Managed Node Group |
alpha.eksctl.io/nodegroup-name + nodegroup-type: unmanaged | eksctl unmanaged node group + ASG |
alpha.eksctl.io/nodegroup-name + nodegroup-type: managed | eksctl managed node group (EKS MNG) |
Record: EKS cluster name, node manager type, and node pool / node group name — needed for resolution guidance (e.g., cordon/drain via kubectl, scale Karpenter node pool, or update ASG desired capacity).
---
5. AWS Batch Detection
Detect whether the instance is a compute environment node managed by AWS Batch.
smartscapeNodes "AWS_EC2_INSTANCE"
| filter id == "<WORKLOAD_EC2_INSTANCE>"
| filter isNotNull(tags[`AWSBatchServiceTag`])
| fieldsAdd
batchJobQueue = tags[`aws:batch:job-queue-name`],
batchComputeEnv = tags[`aws:batch:compute-environment-name`]
| fields name, id, batchJobQueue, batchComputeEnv, tagsRecord: Batch compute environment name (batchComputeEnv) and job queue (batchJobQueue) — needed for resolution guidance (e.g., disabling the compute environment, draining the job queue).
Related skills
FAQ
Which AWS services does dt-obs-aws cover?
EC2, Lambda, ECS, EKS, VPC, RDS, DynamoDB, S3, ELB, SQS, SNS, and related AWS_* Smartscape entities.
When should I use dt-obs-hosts instead?
For generic host CPU or memory metrics rather than AWS cloud resource inventory in Smartscape.
What common AWS fields are available?
aws.account.id, aws.region, aws.resource.id, aws.resource.name, aws.arn, and aws.vpc.id on entities.
Is Dt Obs Aws safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.