_This issue was originally opened by @billwang-au as hashicorp/terraform#14106. It was migrated here as part of the provider split. The original body of the issue is below._
Hi there,
v0.9.x
Please list the resources as a list, for example:
Get from sample https://www.terraform.io/docs/providers/aws/r/dynamodb_table.html#example-usage
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
read_capacity = 20
write_capacity = 20
hash_key = "UserId"
range_key = "GameTitle"
attribute {
name = "UserId"
type = "S"
}
attribute {
name = "GameTitle"
type = "S"
}
attribute {
name = "TopScore"
type = "N"
}
global_secondary_index {
name = "GameTitleIndex"
hash_key = "GameTitle"
range_key = "TopScore"
write_capacity = 10
read_capacity = 10
projection_type = "INCLUDE"
non_key_attributes = ["UserId"]
}
tags {
Name = "dynamodb-table-1"
Environment = "production"
}
}
change from attribute
to attributes
or add a new argument attributes
, so I can feed the AttributeDefinitions
in one shot.
Copy the code from Cloudformation for easily understanding my request.
"AttributeDefinitions" : [
{
"AttributeName" : "Album",
"AttributeType" : "S"
},
{
"AttributeName" : "Artist",
"AttributeType" : "S"
},
{
"AttributeName" : "Sales",
"AttributeType" : "N"
},
{
"AttributeName" : "NumberOfSongs",
"AttributeType" : "N"
}
],
Then I can change to
"AttributeDefinitions" : "${file("templates/dynamodb.tpl")}"
1) I have to feed attribute one by one.
2) I can't customize a dynamoDB module because the attributes are different between applications.
3) I'd like to write a shared dynamoDB module, import attributes from a file
This would be a very useful feature for creating more generic dynamo db modules. For example, this cool-looking module from CloudPosse is harder to reuse because it only creates two attributes
I’ll fix that.
I’ve added a pull request to the cloudposse repo you referenced that allows dynamic attributes now.
@ozbillwang @MichaelPereira
Ill show you something you can do here:
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
read_capacity = 20
write_capacity = 20
hash_key = "UserId"
range_key = "GameTitle"
attribute = [{
name = "UserId"
type = "S"
}, {
name = "GameTitle"
type = "S"
}, {
name = "TopScore"
type = "N"
}, {
name = "OzWang"
type = "S"
}]
global_secondary_index = [{
name = "GameTitleIndex"
hash_key = "GameTitle"
range_key = "TopScore"
write_capacity = 10
read_capacity = 10
projection_type = "INCLUDE"
non_key_attributes = ["UserId"]
},
{
name = "WangIndex"
hash_key = "OzWang"
write_capacity = 10
read_capacity = 10
projection_type = "KEYS_ONLY"
}]
tags {
Name = "dynamodb-table-1"
Environment = "production"
}
}
As per above, you are already able to do this, by treating the fields as lists - as long as the attributes have a use.
I have submitted this pull request to CloudPosse, but it shows an example of how to use it in a module.
On terraforms site they say they can't add unused dynamodb attributes
A note about attributes
Only define attributes on the table object that are going to be used as:Table hash key or range key
LSI or GSI hash key or range key
The DynamoDB API expects attribute structure (name and type) to be passed along when creating or updating GSI/LSIs or creating the initial table. In these cases it expects the Hash / Range keys to be provided; because these get re-used in numerous places (i.e the table's range key could be a part of one or more GSIs), they are stored on the table object to prevent duplication and increase consistency. If you add attributes here that are not used in these scenarios it can cause an infinite loop in planning.
Thanks, @Jamie-BitFlight
Will this PR generate any incompatible issue to previous codes using this resource?
Oh, I see. You mean this feature is there already. Seems the only thing we need do is to update its document and example usage.
Hi @ozbillwang,
I just made it to be backwards compatible.
@ozbillwang this is a bit of a hidden feature on all resources.
Anything that is a repeated block like that is actually a list of maps.
And you can use this same trick on it.
Such as this example https://github.com/Jamie-BitFlight/terraform-null-label
Where the trick is used for adding the tags to an ASG.
or here:
https://github.com/Jamie-BitFlight/terraform-aws-codebuild
Where the trick is used to dynamically extend the environment variables passed to codebuild.
@Jamie-BitFlight Thanks a lot for the information! I believe this PR can be closed 🙂
I can confirm this works. Thanks a lot
attribute = [{
name = "UserId"
type = "S"
}, {
name = "GameTitle"
type = "S"
}, {
name = "TopScore"
type = "N"
}]
The ability to treat a nested block as if it were an attribute was never an intentional feature of the language, and actually stops working properly as soon as the example gets more complex to include computed values, etc. This was working only because HCL happens to use approximately the same Go types to represent child blocks as the interpolation language uses for lists of maps, but the representations are not 100% exact and so would fail as soon as you tread into a situation where they don't match.
The next major version of Terraform will include some first-class syntax for doing this:
locals {
dynamodb_attributes = {
UserId = "S"
GameTitle = "S"
TopScore = "N"
}
}
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
# ...
dynamic "attribute" {
for_each = local.dynamodb_attributes
content {
name = attribute.key
type = attribute.value
}
}
}
The automatic upgrade tool mentioned in the blog post I linked above will be able to adjust this automatically in many cases, but the result may need some tweaking if the expression being assigned is very dynamic/complex.
In the initial issue write-up here it looks like the ultimate goal was to derive the set of attributes from a cloudformation-alike JSON file, in which case the other forthcoming 0.12 features of a jsondecode
function and _for
expressions_ may help:
resource "aws_dynamodb_table" "basic-dynamodb-table" {
name = "GameScores"
# ...
dynamic "attribute" {
for_each = jsondecode(file("${path.module}/attributes.json")).AttributeDefinitions
content {
name = attribute.value.AttributeName
type = attribute.value.AttributeType
}
}
}
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!
Most helpful comment
I’ll fix that.