AnalysisType: policy
Filename: aws_dynamodb_autoscaling.py
PolicyID: "AWS.DynamoDB.Autoscaling"
DisplayName: "AWS DynamoDB Table Autoscaling"
Enabled: true
ResourceTypes:
- AWS.DynamoDB.Table
Tags:
- AWS
- Availability
Severity: Low
Description: >
DynamoDB Auto Scaling can dynamically adjust provisioned throughput capacity in response to
traffic patterns. This enables a table to increase its provisioned read and write capacity
to handle sudden increases in traffic
Runbook: >
https://docs.runpanther.io/alert-runbooks/built-in-policies/aws-dynamodb-table-has-autoscaling-enabled
Reference: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.html
Tests:
- Name: AutoScaling Enabled
ExpectedResult: true
Resource:
{
"AutoScalingDescriptions":
[
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:table:ReadCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:table:WriteCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:ReadCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:WriteCapacityUnits",
"ServiceNamespace": "dynamodb",
},
],
"AttributeDefinitions":
[
{ "AttributeName": "attribute_one", "AttributeType": "S" },
{ "AttributeName": "attribute_two", "AttributeType": "S" },
],
"BillingModeSummary":
{
"BillingMode": "PROVISIONED",
"LastUpdateToPayPerRequestDateTime": "2019-01-01T00:00:00Z",
},
"CreationDateTime": "2019-01-01T100:00:00Z",
"GlobalSecondaryIndexes":
[
{
"Backfilling": null,
"IndexArn": "arn:aws:dynamodb:us-west-2:123456789012:table/panther-internal-poller-test-table/index/high_score-index",
"IndexName": "example-index",
"IndexSizeBytes": 0,
"IndexStatus": "ACTIVE",
"ItemCount": 0,
"KeySchema":
[{ "AttributeName": "high_score", "KeyType": "HASH" }],
"Projection":
{ "NonKeyAttributes": null, "ProjectionType": "ALL" },
"ProvisionedThroughput":
{
"LastDecreaseDateTime": "2019-01-01T00:00:00Z",
"LastIncreaseDateTime": "2019-01-01T00:00:00Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
},
],
"ItemCount": 42,
"KeySchema":
[
{ "AttributeName": "attribute_one", "KeyType": "HASH" },
{ "AttributeName": "attribute_two", "KeyType": "RANGE" },
],
"LatestStreamArn": null,
"LatestStreamLabel": null,
"LocalSecondaryIndexes": null,
"ProvisionedThroughput":
{
"LastDecreaseDateTime": null,
"LastIncreaseDateTime": null,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0,
},
"RestoreSummary": null,
"SSEDescription":
{
"KMSMasterKeyArn": "arn:aws:kms:us-west-2:123456789012:key/111222333",
"SSEType": "KMS",
"Status": "ENABLED",
},
"StreamSpecification": null,
"TableArn": "arn:aws:dynamodb:us-west-2:123456789012:table/example-table",
"TableId": "aaaaaaaa-bbbb-1111-2222-1111222233334444",
"Name": "example-table",
"TableSizeBytes": 20000,
"TableStatus": "ACTIVE",
}
- Name: AutoScaling Never Configured
ExpectedResult: false
Resource:
{
"AutoScalingDescriptions": null,
"AttributeDefinitions":
[
{ "AttributeName": "attribute_one", "AttributeType": "S" },
{ "AttributeName": "attribute_two", "AttributeType": "S" },
],
"BillingModeSummary": null,
"CreationDateTime": "2019-01-01T100:00:00Z",
"GlobalSecondaryIndexes":
[
{
"Backfilling": null,
"IndexArn": "arn:aws:dynamodb:us-west-2:123456789012:table/panther-internal-poller-test-table/index/high_score-index",
"IndexName": "example-index",
"IndexSizeBytes": 0,
"IndexStatus": "ACTIVE",
"ItemCount": 0,
"KeySchema":
[{ "AttributeName": "high_score", "KeyType": "HASH" }],
"Projection":
{ "NonKeyAttributes": null, "ProjectionType": "ALL" },
"ProvisionedThroughput":
{
"LastDecreaseDateTime": "2019-01-01T00:00:00Z",
"LastIncreaseDateTime": "2019-01-01T00:00:00Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
},
],
"ItemCount": 42,
"KeySchema":
[
{ "AttributeName": "attribute_one", "KeyType": "HASH" },
{ "AttributeName": "attribute_two", "KeyType": "RANGE" },
],
"LatestStreamArn": null,
"LatestStreamLabel": null,
"LocalSecondaryIndexes": null,
"ProvisionedThroughput":
{
"LastDecreaseDateTime": null,
"LastIncreaseDateTime": null,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0,
},
"RestoreSummary": null,
"SSEDescription":
{
"KMSMasterKeyArn": "arn:aws:kms:us-west-2:123456789012:key/111222333",
"SSEType": "KMS",
"Status": "ENABLED",
},
"StreamSpecification": null,
"TableArn": "arn:aws:dynamodb:us-west-2:123456789012:table/example-table",
"TableId": "aaaaaaaa-bbbb-1111-2222-1111222233334444",
"Name": "example-table",
"TableSizeBytes": 20000,
"TableStatus": "ACTIVE",
}
- Name: AutoScaling Not Enabled For Table
ExpectedResult: false
Resource:
{
"AutoScalingDescriptions":
[
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:ReadCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:WriteCapacityUnits",
"ServiceNamespace": "dynamodb",
},
],
"AttributeDefinitions":
[
{ "AttributeName": "attribute_one", "AttributeType": "S" },
{ "AttributeName": "attribute_two", "AttributeType": "S" },
],
"BillingModeSummary":
{
"BillingMode": "PROVISIONED",
"LastUpdateToPayPerRequestDateTime": "2019-01-01T00:00:00Z",
},
"CreationDateTime": "2019-01-01T100:00:00Z",
"GlobalSecondaryIndexes":
[
{
"Backfilling": null,
"IndexArn": "arn:aws:dynamodb:us-west-2:123456789012:table/panther-internal-poller-test-table/index/high_score-index",
"IndexName": "example-index",
"IndexSizeBytes": 0,
"IndexStatus": "ACTIVE",
"ItemCount": 0,
"KeySchema":
[{ "AttributeName": "high_score", "KeyType": "HASH" }],
"Projection":
{ "NonKeyAttributes": null, "ProjectionType": "ALL" },
"ProvisionedThroughput":
{
"LastDecreaseDateTime": "2019-01-01T00:00:00Z",
"LastIncreaseDateTime": "2019-01-01T00:00:00Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
},
],
"ItemCount": 42,
"KeySchema":
[
{ "AttributeName": "attribute_one", "KeyType": "HASH" },
{ "AttributeName": "attribute_two", "KeyType": "RANGE" },
],
"LatestStreamArn": null,
"LatestStreamLabel": null,
"LocalSecondaryIndexes": null,
"ProvisionedThroughput":
{
"LastDecreaseDateTime": null,
"LastIncreaseDateTime": null,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0,
},
"RestoreSummary": null,
"SSEDescription":
{
"KMSMasterKeyArn": "arn:aws:kms:us-west-2:123456789012:key/111222333",
"SSEType": "KMS",
"Status": "ENABLED",
},
"StreamSpecification": null,
"TableArn": "arn:aws:dynamodb:us-west-2:123456789012:table/example-table",
"TableId": "aaaaaaaa-bbbb-1111-2222-1111222233334444",
"Name": "example-table",
"TableSizeBytes": 20000,
"TableStatus": "ACTIVE",
}
- Name: Billing Mode Pay Per Request
ExpectedResult: true
Resource:
{
"AutoScalingDescriptions":
[
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:table:ReadCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:table:WriteCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:ReadCapacityUnits",
"ServiceNamespace": "dynamodb",
},
{
"CreationTime": "2019-01-01T00:00:00Z",
"MaxCapacity": 40000,
"MinCapacity": 5,
"ResourceId": "table/example-table/index/example-index",
"RoleARN": "arn:aws:iam::123456789012:role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_DynamoDBTable",
"ScalableDimension": "dynamodb:index:WriteCapacityUnits",
"ServiceNamespace": "dynamodb",
},
],
"AttributeDefinitions":
[
{ "AttributeName": "attribute_one", "AttributeType": "S" },
{ "AttributeName": "attribute_two", "AttributeType": "S" },
],
"BillingModeSummary":
{
"BillingMode": "PAY-PER-REQUEST",
"LastUpdateToPayPerRequestDateTime": "2019-01-01T00:00:00Z",
},
"CreationDateTime": "2019-01-01T100:00:00Z",
"GlobalSecondaryIndexes":
[
{
"Backfilling": null,
"IndexArn": "arn:aws:dynamodb:us-west-2:123456789012:table/panther-internal-poller-test-table/index/high_score-index",
"IndexName": "example-index",
"IndexSizeBytes": 0,
"IndexStatus": "ACTIVE",
"ItemCount": 0,
"KeySchema":
[{ "AttributeName": "high_score", "KeyType": "HASH" }],
"Projection":
{ "NonKeyAttributes": null, "ProjectionType": "ALL" },
"ProvisionedThroughput":
{
"LastDecreaseDateTime": "2019-01-01T00:00:00Z",
"LastIncreaseDateTime": "2019-01-01T00:00:00Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5,
},
},
],
"ItemCount": 42,
"KeySchema":
[
{ "AttributeName": "attribute_one", "KeyType": "HASH" },
{ "AttributeName": "attribute_two", "KeyType": "RANGE" },
],
"LatestStreamArn": null,
"LatestStreamLabel": null,
"LocalSecondaryIndexes": null,
"ProvisionedThroughput":
{
"LastDecreaseDateTime": null,
"LastIncreaseDateTime": null,
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0,
},
"RestoreSummary": null,
"SSEDescription":
{
"KMSMasterKeyArn": "arn:aws:kms:us-west-2:123456789012:key/111222333",
"SSEType": "KMS",
"Status": "ENABLED",
},
"StreamSpecification": null,
"TableArn": "arn:aws:dynamodb:us-west-2:123456789012:table/example-table",
"TableId": "aaaaaaaa-bbbb-1111-2222-1111222233334444",
"Name": "example-table",
"TableSizeBytes": 20000,
"TableStatus": "ACTIVE",
}
# ------ paired body: aws_dynamodb_autoscaling.py ------
from panther_base_helpers import deep_get
# If you do not wish to enforce application auto-scaling on your dynamo tables Global
# Secondary Indices, set this variable to False
CHECK_GSI = True
def policy(resource):
# Check if this table has never had auto scaling configured
if resource["BillingModeSummary"] is None and resource["AutoScalingDescriptions"] is None:
return False
# Check if this table is not on provisioned billing (and therefore auto scaling does not apply)
if deep_get(resource, "BillingModeSummary", "BillingMode") != "PROVISIONED":
return True
# Check if application auto scaling is configured
if resource["AutoScalingDescriptions"] is None:
return False
# Build a list of all the resources (the table and optionally the GSI's) to be checked
table_id = "table/" + resource["Name"]
resource_auto_scaling = {table_id: False}
if CHECK_GSI:
# We cannot use resource.get('GSI', []) here as the value is present, it is just a NoneType
for gsi in resource["GlobalSecondaryIndexes"] or []:
resource_auto_scaling[table_id + "/index/" + gsi["IndexName"]] = False
# Check that each resource that requires application autoscaling has it enabled
for auto_scale_target in resource["AutoScalingDescriptions"]:
resource_auto_scaling[auto_scale_target["ResourceId"]] = True
# Return True if all resources have autoscaling enabled
return all(resource_auto_scaling.values())