Terraform-provider-aws: Feature request: DynamoDB Autoscaling for GSIs

Created on 12 Dec 2017  ·  19Comments  ·  Source: hashicorp/terraform-provider-aws

Hi,

I recently added aws_appautoscaling_target and aws_appautoscaling_policy resources to my terraform to turn on autoscaling for a DynamoDB table. However, there is no option to add the same autoscaling policy to the table's GSIs as well.

In DynamoDB's UI, they have the option "Apply same settings to global secondary indexes". Can we add this to the terraform resource?

If I make another set of aws_appautoscaling_target/aws_appautoscaling_policy for the index specifically, I get the following warning in DynamoDB's UI: "Auto Scaling settings already exist for global secondary indexes. Any changes made will overwrite current settings."

It would be really helpful to just apply the same autoscaling policy to the table and its GSIs.

Thank you,
Arman

question servicdynamodb

Most helpful comment

Slightly highjacking the thread, but:

is there a way to manage GSI minimum capacity with auto-scaling on?

The documentation currently says:

Note: It is recommended to use lifecycle ignore_changes for read_capacity and/or write_capacity if there's autoscaling policy attached to the table.

This is a bit cumbersome, but it becomes even weirder with GSIs.

(I think) we're forced to do:

lifecycle {
    ignore_changes = ["read_capacity", "write_capacity", "global_secondary_index"]
}

otherwise, Terraform will reset the capacity on every run.

But then when adding more GSIs/modifying the current one we have to do that strange dance of:

  • disabling the ignore_changes,
  • manually setting the capacity in terraform config to whatever auto-scaling thinks it should be,
  • adding more indexes,
  • applying changes,
  • re-ignoring the global_secondary_indexes via ignore_changes.

It feels very awkward, almost like GSI should be a separate resource, maybe?

All 19 comments

@armanshan12 Are you able to actually create a set of auto-scaling targets for the index? I tried to create them (using the table/my-table/index/my-table-index resource-id as specified in the AWS docs) but I get an error from terraform

aws_appautoscaling_target.dynamodb_index_read_target: Error creating application autoscaling target: ValidationException: Unsupported service namespace, resource type or scalable dimension

I can. Can you share your resource?

On Jan 15, 2018, at 11:43 PM, dewang notifications@github.com wrote:

@armanshan12 Are you able to actually create a set of auto-scaling targets for the index? I tried to create them (using the table/my-table/index/my-table-index resource-id as specified in the AWS docs) but I get an error from terraform

aws_appautoscaling_target.dynamodb_index_read_target: Error creating application autoscaling target: ValidationException: Unsupported service namespace, resource type or scalable dimension


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

Was just about to share and noticed my mistake! I had not changed the scalable_dimension when I copied it from my table target. Needs to be set to dynamodb:index:ReadCapacityUnits

Is there any update on this.? How can we perform dynamodb index autoscaling using terraform..?

Is there any update on this.? How can we perform dynamodb index autoscaling using terraform..?

My experiences:-
I need to update tables and index values via Terraform, Possible to create/update autoscaling for tables, not possible for indexs.
If we want to use same values for tables and indexs, Once we applied the tables auto-scaling changes then we need to click "save" button in AWS DynamoDB console. Then only the values will effect for Indexs. Even "apply same setting to global sec index" is ticked we have to click "save" button. Other wise, tables R/W values will not effect index.

Note:- Once you saved, you can verify the index changes in AWS CLI commands. In console index values will show different because these R/W values will changes while Auto-scaling time.
If you keep on add items in tables then you will get changes in index values after 15 mins.

Hi everyone! Its currently possible to do this and has been for a few versions of the AWS provider now. As of yesterday, the documentation at least on the aws_appautoscaling_target resource was updated to reflect a DynamoDB index example: https://www.terraform.io/docs/providers/aws/r/appautoscaling_target.html#dynamodb-index-autoscaling

Below is a repost of my response from another ticket: https://github.com/hashicorp/terraform/issues/15470#issuecomment-360551708

...it can be accomplished with a configuration such as:

resource "aws_appautoscaling_target" "dynamodb_index_read_target" {
  max_capacity       = 100
  min_capacity       = 5
  resource_id        = "table/${aws_dynamodb_table.example.name}/index/YourIndexName"
  role_arn           = "${data.aws_iam_role.DynamoDBAutoscaleRole.arn}"
  scalable_dimension = "dynamodb:index:ReadCapacityUnits"
  service_namespace  = "dynamodb"
}

The same works for dynamodb:index:WriteCapacityUnits. To achieve the functionality in the web console that is "Apply same settings to global secondary indexes", I would suggest creating variables that can be used for min/max_capacity or you can pull the attribute from a table target if you have one defined. e.g.

resource "aws_appautoscaling_target" "dynamodb_table_read_target" {
  max_capacity       = 100
  min_capacity       = 5
  resource_id        = "table/${aws_dynamodb_table.example.name}"
  role_arn           = "${data.aws_iam_role.DynamoDBAutoscaleRole.arn}"
  scalable_dimension = "dynamodb:table:ReadCapacityUnits"
  service_namespace  = "dynamodb"
}

resource "aws_appautoscaling_target" "dynamodb_index_read_target" {
  max_capacity       = "${aws_appautoscaling_target.dynamodb_table_read_target.max_capacity}"
  min_capacity       = "${aws_appautoscaling_target.dynamodb_table_read_target.min_capacity}"
  resource_id        = "table/${aws_dynamodb_table.example.name}/index/YourIndexName"
  role_arn           = "${data.aws_iam_role.DynamoDBAutoscaleRole.arn}"
  scalable_dimension = "dynamodb:index:ReadCapacityUnits"
  service_namespace  = "dynamodb"
}

Does this answer your questions or help your situation? Can we improve the documentation around this better?

Terraform is passing malformed way for index, so that AWS not accepting the index value alone. but DynamoDB autoscaling tables values are reflecting in AWS console.

I have created case with AWS, they suggest to use AWS CLI or use same values for both tables and Index.

Cloudtrail log:-
"errorCode": "ValidationException",
"errorMessage": "2 validation errors detected: Value '-1' at 'provisionedThroughput.writeCapacityUnits' failed to satisfy constraint: Member must have value greater than or equal to 1; Value '-1' at 'provisionedThroughput.readCapacityUnits' failed to satisfy constraint: Member must have value greater than or equal to 1",
"requestParameters": {
"provisionedThroughput": {
"writeCapacityUnits": -1,
"readCapacityUnits": -1
},

I have achieved in one way.. For tables auto-scaling i used terraform resources
https://www.terraform.io/docs/providers/aws/r/appautoscaling_target.html

For Indexs auto-scaling, I used AWS CLI commands in terrafrom
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.CLI.html
resource "null_resource" "ddb_index_register_target" {
provisioner "local-exec" {
command = "aws --region ${var.aws_region} --profile ${var.aws_profile} application-autoscaling register-scalable-target --service-namespace dynamodb --resource-id table/tables-name/index/index-name --scalable-dimension dynamodb:index:ReadCapacityUnits --min-capacity 5 --max-capacity 100 --role-arn full-role-arn"
}
}

resource "null_resource" "ddb_index_scalingpolicy" {
provisioner "local-exec" {
command = "aws --region ${var.aws_region} --profile ${var.aws_profile} application-autoscaling put-scaling-policy --service-namespace dynamodb --resource-id table/tables-name/index/index-name --scalable-dimension dynamodb:index:ReadCapacityUnits --policy-name index-read-policy --policy-type TargetTrackingScaling --target-tracking-scaling-policy-configuration file://index-read-scaling-policy.json"
}
}

Its working fine for me.

Can you please provide the aws_appautoscaling_target configuration you tried (or a larger portion of your configuration if necessary) and the Terraform core/provider versions you are running via terraform version? The above workarounds should not be necessary.

So I have two pairs of aws_appautoscaling_target and aws_appautoscaling_policy, one pair for the table itself, and the other pair for the GSI of the table.

Don't get me wrong, it works perfectly fine. The autoscaling is setup for both table and the GSI, but If I go to the Capacity tab of the table in AWS console, I see the following warning:

Auto Scaling settings already exist for global secondary indexes. Any changes made will overwrite current settings.

Not that I will use the console to update the settings, but I don't understand why this warning shows up when creating the autoscaling stuff, but not when you create the autoscaling stuff through the console.

Anyone else seeing that warning?

Although I don't like the solution that I come up with, the following seems to work. I use the var.as_read_min, var.as_write_min, var.as_read_target_util and var.as_write_target_util variables in the table definition. (Terraform v0.10.8)

resource "aws_appautoscaling_target" "dynamodb_index_read_target" {
  max_capacity = "${var.as_read_min}"
  min_capacity = "${var.as_read_min}"
  resource_id = "table/${aws_dynamodb_table.table_name.id}/index/index_name"
  role_arn = "${var.iam_autoscaling_role_arn}"
  scalable_dimension = "dynamodb:index:ReadCapacityUnits"
  service_namespace = "dynamodb"
}

resource "aws_appautoscaling_policy" "dynamodb_index_read_policy" {
  name = "DynamoDBReadCapacityUtilization:${aws_appautoscaling_target.dynamodb_index_read_target.resource_id}"
  policy_type = "TargetTrackingScaling"
  resource_id = "${aws_appautoscaling_target.dynamodb_index_read_target.resource_id}"
  scalable_dimension = "${aws_appautoscaling_target.dynamodb_index_read_target.scalable_dimension}"
  service_namespace = "${aws_appautoscaling_target.dynamodb_index_read_target.service_namespace}"

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "DynamoDBReadCapacityUtilization"
    }
    target_value = "${var.as_read_target_util}"
  }
}

resource "aws_appautoscaling_target" "dynamodb_index_write_target" {
  max_capacity = "${var.as_write_min}"
  min_capacity = "${var.as_write_min}"
  resource_id = "table/${aws_dynamodb_table.table_name.id}/index/index_name"
  role_arn = "${var.iam_autoscaling_role_arn}"
  scalable_dimension = "dynamodb:index:WriteCapacityUnits"
  service_namespace = "dynamodb"
}

resource "aws_appautoscaling_policy" "dynamodb_index_write_policy" {
  name = "DynamoDBWriteCapacityUtilization:${aws_appautoscaling_target.dynamodb_index_write_target.resource_id}"
  policy_type = "TargetTrackingScaling"
  resource_id = "${aws_appautoscaling_target.dynamodb_index_write_target.resource_id}"
  scalable_dimension = "${aws_appautoscaling_target.dynamodb_index_write_target.scalable_dimension}"
  service_namespace = "${aws_appautoscaling_target.dynamodb_index_write_target.service_namespace}"

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "DynamoDBWriteCapacityUtilization"
    }
    target_value = "${var.as_write_target_util}"
  }
}

@adys my code looks identical (with the exception of variable names), but I still do get the warnings. Do they not show up for you?

I have done a little digging between what my code does and what AWS Console does. I've compared results of API calls (done by the AWS Console).

The only difference I can see is in the result of com.amazonaws.console.dynamodbv2.shared.AnyScaleRequestContext.describeScalingPolicies - for a resource created with TF, there are two additional fields: "scaleOutCooldown":0,"scaleInCooldown":0. They do not exist for resources created manually in the Console.
The whole nested object is:

          "targetTrackingScalingPolicyConfiguration": {
            "targetValue": 70,
            "predefinedMetricSpecification": {
              "predefinedMetricType": "DynamoDBReadCapacityUtilization"
            },
            "scaleOutCooldown": 0,
            "scaleInCooldown": 0
          },

Explicitly setting them to any values (in TF) doesn't prevent the warning from appearing.

When the Console creates the GSI and creates a scaling policy, it does not specify any values for those two fields. However, when TF creates that scaling policy, it does specify zero's. That, to me, seems to be the only difference.

Hi @bflad ,
Is there any update about "In DynamoDB's UI, they have the option "Apply same settings to global secondary indexes". Can we add this to the terraform resource?"

I have also spoken to AWS regarding the error message (Auto Scaling settings already exist for global secondary indexes. Any changes made will overwrite current settings.).

Their answer is:

The way we detect this presently is based on a custom AutoScale 'ScalingPolicy' name. Console has the scaling policy named under a specific convention -- if divergent, this warning will be present.

And that's where the message is coming from.

No ETA on fixing that behaviour.

Slightly highjacking the thread, but:

is there a way to manage GSI minimum capacity with auto-scaling on?

The documentation currently says:

Note: It is recommended to use lifecycle ignore_changes for read_capacity and/or write_capacity if there's autoscaling policy attached to the table.

This is a bit cumbersome, but it becomes even weirder with GSIs.

(I think) we're forced to do:

lifecycle {
    ignore_changes = ["read_capacity", "write_capacity", "global_secondary_index"]
}

otherwise, Terraform will reset the capacity on every run.

But then when adding more GSIs/modifying the current one we have to do that strange dance of:

  • disabling the ignore_changes,
  • manually setting the capacity in terraform config to whatever auto-scaling thinks it should be,
  • adding more indexes,
  • applying changes,
  • re-ignoring the global_secondary_indexes via ignore_changes.

It feels very awkward, almost like GSI should be a separate resource, maybe?

Is there any way around this? Im trying to create a module for creating a dynamodb table with GSI scaling and its currently impossible.

ignore_changes doesn't allow for interpolated values so that sucks. I cant switch it on and off in a module which means I cant use a module at all. have to have big configs for all DDB tables and ignore/un-ignore "global_secondary_index"

Thank you for using Terraform and for opening up this question! Issues on GitHub are intended to be related to bugs or feature requests with the provider codebase.

As there have been multiple workarounds and solutions posted, this issue will now be closed. If needed, please use https://discuss.hashicorp.com/c/terraform-providers for additional feedback, community discussions, and questions around Terraform.

If you believe that your issue was miscategorized as a question or closed in error, please create a new issue using one of the following provided templates: bug report or feature request. Please make sure to provide us with the information requested in the template as well as any additional appropriate information so we can best determine how to assist with the given issue.

I'm going to lock this issue because it has been closed for _30 days_ ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!

Was this page helpful?
0 / 5 - 0 ratings