These rules identify DynamoDB tables with stale data, provisioned tables missing auto-scaling, completely unused provisioned tables, and tables where autoscaling is configured with identical min and max values — all common sources of unnecessary DynamoDB spend.
| Rule ID | Scan Type | Name |
|---|---|---|
| CLDBRN-AWS-DYNAMODB-1 | Discovery | DynamoDB Table Stale Data |
| CLDBRN-AWS-DYNAMODB-2 | Discovery and IaC | DynamoDB Table Without Autoscaling |
| CLDBRN-AWS-DYNAMODB-3 | Discovery | DynamoDB Table Unused |
| CLDBRN-AWS-DYNAMODB-4 | IaC | DynamoDB Autoscaling Range Fixed |
CLDBRN-AWS-DYNAMODB-1
DynamoDB Table Stale Data
Scan type: Discovery
What it checks
Flags DynamoDB tables whose data has not changed for more than 90 days, based on the table's latest stream label timestamp. Tables that store data nobody reads or writes are candidates for archival or deletion.
Tables without DynamoDB Streams enabled are skipped because there's no reliable timestamp to measure staleness.
Why it matters
DynamoDB charges for provisioned or on-demand read/write capacity and for stored data. A table sitting idle with no writes for 90+ days is still paying for storage (and capacity, if provisioned). If the data is no longer needed, deleting or archiving the table to S3 eliminates ongoing charges.
What triggers a finding
The table has DynamoDB Streams enabled, and the latestStreamLabel timestamp is more than 90 days in the past.
How to remediate
- Check whether any application still reads from the table. CloudWatch's
ConsumedReadCapacityUnitsmetric can confirm if reads are happening - If the table is no longer needed, export it to S3 for archival and delete it:
aws dynamodb export-table-to-point-in-time \
--table-arn arn:aws:dynamodb:us-east-1:123456789012:table/my-table \
--s3-bucket my-archive-bucket \
--s3-prefix dynamodb-exports/
aws dynamodb delete-table --table-name my-table
- If the data is still needed but access is infrequent, switch the table to on-demand capacity mode to eliminate provisioned throughput charges during idle periods
CLDBRN-AWS-DYNAMODB-2
DynamoDB Table Without Autoscaling
Scan type: Discovery and IaC
What it checks
Flags provisioned-capacity DynamoDB tables that do not have Application Auto Scaling configured for table-level read and write capacity. Without auto-scaling, provisioned tables pay for a fixed throughput regardless of actual traffic.
Tables using on-demand (pay-per-request) billing are not flagged because they scale automatically.
Why it matters
A provisioned DynamoDB table without auto-scaling pays for its fixed capacity around the clock. If traffic drops to zero at night but the table is provisioned for peak daytime load, you're paying for unused capacity every off-peak hour. Auto-scaling adjusts provisioned capacity based on actual utilization, keeping costs proportional to demand.
What triggers a finding
All of the following must be true:
- The table's billing mode is not
PAY_PER_REQUEST(i.e., it uses provisioned capacity) - The table does not have a table-level read scalable target registered with Application Auto Scaling
- The table does not have a table-level write scalable target registered with Application Auto Scaling
GSI-level auto-scaling targets are not considered for this rule, only table-level targets.
How to remediate
Register the table with Application Auto Scaling and create scaling policies:
# Register read capacity target
aws application-autoscaling register-scalable-target \
--service-namespace dynamodb \
--resource-id "table/my-table" \
--scalable-dimension "dynamodb:table:ReadCapacityUnits" \
--min-capacity 5 \
--max-capacity 1000
# Register write capacity target
aws application-autoscaling register-scalable-target \
--service-namespace dynamodb \
--resource-id "table/my-table" \
--scalable-dimension "dynamodb:table:WriteCapacityUnits" \
--min-capacity 5 \
--max-capacity 1000
Alternatively, consider switching to on-demand billing if your traffic is unpredictable. On-demand costs more per request but eliminates the risk of over-provisioning entirely.
IaC resources checked
| Provider | Resource |
|---|---|
| Terraform | aws_dynamodb_table + aws_appautoscaling_target |
| CloudFormation | AWS::DynamoDB::Table + AWS::ApplicationAutoScaling::ScalableTarget |
CLDBRN-AWS-DYNAMODB-3
DynamoDB Table Unused
Scan type: Discovery
What it checks
Flags provisioned-capacity DynamoDB tables with no consumed read or write capacity over the last 30 days.
Why it matters
Provisioned tables incur hourly charges for read and write capacity units whether or not the table receives any traffic. A table with zero consumed capacity for 30 days is wasting money on unused capacity.
What triggers a finding
billingMode is PROVISIONED AND totalConsumedReadCapacityUnitsLast30Days is 0 AND totalConsumedWriteCapacityUnitsLast30Days is 0.
How to remediate
Switch the table to on-demand billing (PAY_PER_REQUEST) if occasional access is expected, or delete the table if it's no longer needed. For tables with sporadic usage patterns, on-demand pricing eliminates charges during idle periods.
CLDBRN-AWS-DYNAMODB-4
DynamoDB Autoscaling Range Fixed
Scan type: IaC
What it checks
Flags provisioned-capacity DynamoDB tables whose autoscaling min and max capacity are set to the same value, making autoscaling ineffective.
Why it matters
When autoscaling min equals max, the table cannot scale up during traffic spikes or scale down during quiet periods. This defeats the purpose of autoscaling and locks you into paying for peak capacity around the clock.
What triggers a finding
billingMode is PROVISIONED AND either read min capacity equals read max capacity or write min capacity equals write max capacity.
How to remediate
Set a meaningful gap between min and max capacity. A common pattern is to set min at the sustained baseline and max at 2-5x the baseline, allowing autoscaling to handle spikes without over-provisioning during off-peak hours.
IaC resources checked
| Provider | Resource |
|---|---|
| Terraform | aws_dynamodb_table + aws_appautoscaling_target |
| CloudFormation | AWS::DynamoDB::Table + AWS::ApplicationAutoScaling::ScalableTarget |
See Also
- CLI discover command - scan live DynamoDB tables
- SDK Reference - run discovery programmatically