Terraform: Outputs on count=0 resources and null indexed attributes failing in Terraform >=0.12.11

Created on 29 Oct 2019  ยท  10Comments  ยท  Source: hashicorp/terraform

Terraform Version

$ terraform -version
Terraform v0.12.12
+ provider.random v2.2.1

Terraform Configuration Files

resource "random_pet" "this" {
    count = 0
}

output "pet" {
  value = random_pet.this[0].id
}

Expected Behavior

Plan should succeed, with no changes, no outputs. Tested this worked fine in Terraform 0.12.10 and earlier.

Actual Behavior

In Terraform >=0.12.11, plan fails with error message, "The given key does not identify an element in this collection value."

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

Error: Invalid index

  on main.tf line 6, in output "pet":
   6:   value = random_pet.this[0].id
    |----------------
    | random_pet.this is empty tuple

The given key does not identify an element in this collection value.

Steps to Reproduce

  1. terraform init
  2. terraform plan

Most helpful comment

For others that happen upon this issue looking for a workaround, I had the correct syntax on join, according to the docs and functional testing:

join produces a string by concatenating together all elements of a given list of strings with the given delimiter.

The output of join is just a string, so this works fine (joining an empty list with an empty string as the separator creates an empty string). This type of thing was very common in tf0.11 configs:

output "pet" {
  value = join("", random_pet.this.*.id)
}

Though if you want the (pre-tf0.12.11) null behavior back instead, that will require the ternary operator:

output "pet" {
  value = length(random_pet.this) > 0 ? random_pet.this[0].id : null
}

All 10 comments

Hi @lorengordon,

Sorry you ran into this; it should not have been accepted as a valid config in the first place, but there were some places where the errors were missed due to how resources were being evaluated.

The output expression here is directly referencing the first item in the list of random_pet.this instances, but there are no instances hence it's an invalid index. If you want the evaluation to be conditional, you need to explicitly define that condition. Whatever condition that is changing count can be used, or the resource address itself can be checked

value = length(random_pet.this) > 0 ? random_pet.this[0].id : ""

Duplicate of #23140

@teamterraform Pretty sure this type of thing was a result of the terraform 0.12upgrade tool. I'll try to doublecheck that also. But, that workaround is _awful_. So much nicer when nulled resources were just ignored.

Guess we're back to the old tf0.11 hack for outputs on conditional resources:

resource "random_pet" "this" {
    count = 0
}

output "pet" {
  value = join("", random_pet.this.*.id)
}

Also, yes, terraform 0.12upgrade will generate this style output. Seems the indexing in tf0.12 is still not that reliable for creating optional resources. Ignoring nulled resources simplified so much.

$ cat main.tf
resource "random_pet" "this" {
  count = 1
}

output "pet" {
  value = "${random_pet.this.id}"
}
$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12.

We recommend using this command in a clean version control work tree, so that
you can easily see the proposed changes as a diff against the latest commit.
If you have uncommited changes already present, we recommend aborting this
command and dealing with them before running this command again.

Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

The configuration files were upgraded successfully. Use your version control
system to review the proposed changes, make any necessary adjustments, and
then commit.

$ cat main.tf
resource "random_pet" "this" {
  count = 1
}

output "pet" {
  value = random_pet.this[0].id
}

@lorengordon,

As mentioned in the linked issue, previous handling of the expression was not simple a null to be ignored, but it was an invalid expression which resulted in an unknown type and value. This has serious ramifications as it was passed around through other modules, as it could cause resources to be applied with invalid configuration, or the type changes between executions resulting in hard to diagnose problems. In general we prefer to favor the reader and maintainer of the config rather than the writer, and require expressions to be as explicit as possible.

The 0.12upgrade tool is rewriting the syntax correctly in this case, it however cannot assume the intent of your logic. It might be a nice enhancement if it were to detect that the count was statically set to 0 and make the index conditional, but from our experience that is a very rare occurrence within a single module. Better analysis might be feasible, but has to be balanced with the expected lifespan of the tool.

It's just really confusing behavior. Anything without a count gets impacted, such as locals, where before it was fine. It would be nice if the syntax were more consistent.

This is valid, because there is a count on the null_resource:

resource "random_pet" "this" {
  count = 0
}

resource "null_resource" this {
  count = 0

  triggers = {
    pet = random_pet.this[0].id
  }
}

output "pet" {
  value = join("", random_pet.this.*.id)
}

But this is no longer is, with tf >=0.12.11:

resource "random_pet" "this" {
  count = 0
}

locals {
  pet = random_pet.this[0].id
}

resource "null_resource" this {
  count = 0

  triggers = {
    pet = local.pet
  }
}

output "pet" {
  value = join("", random_pet.this.*.id)
}

It was a big improvement as part of the tf0.11 to tf0.12 upgrade to be able to use the same 0-index syntax everywhere. Very frustrating that was considered a bug to revert instead of a feature to protect.

This example also fails in tf>=0.12.11. Can't pass 0-index references through modules, even when the module-resource uses count. This is really starting to pinch.

$ cat foo/main.tf
variable "prefix" {}

resource "random_id" "this" {
  count = 0

  byte_length = 10
  prefix      = var.prefix
}

$ cat main.tf
resource "random_pet" "this" {
  count = 0
}

module "foo" {
  source = "./foo"

  prefix = random_pet.this[0].id
}

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

Error: Invalid index

  on main.tf line 8, in module "foo":
   8:   prefix = random_pet.this[0].id
    |----------------
    | random_pet.this is empty tuple

The given key does not identify an element in this collection value.

For others that happen upon this issue looking for a workaround, I had the correct syntax on join, according to the docs and functional testing:

join produces a string by concatenating together all elements of a given list of strings with the given delimiter.

The output of join is just a string, so this works fine (joining an empty list with an empty string as the separator creates an empty string). This type of thing was very common in tf0.11 configs:

output "pet" {
  value = join("", random_pet.this.*.id)
}

Though if you want the (pre-tf0.12.11) null behavior back instead, that will require the ternary operator:

output "pet" {
  value = length(random_pet.this) > 0 ? random_pet.this[0].id : null
}

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 have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

Was this page helpful?
0 / 5 - 0 ratings