DynamoDB Global Secondary Index Autoscaling with Cloudformation

Recently, I wanted to bake my DynamoDB table setup into a CloudFormation template. The table contained a global secondary index and is an autoscaling target for read and write capacity scaling.
Registering autoscaling with a DynamoDB table in Cloudformation isn’t hard at all. Yet it is not immediately obvious how to register the index as scalable target. The documentation did not go into detail about index autoscaling, so how do we do it?

First, we will register the table itself as scalable target for write capacity and then attach a policy to it. This isn’t strictly necessary
to enable scaling for the index, but why would you want to autoscale the index and not the table itself?
The following CloudFormation snipped will do the trick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Table:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: !Ref TableName
AttributeDefinitions:
- AttributeName: 'id'
AttributeType: 'S'
- AttributeName: 'timestamp'
AttributeType: 'N'
- AttributeName: 'productgroup'
AttributeType: 'S'
KeySchema:
- KeyType: 'HASH'
AttributeName: 'id'
- KeyType: 'RANGE'
AttributeName: 'timestamp'
GlobalSecondaryIndexes:
- IndexName: !Sub '${TableName}-index'
KeySchema:
- KeyType: 'HASH'
AttributeName: 'productgroup'
Projection:
ProjectionType: 'KEYS_ONLY'
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2
ProvisionedThroughput:
ReadCapacityUnits: 2
WriteCapacityUnits: 2

WriteScalableTarget:
DependsOn: Table
Type: "AWS::ApplicationAutoScaling::ScalableTarget"
Properties:
MaxCapacity: 10
MinCapacity: 2
ResourceId: !Sub 'table/${Table}'
RoleARN: !GetAtt ScalingRole.Arn
ScalableDimension: dynamodb:table:WriteCapacityUnits
ServiceNamespace: dynamodb

WriteScalingPolicy:
DependsOn: Table
Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
Properties:
PolicyName: WriteScalingPolicy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref WriteScalableTarget
TargetTrackingScalingPolicyConfiguration:
TargetValue: 50.0
ScaleInCooldown: 70
ScaleOutCooldown: 70
PredefinedMetricSpecification:
PredefinedMetricType: DynamoDBWriteCapacityUtilization

That will scale the write capacity between two to ten units targeting a value of 50% consumption.
But what about the global secondary index? The given template snipped won’t register it as scalable target.
Two questions arise: what is the scalable dimension of a global secondary index, and what is its resource identifier?
The answers are in the following snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
IndexWriteScalableTarget:
DependsOn: Table
Type: "AWS::ApplicationAutoScaling::ScalableTarget"
Properties:
MaxCapacity: 10
MinCapacity: 2
ResourceId: !Sub 'table/${Table}/index/${TableName}-index'
RoleARN: !GetAtt ScalingRole.Arn
ScalableDimension: dynamodb:index:WriteCapacityUnits
ServiceNamespace: dynamodb

IndexWriteScalingPolicy:
DependsOn: Table
Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
Properties:
PolicyName: IndexWriteScalingPolicy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref IndexWriteScalableTarget
TargetTrackingScalingPolicyConfiguration:
TargetValue: 50.0
ScaleInCooldown: 70
ScaleOutCooldown: 70
PredefinedMetricSpecification:
PredefinedMetricType: DynamoDBWriteCapacityUtilization

The resource identifier has the format table/${Table}/index/MyIndex, the scalable dimension is dynamodb:index:WriteCapacityUnits
for write capacity scaling and dynamodb:index:ReadCapacityUnits for read capacity scaling.
You can find the full template here, where the ScalingRole needed for the scaling to work is defined as well.

So that’s it, thank you for reading!