Bizarre The "count" value depends on resource attributes that cannot be determined until apply error

So I have module that has the following in it

data "aws_iam_policy_document" "role_policy" {
  statement {
    sid    = "AWSVPCFlowLogs"
    effect = "Allow"

    resources = ["*"]

    actions = [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
      "logs:DescribeLogGroups",
      "logs:DescribeLogStreams"
    ]
  }

I then call a second module pass as one of its parameters the following:

iam_policy_json      = data.aws_iam_policy_document.role_policy.json

Inside the second module it has the following line which is throwing the error

data "aws_iam_policy_document" "managed_policy" {
  count = length(var.iam_policy_json) > 0 ? 1 : 0

To me everything looks correct and the error been throw is wrong as everything is known at plan time

1 Like

Hi @DaveO,

Can you share the full output of Terraform when you run terraform plan, including any planned actions and the exact error message you saw? The plan output often gives some hints about why values are unknown or why actions are deferred until the apply phase, but it sometimes takes practice to read and understand that information so I’d like to try to help explain what Terraform is reporting.

There’s something relevant you’re not showing us about your configuration, because if I copy/paste what you’ve given into files, and add the minimum necessary glue to make it a valid configuration, then terraform plan runs just fine.

ok, for completeness here if the code in module 1

module "role" {
  count  = var.publish_flow_log_to_cloud_watch ? 1 : 0

  should_create_iam_role = true
  should_require_mfa     = false
  iam_role_name          = local.role_name

  iam_role_permissions_boundary = var.cloudwatch_iam_role_permissions_boundary

  assume_role_services = ["vpc-flow-logs.amazonaws.com"]
  assume_role_custom_conditions = {
    cond1 = {
      test     = "StringEquals"
      values   = ["${data.aws_caller_identity.current.account_id}"]
      variable = "aws:SourceAccount"
    },
    cond2 = {
      test     = "ArnLike"
      values   = ["arn:aws:ec2:*:${data.aws_caller_identity.current.account_id}:vpc-flow-log/${aws_flow_log.cloudwatch[0].arn}"]
      variable = "aws:SourceArn"
    }
  }

  custom_iam_policy      = !var.use_managed_iam_policies ? data.aws_iam_policy_document.role_policy.json : ""
  custom_iam_policy_name = local.role_name

  iam_policy_json      = var.use_managed_iam_policies ? data.aws_iam_policy_document.role_policy.json  : ""
  iam_policy_json_name = local.role_name

  tags = merge(var.tags, local.tags)
}

data "aws_iam_policy_document" "role_policy" {
  statement {
    sid    = "AWSVPCFlowLogs"
    effect = "Allow"

    resources = ["*"]

    actions = [
      "logs:CreateLogGroup",
      "logs:CreateLogStream",
      "logs:PutLogEvents",
      "logs:DescribeLogGroups",
      "logs:DescribeLogStreams"
    ]
  }
}

In the Role Module this is the resource where the error is been raised:

data "aws_iam_policy_document" "managed_policy" {
  count = ((var.should_create_iam_group && var.iam_group_assume_role_arns != null) || length(var.iam_policy_json) > 0 || var.should_require_mfa) ? 1 : 0 #LINE 86

  source_policy_documents = [
    length(data.aws_iam_policy_document.assume_roles) > 0 ? data.aws_iam_policy_document.assume_roles[0].json : "",
    length(var.iam_policy_json) > 0 ? var.iam_policy_json : "",
    var.should_require_mfa && var.should_create_iam_group ? module.iam_policies.require_mfa_policy : ""
  ]
}

*note
var.should_create_iam_group defaults to false
var.iam_group_assume_role_arns defaults to null

Here is the TF output

Error: Invalid count argument
on .terraform/modules/baseline.mod1/modules/mod2/main.tf line 86, in data "aws_iam_policy_document" "managed_policy":
  count = ((var.should_create_iam_group && var.iam_group_assume_role_arns != null) || length(var.iam_policy_json) > 0 || var.should_require_mfa) ? 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.

The fix i had to apply was in the original call to the module, i changed “iam_policy_json = data.aws_iam_policy_document.role_policy.json” into

  iam_policy_json = var.use_managed_iam_policies ? jsonencode({
    "Version" : "2012-10-17",
    "Statement" : [
      {
        "Effect" : "Allow",
        "Action" : [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "logs:DescribeLogStreams"
        ],
        "Resource" : [
          "arn:aws:logs:*:*:*"
        ]
      }
    ]
  }) : ""

Still can’t reproduce it … I took what you posted, added in missing variables, chopped out unreferenced bits: GitHub - maxb/aws-count-repro … run terraform plan and it works.

Is this for a slightly older version of Terraform? The error output is missing some details I would’ve expected so I wonder if you are using an older version that wasn’t yet able to generate as much debug information in error messages.

With that said, I think the problem here is probably related to the policy document including a reference to aws_flow_log.cloudwatch[0].arn, which probably isn’t known yet during initial planning because the AWS provider tends to populate ARNs only during the apply phase.

That means the entire resulting policy document is unknown (since a JSON document can’t have an unknown substring in the middle of it), and so this part of your count expression will be unknown itself:

length(var.iam_policy_json) > 0

Although us humans know that the resulting policy document can’t possibly be empty because we know how IAM policy documents work, Terraform itself cannot prove that since it doesn’t know what an IAM policy document is and only sees this as a general string.

Can you find a different way to express this that doesn’t require directly testing the dynamic policy document?

For example, one way to do it would be to change your input variable to take an object with a single attribute that contains the policy document:

variable "iam_policy" {
  type = object({
    json = string
  })
  default = null
}

With it structured this way, the variable as a whole can be known to be non-null even if the json attribute inside is unknown, so you can test var.iam_policy != null instead of directly testing the length of the (not yet known) string.

This running in TFC using the latest version and providers for terrform and aws are both configured to use the latest version as well

Im not sure how you came to that conclusion the use of aws_flow_log.cloudwatch[0].arn is in the assume_role_custom_conditions parameter.

Taking the line in question
count = ((var.should_create_iam_group && var.iam_group_assume_role_arns != null) || length(var.iam_policy_json) > 0 || var.should_require_mfa) ? 1 : 0

substituting the variables it becomes
count = ((false && null != null) || length(var.iam_policy_json) > 0 || false) ? 1 : 0

given var.iam_policy_json is just a string populated from the aws_iam_policy_document resource and has no dynamic\computed content then i see no reason the error should occur

@DaveO,

The hint was that you “fixed” the problem by removing data.aws_iam_policy_document.role_policy.json from the iam_policy_json, which means that the data source was not known during the plan. If the data source has an unknown input, or depends on any changes in a managed resource, then it cannot be read until it’s dependencies have been resolved.

The complete plan output would probably include some more information about the status of that data source.

@jbardin

I completely agree and get that however if you look at my original post, this is how the data resource was defined its completely static with nothing dynamic about it.
‘’'data “aws_iam_policy_document” “role_policy” {
statement {
sid = “AWSVPCFlowLogs”
effect = “Allow”

resources = ["*"]

actions = [
  "logs:CreateLogGroup",
  "logs:CreateLogStream",
  "logs:PutLogEvents",
  "logs:DescribeLogGroups",
  "logs:DescribeLogStreams"
]

}
}‘’’

What does the actual plan output show for that data source?
Does that data source happen to use depends_on, or be in a module which uses depends_on?

I am having the exact same issue out of the blue on modules that has been working long time now.

Inside the module I am using the following count line.

count = var.policy == "" ? 0 : 1

–
Tested with AWS provider 4.15 & 4.65. Getting the same issue :face_in_clouds:

@siourdas-vasilis Start a new topic please, and show your entire configuration, and actual specific error messages - refer to Guide to asking for help in this forum. This message is highly dependent on individual users’ configurations, and this topic is already quite long already discussing the OP’s configuration.

1 Like

@jbardin your a genius, my module had a depends on further up the stack and once removed works perfectly now