Data aws_iam_policy_document and for_each showing changes on every plan and nothing on apply

So, I have some IAM policies I am building with for_each which are then used as assume_role_policy and aws_iam_policy but on every plan:

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

and then apply:

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

Some details:

$ tf version
Terraform v0.13.3
+ provider instaclustr/instaclustr/instaclustr v1.4.1
+ provider registry.terraform.io/hashicorp/aws v3.7.0
+ provider registry.terraform.io/hashicorp/helm v1.3.0
+ provider registry.terraform.io/hashicorp/kubernetes v1.13.2
+ provider registry.terraform.io/hashicorp/local v1.4.0
+ provider registry.terraform.io/hashicorp/null v2.1.2
+ provider registry.terraform.io/hashicorp/random v2.3.0
+ provider registry.terraform.io/hashicorp/template v2.1.2
+ provider registry.terraform.io/hashicorp/tfe v0.21.0

I will just focus on one resource, the roles:

resource aws_iam_role this {
  for_each             = local.k8s_sa_to_iam_roles_indexed

  name                 = "${each.value.name}-${var.cluster_name}"

  max_session_duration = 43200
  assume_role_policy   = data.aws_iam_policy_document.this[each.key].json

  tags = merge(local.tags, {"app" = each.value.name})
}

and the aws_iam_policy_document data source:

data aws_iam_policy_document this {
  for_each = local.k8s_sa_to_iam_roles_indexed

  statement {
    principals {
      type        = "Federated"
      identifiers = [ var.oidc_arn ]
    }

    actions = [
      "sts:AssumeRoleWithWebIdentity",
    ]

    condition {
      test      = "StringEquals"
      variable  = "${var.oidc_url}:sub"
      values = [
        "system:serviceaccount:namespace:${each.value.name}"
      ]
    }
  }
}

but on every plan:

# module.applications["0"].data.aws_iam_policy_document.this["0"] will be read during apply
  # (config refers to values not yet known)
 <= data "aws_iam_policy_document" "this"  {
      ~ id      = "2618924450" -> (known after apply)
      ~ json    = jsonencode(
            {
              - Statement = [
                  - {
                      - Action    = "sts:AssumeRoleWithWebIdentity"
                      - Condition = {
                          - StringEquals = {
                              - oidc.eks.ap-southeast-2.amazonaws.com/id/XXXX:sub = "system:serviceaccount:namespace:app"
                            }
                        }
                      - Effect    = "Allow"
                      - Principal = {
                          - Federated = "arn:aws:iam::XXXX:oidc-provider/oidc.eks.ap-southeast-2.amazonaws.com/id/XXXX"
                        }
                      - Sid       = ""
                    },
                ]
              - Version   = "2012-10-17"
            }
        ) -> (known after apply)
      - version = "2012-10-17" -> null

      ~ statement {
            actions       = [
                "sts:AssumeRoleWithWebIdentity",
            ]
          - effect        = "Allow" -> null
          - not_actions   = [] -> null
          - not_resources = [] -> null
          - resources     = [] -> null

            condition {
                test     = "StringEquals"
                values   = [
                    "system:serviceaccount:namespace:app",
                ]
                variable = "oidc.eks.ap-southeast-2.amazonaws.com/id/XXXX:sub"
            }

            principals {
                identifiers = [
                    "arn:aws:iam::XXXX:oidc-provider/oidc.eks.ap-southeast-2.amazonaws.com/id/XXXX",
                ]
                type        = "Federated"
            }
        }
    }

I assume it is something I am doing wrong, I can find others doing this without issue but I don’t see it. Any thoughts?

this now seems to be resolved in 0.13.4… maybe this? https://github.com/hashicorp/terraform/pull/26375

Hi @jurgenweber i’m experiencing the same thing with terraform 0.14.4, did you find a solution?

1 Like

I’m also experiencing this, on Terraform v0.14.9. Tried some suggested workarounds but didn’t help.

1 Like

I’m not sure what was causing the original behavior here because the configuration example wasn’t complete, but I can at least say some things about the data resource behavior:

Terraform will read a data resource either during the plan phase or the apply phase, decided based on what’s in its configuration.

Terraform will delay the read until the apply step if either of the following are true:

  1. If any values in the configuration are unknown ((known after apply)).
  2. If the data resource depends either implicitly (via references) or explicitly (via depends_on) on another resource that has an action in the current plan.

If neither of the above are true then Terraform should read the data resource during the plan phase. The plan output should be quiet about it as long as it still has the same value it had on the previous read (recorded in the state), but Terraform can also report it in the plan if its value has changed compared to last time, so you can see a diff of how it changed.

(I’m describing the behavior of the latest version of Terraform here. Older versions of Terraform had some different behavior due to data source reading being handled as a separate step from planning.)

In the original post in this topic it seems like the policy document contains some ARN values that are presumably derived indirectly from the .arn attribute of some resources, and thus those would typically be unknown whenever there’s a plan to create or replace one of those objects. That puts it in situation 1 above, where a delay to the apply step would be expected. If that’s happening every time then it suggests that one of the objects whose ARNs are being used isn’t converging, in which case there would be another action in the same plan describing that this object is being created or replaced.

If you are seeing something similar then the only way to explain it would be to see the full plan that Terraform is producing, at which point we can apply the rules I described above to understand what’s happening.

I see this thread has long time, but is not easier use a loop for instead of for_each loop to include all namespaces? Something like

values = [for namespaces in toset(local.k8s_sa_to_iam_roles_indexed) : "system:serviceaccount:namespace:${namespaces}"]

I hope this can simplify initial request