These rules identify DynamoDB tables with no recent write activity, 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 | Severity | Name |
|---|---|---|---|
| CLDBRN-AWS-DYNAMODB-1 | Discovery | Medium | DynamoDB Table Inactive |
| CLDBRN-AWS-DYNAMODB-2 | Discovery and IaC | Medium | DynamoDB Table Without Autoscaling |
| CLDBRN-AWS-DYNAMODB-3 | Discovery | High | DynamoDB Table Unused |
| CLDBRN-AWS-DYNAMODB-4 | IaC | Medium | DynamoDB Autoscaling Range Fixed |
CLDBRN-AWS-DYNAMODB-1
DynamoDB Table Inactive
Scan type: Discovery
Severity: Medium
What it checks
Flags DynamoDB tables that consumed no write capacity at all over a complete 90-day window. A table nothing has written to in three months holds data that is either archival or forgotten.
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
totalConsumedWriteCapacityUnitsLast90Days is 0 for the table.
Coverage
A table with no totalConsumedWriteCapacityUnitsLast90Days value (the 90-day CloudWatch window is incomplete or the metric is unavailable) is reported as unknown coverage instead of a pass.
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
Severity: Medium
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
Severity: High
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.
Coverage
A provisioned table missing either 30-day consumed-capacity value is reported as unknown coverage instead of a pass. On-demand tables are outside this rule and count as assessed.
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
Severity: Medium
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