Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The "count" value depends on resource attributes that cannot be determined until apply, but resource attributes are already applied #26078

Closed
dimisjim opened this issue Sep 1, 2020 · 19 comments
Labels
bug explained a Terraform Core team member has described the root cause of this issue in code
Milestone

Comments

@dimisjim
Copy link

dimisjim commented Sep 1, 2020

Terraform Version

0.13.1

Terraform Configuration Files

#################
# at root level #
#################

module "profileapi" {
  source                 = "./modules/s3_bucket"
  bucket_name            = "1plusx-profileapi"
  enable_versioning      = true
  retention_days         = 7
  destination_bucket_arn = module.profileapi-dub.bucket_arn

  providers = {
    aws = aws.frankfurt
  }
}

module "profileapi-dub" {
  source            = "./modules/s3_bucket"
  bucket_name       = "1plusx-profileapi-dub"
  enable_versioning = true
  retention_days    = 7

  providers = {
    aws = aws.dublin
  }
}

output "test" {
  value = module.profileapi-dub.bucket_arn
}

###########################
# inside s3_bucket module #
###########################
locals {
  enable_replication = signum(length(var.destination_bucket_arn)) == 1 ? true : false
  replication_cnt    = signum(length(var.destination_bucket_arn))
}

resource "aws_s3_bucket" "logged_retention_crrd_bucket" {
  count         = var.enable_logging && var.enable_expiration && local.enable_replication && false == local.enable_encryption ? 1 : 0
...
}

resource "aws_s3_bucket" "logged_retention_bucket" {
  count         = var.enable_logging && var.enable_expiration && var.encryption_algorithm == "" && false == local.enable_replication ? 1 : 0
...
}

resource "aws_s3_bucket" "retention_bucket" {
  count         = false == var.enable_logging && var.enable_expiration && var.encryption_algorithm == "" && false == local.enable_replication ? 1 : 0
...
}

resource "aws_s3_bucket" "logged_bucket" {
  count         = var.enable_logging && false == var.enable_expiration && var.encryption_algorithm == "" && false == local.enable_replication ? 1 : 0
...
}

Debug Output

Trace outputs were too large for gist, so used gdrive instead

Snippet of the errors:

Error: Invalid count argument

  on modules/s3_bucket/main.tf line 434, in resource "aws_s3_bucket" "logged_retention_crrd_bucket":
 434:   count         = var.enable_logging && var.enable_expiration && local.enable_replication && false == local.enable_encryption ? 1 : 0

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.

Expected Behavior

As it was in 0.12, the module output should be computed and provided to the root level where it is referenced as one of another's module's inputs, without having to explicitly define an output value with it. Commands like state show correctly output the state that the other module presumes that has never been applied.

Actual Behavior

tf 0.13 thinks that module has never been applied, and therefore its output is not available to another module which uses it as an input.

Steps to Reproduce

  1. Define the outputs and apply to make them available
  2. Run tf plan, which works fine
  3. Remove the outputs, and apply again in order to remove them from the state.
  4. Plan again, now you get "Error: Invalid count argument"

Additional Context

Nothing special in particular.

@dimisjim dimisjim added bug new new issue not yet triaged labels Sep 1, 2020
@shajithravi
Copy link

shajithravi commented Sep 3, 2020

Im facing the same issue with 0.12.29
The resources are already deployed, but I get this error all of a sudden for past few days.

Edit: Im facing this issue after upgrading from 0.12.25 to 0.12.29

@woz5999
Copy link
Contributor

woz5999 commented Sep 4, 2020

same on 0.13.2

@dawidmalina
Copy link

I am having the same issue and I thought that this will be solved by terraform 0.13 and depends_on on module level. But it's not working :(

module "a" {
  source = "./modules/a"
}

module "b" {
  source     = "./modules/b"
  uuid       = module.a.uuid
  depends_on = [module.a]
}

@acdha
Copy link

acdha commented Sep 8, 2020

I ran into another variation: if a module has an output which happens to be empty, it will always hit this error even though it should be perfectly legal to have count=0 for an a optional feature. In my case, it was easy to resolve by changing count = length(module.public_web.instance_ids) to for_each = toset(module.public_web.instance_ids) but it seems like this could be improved.

@alisdair
Copy link
Contributor

@dimisjim Thanks for reporting this!

Unfortunately I am having trouble understanding your report and following the reproduction steps. The configuration you provided seems to be missing some variable values, and is quite complex. So that we can reproduce and fix this issue, can you help me create a smaller reproduction configuration?

Here is what I've tried, which is based on my understanding of your report:

main.tf:

module "a" {
  source    = "./child"
  name      = "a"
  input_ids = ["test"]
}

module "b" {
  source    = "./child"
  name      = "b"
  input_ids = module.a.ids
}

child/main.tf:

variable "name" {
  type = string
}

variable "input_ids" {
  type    = list(string)
  default = []
}

locals {
  enabled = length(var.input_ids) > 0
}

resource "null_resource" "x" {
  count = local.enabled ? 1 : 0
}

output "ids" {
  value = null_resource.x.*.id
}

Unfortunately this successfully plans, even from an empty state. As you can see, the module outputs are not copied through as outputs from the root module, which I think is what you identified as the problem you saw.

Can you modify this configuration to make it similar to your own and reproduce the bug? Failing that, can you simplify your original configuration, ideally using null_resource, and provide an isolation reproduction case?

Thanks in advance for your help!

@alisdair alisdair added waiting for reproduction unable to reproduce issue without further information waiting-response An issue/pull request is waiting for a response from the community and removed new new issue not yet triaged labels Sep 17, 2020
@dimisjim
Copy link
Author

@alisdair Your example indeed works. I also have other modules that take up values from other modules, so the issue that I am having is particular to one type of module co-dependence. Not sure how I could reproduce it extending your example.

@zachwhaley
Copy link
Contributor

I may have reproduced this bug. Check out https://github.com/zachwhaley/tfbug-26078

$ terraform apply
var.create_asc
  Enter a value: false


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.org.null_resource.this will be created
  + resource "null_resource" "this" {
      + id = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.org.null_resource.this: Creating...
module.org.null_resource.this: Creation complete after 0s [id=2709081041753520259]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

$ terraform apply
var.create_asc
  Enter a value: false

module.org.null_resource.this: Refreshing state... [id=2709081041753520259]

Error: Invalid count argument

  on asc/main.tf line 15, in resource "null_resource" "this":
  15:   count = var.create ? 1 : 0

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.

@alisdair
Copy link
Contributor

Thanks, @zachwhaley! 👍 I can reproduce the issue on 0.13.2 and 0.13.3.

The good news is that it appears to be fixed with a build from the latest main branch, due to #26270. I'm going to leave this issue open but expect this issue to be fixed with the release of 0.14.0.

@alisdair alisdair added explained a Terraform Core team member has described the root cause of this issue in code and removed waiting for reproduction unable to reproduce issue without further information waiting-response An issue/pull request is waiting for a response from the community labels Sep 18, 2020
@alisdair alisdair added this to the v0.14.0 milestone Sep 18, 2020
@dawidmalina
Copy link

I still can replicate this issue on terraform v0.14.0-alpha20200923 :(

My main use case is to generate sns in one module and then consume output with sns arn in another module - 2nd module have this argument as a optional (default = "").

To illustrate the use case here are two dummy modules:

module a

resource "random_uuid" "uuid" {}

output "uuid" {
  value = random_uuid.uuid.result
}

module b

variable "uuid" {
  type    = string
  default = ""
}

# this file will be created conditionally if uuid is provided
resource "local_file" "file" {
  count    = var.uuid == "" ? 0 : 1
  content  = var.uuid
  filename = "${path.module}/foo.bar"
}

output "uuid" {
  value = var.uuid
}

usage

module "a" {
  source = "./modules/a"
}

module "b" {
  source = "./modules/b"

  uuid = module.a.uuid

  depends_on = [module.a]
}

output "uuid" {
  value = module.b.uuid
}

@alisdair am I doing something wrong?

@alisdair
Copy link
Contributor

alisdair commented Oct 7, 2020

@dawidmalina Unfortunately the bug was fixed after that alpha release. I've just reconfirmed that it continues to be fixed on the latest pre-release 0.14 code. When we release another alpha (which should be pretty soon!) you should hopefully see your issue fixed too.

@Ninir
Copy link
Contributor

Ninir commented Oct 9, 2020

@alisdair do you know if we can backport this change to the 0.13 stream? a 0.13.5 would be definitely amazing to have 🙇‍♂️ 🙏

@alisdair
Copy link
Contributor

alisdair commented Oct 9, 2020

Unfortunately not—it's part of a very large series of changes which are only feasible to release with 0.14.

The good news is that the upgrade path to 0.14 should be very straightforward from 0.13! There are no significant user-facing breaking changes planned.

@dawidmalina
Copy link

@alisdair my use case is still not covered by terrraform 0.14 beta :(

Terraform v0.14.0-beta1 
+ provider registry.terraform.io/hashicorp/local v2.0.0
+ provider registry.terraform.io/hashicorp/random v3.0.0

Error: Invalid count argument

  on modules/b/main.tf line 7, in resource "local_file" "file":
   7:   count    = var.uuid == "" ? 0 : 1

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the 
resources that the count depends on.

@alisdair
Copy link
Contributor

@dawidmalina I'm not able to reproduce the problem, but I think there might be a misunderstanding here. Partly this is on me for not understanding your reproduction case, but also I think there's confusion about what module depends_on can achieve.

With your configuration, here are my results. An initial apply fails:

$ terraform-0.14.0-beta1 apply

Error: Invalid count argument

  on modules/b/main.tf line 8, in resource "local_file" "file":
   8:   count    = var.uuid == "" ? 0 : 1

The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.

As suggested by the error message, I need to target module.a so that its value can be used in the rest of the config. This works:

$ terraform-0.14.0-beta1 apply -auto-approve -target module.a
module.a.random_uuid.uuid: Creating...
module.a.random_uuid.uuid: Creation complete after 0s [id=683e32fd-8eb0-698d-ef2f-b3cafec97eb8]

Warning: Resource targeting is in effect

You are creating a plan with the -target option, which means that the result
of this plan may not represent all of the changes requested by the current
configuration.

The -target option is not for routine use, and is provided only for
exceptional situations such as recovering from errors or mistakes, or when
Terraform specifically suggests to use it as part of an error message.


Warning: Applied changes may be incomplete

The plan was created with the -target option in effect, so some changes
requested in the configuration may have been ignored and the output values may
not be fully updated. Run the following command to verify that no other
changes are pending:
    terraform plan

Note that the -target option is not suitable for routine use, and is provided
only for exceptional situations such as recovering from errors or mistakes, or
when Terraform specifically suggests to use it as part of an error message.


Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

uuid = "683e32fd-8eb0-698d-ef2f-b3cafec97eb8"

Now the rest of the configuration works:

$ terraform-0.14.0-beta1 apply
module.a.random_uuid.uuid: Refreshing state... [id=a17d4626-5f60-6f74-a5ed-012fae01ff84]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # module.b.local_file.file[0] will be created
  + resource "local_file" "file" {
      + content              = "a17d4626-5f60-6f74-a5ed-012fae01ff84"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "modules/b/foo.bar"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Changes to Outputs:
    # (1 unchanged attribute hidden)

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.b.local_file.file[0]: Creating...
module.b.local_file.file[0]: Creation complete after 0s [id=8bb2c18e5478f379f46e39cdbf59798d2d534f07]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Outputs:

uuid = "a17d4626-5f60-6f74-a5ed-012fae01ff84"
$ ls modules/b/
foo.bar main.tf

The use of depends_on is only for ordering in rare circumstances where Terraform is unable to determine dependency orders. For count and for_each, Terraform requires that the arguments are known at time of plan, and you can't use depends_on to delay this until mid-apply.

With how Terraform works at present, we consider this sort of configuration to be incorrect. Using resource computed outputs as arguments to count and for_each is not recommended. Instead, using variables or derived local values which are known at plan time is the preferred approach. If you choose to go ahead with using computed values for count/for_each, this will sometimes require you to work around this using -target as illustrated above.

The above is perhaps not as clear as it could be, and we could be more prescriptive in the documentation and warnings about this. I hope this explanation helps!

I'm going to close this issue now, as I consider it to be fixed in the 0.14.0-beta, at least under the original definition. Thanks for following up!

@dawidmalina
Copy link

@alisdair thanks for your deep explanation. Are there any plans in long term where such use case would be possible? Using -target is quite difficult to do with fully automated and non-interactive way through ci/cd.

@alisdair
Copy link
Contributor

It's definitely something we'd like Terraform to be able to do in the future, but we don't have any concrete plans for how to achieve that at this time.

@giondo
Copy link

giondo commented Oct 22, 2020

ran into the same issue here
need to use count and depend_on
maybe sometime in the future?

@atrakic
Copy link

atrakic commented Nov 6, 2020

I have followed @alisdair advice and after dropping using computed output within conditional, destroy works as it should on Terraform v0.13.5).

 resource aws_lb_listener "https_alb_listener" {
  ## https://github.com/hashicorp/terraform/issues/26078
  #  Using resource computed outputs as arguments to count and for_each is not recommended.
  ## count             = local.enable_ssl_listener && var.certificate_arn != "" ? 1 : 0  
  count             = local.enable_ssl_listener ? 1 : 0
   load_balancer_arn = join("", aws_lb.this.*.arn)
   port              = "443"
   protocol          = "HTTPS"
  ssl_policy        = local.ssl_policy
  certificate_arn   = var.certificate_arn
  default_action {
    type = "fixed-response"
    fixed_response {
      content_type = "application/json"
      message_body = local.message_body
      status_code  = "200"
    }
  }
  lifecycle {
    ignore_changes = [default_action[0].fixed_response]
  }
}

@ghost
Copy link

ghost commented Nov 16, 2020

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.

@ghost ghost locked as resolved and limited conversation to collaborators Nov 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug explained a Terraform Core team member has described the root cause of this issue in code
Projects
None yet
Development

No branches or pull requests

10 participants