configs/configupgrade: Fix up uses of the .count pseudo-attribute

Terraform 0.11 and prior had an odd special case where a resource
attribute access for "count" would be resolved as the count for the
whole resource, rather than as an attribute of an individual instance as
for all other attributes.

Because Terraform 0.12 makes test_instance.foo appear as a list when count
is set (so it can be used in other expressions), it's no longer possible
to have an attribute in that position: lists don't have attributes.
Fortunately we don't really need that special case anymore since it
doesn't do anything we can't now do with the length(...) function.

This upgrade rule, then, detects references like test_instance.foo.count
and rewrites to length(test_instance.foo). As a special case, if
test_instance.foo doesn't have "count" set then it just rewrites as the
constant 1, which mimics what would've happened in that case in Terraform
0.11.
This commit is contained in:
Martin Atkins 2019-02-22 15:55:26 -08:00
parent b3cb94a929
commit dd43926761
4 changed files with 99 additions and 0 deletions

View File

@ -0,0 +1,29 @@
resource "test_instance" "one" {
}
resource "test_instance" "many" {
count = 2
}
data "terraform_remote_state" "one" {
}
data "terraform_remote_state" "many" {
count = 2
}
output "managed_one" {
value = "${test_instance.one.count}"
}
output "managed_many" {
value = "${test_instance.many.count}"
}
output "data_one" {
value = "${data.terraform_remote_state.one.count}"
}
output "data_many" {
value = "${data.terraform_remote_state.many.count}"
}

View File

@ -0,0 +1,29 @@
resource "test_instance" "one" {
}
resource "test_instance" "many" {
count = 2
}
data "terraform_remote_state" "one" {
}
data "terraform_remote_state" "many" {
count = 2
}
output "managed_one" {
value = 1
}
output "managed_many" {
value = length(test_instance.many)
}
output "data_one" {
value = 1
}
output "data_many" {
value = length(data.terraform_remote_state.many)
}

View File

@ -0,0 +1,3 @@
terraform {
required_version = ">= 0.12"
}

View File

@ -224,6 +224,44 @@ Value:
// here so we can normalize and introduce some newer syntax where it's
// safe to do so.
parts := strings.Split(tv.Name, ".")
// First we need to deal with the .count pseudo-attributes that 0.11 and
// prior allowed for resources. These no longer exist, because they
// don't do anything we can't do with the length(...) function.
if len(parts) > 0 {
var rAddr addrs.Resource
switch parts[0] {
case "data":
if len(parts) == 4 && parts[3] == "count" {
rAddr.Mode = addrs.DataResourceMode
rAddr.Type = parts[1]
rAddr.Name = parts[2]
}
default:
if len(parts) == 3 && parts[2] == "count" {
rAddr.Mode = addrs.ManagedResourceMode
rAddr.Type = parts[0]
rAddr.Name = parts[1]
}
}
// We need to check if the thing being referenced is actually an
// existing resource, because other three-part traversals might
// coincidentally end with "count".
if hasCount, exists := an.ResourceHasCount[rAddr]; exists {
if hasCount {
buf.WriteString("length(")
buf.WriteString(rAddr.String())
buf.WriteString(")")
} else {
// If the resource does not have count, the .count
// attr would've always returned 1 before.
buf.WriteString("1")
}
break Value
}
}
parts = upgradeTraversalParts(parts, an) // might add/remove/change parts
first, remain := parts[0], parts[1:]
buf.WriteString(first)