hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.74k stars 9.1k forks source link

data aws_iam_policy_document re-reading #38862

Closed mpzfm1 closed 2 weeks ago

mpzfm1 commented 1 month ago

Description

Terraform Version: 1.8.5 AWS Provider Version: 5.62.0

The resources are:

resource "aws_iam_role_policy" "policy" {
  for_each = var.module_iam_role_list

  name = "${var.cloud_provider_id}${var.aws_region_id}-${var.environment_letter}-${var.project_name}-iap-${each.value.aws_service}${split("-", each.key)[0]}"
  role = aws_iam_role.role[each.key].id
  policy = data.aws_iam_policy_document.policyDocument[each.value.custom_policy].json
}

data "aws_iam_policy_document" "policyDocument" {
  for_each = var.module_iam_role_policies_list

  dynamic "statement" {
      for_each = each.value.statements_list
      content {
          sid       = statement.value.prefix_sid
          effect    = statement.value.effect
          actions   = statement.value.actions
          not_actions   = statement.value.not_actions
          resources = statement.value.resources
          not_resources = statement.value.not_resources

          dynamic "principals" {
              for_each = statement.value.principals
              content {
                  type          = principals.value.type
                  identifiers   = principals.value.identifiers
              }
          }

          dynamic "not_principals" {
              for_each = statement.value.not_principals
              content {
                  type          = not_principals.value.type
                  identifiers   = not_principals.value.identifiers
              }
          }

          dynamic "condition" {
              for_each = { for key, condition in statement.value.conditions : key => condition if each.value.external_id }
              content {
                test = condition.value.test
                variable = condition.value.variable
                values = condition.value.variable == "sts:ExternalId" ? [random_password.policyDocument[each.key].result] : condition.value.variable.values

              }
          }

       }
  }
  depends_on = [ ]
}

resource "random_password" "policyDocument" {
  for_each = { for role_name, role_data in var.module_iam_role_policies_list : role_name => role_data if role_data.external_id}

  length  = 32
  special = false
  numeric = true
  override_special = "!#$%&*()_=+[]{}:?"
}

The variable is a objetcs map like:

"module_iam_role_policies_list": {
    "test": {
        "external_id": true,
        "statements_list": {
            "allow-01": {
                "actions": [
                    "sts:AssumeRole"
                ],
                "not_actions": [],
                "effect": "Allow",
                "prefix_sid": "assumerolesfn",
                "principals": {
                    "principal-01": {
                        "identifiers": [
                            "arn:aws:iam::xxxxxxxx:user/xxxxxxxxx"
                        ],
                        "type": "AWS"
                    }
                },
                "not_principals": {},
                "resources": [],
                "not_resources": [],
                "conditions": {
                    "condition-01": {
                        "test": "StringEquals",
                        "variable": "sts:ExternalId",
                        "values": []
                    }
                }
            }
        }
    },
    "testtwo": {
        "external_id": false,
        "statements_list": {
            "allow-01": {
                "actions": [
                    "sqs:ReceiveMessage"
                ],
                "not_actions": [],
                "effect": "Allow",
                "prefix_sid": "SqsWriter",
                "principals": {},
                "not_principals": {},
                "resources": [
                    "arn:aws:sqs:eu-west-1:xxxxxxx:xxxxxxx"
                ],
                "not_resources": [],
                "conditions": {}
            }
        }
    }
}

If we add more keys to the variable with the attribute external_id: false, there are no issues.

The problem arises when we add new keys with external_id: true. This triggers the creation of a random_password specifically for that key (which is expected), but it also reprocesses all the policy documents for all keys, regardless of whether external_id is true or false, and without any actual changes. As a result, it redeploys the policy documents (unchanged) to the role's policy.

If I include a simple string within the dynamic condition block, such as:

values = condition.value.variable == "sts:ExternalId" ? ["hello"] : condition.value.variable.values

where "hello" is not referencing any other resource, there are no issues. Therefore, the problem seems to occur when random_password is generated, even if it's only for a new key with external_id: true.

thanks

References

No response

Would you like to implement a fix?

None

github-actions[bot] commented 1 month ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue

justinretzolk commented 2 weeks ago

Hey @mpzfm1 👋 Thank you for taking the time to raise this! This particular behavior is controlled by Terraform Core, and in this case, is what I would expect based on the information in Data Resource Behavior.

The aws_iam_policy_document data source has an implied dependency on the random_password resource by way of interpolation here:

                values = condition.value.variable == "sts:ExternalId" ? [random_password.policyDocument[each.key].result] : condition.value.variable.values

The random_password resource's result attribute isn't known until apply time, so as mentioned in the data resource behavior document linked above, when there are changes, the read of the aws_iam_policy_document data source will be deferred until apply time. This results in the behavior that you're experiencing here. In order to get around this, you'll need to adjust the configuration (exactly how will depend on other factors within the configuration). Since there's no action to be taken by the provider team, I'm going to close this issue. If you encounter unexpected behavior in the future, please do let us know 🙂

github-actions[bot] commented 2 weeks ago

[!WARNING] This issue has been closed, meaning that any additional comments are hard for our team to see. Please assume that the maintainers will not see them.

Ongoing conversations amongst community members are welcome, however, the issue will be locked after 30 days. Moving conversations to another venue, such as the AWS Provider forum, is recommended. If you have additional concerns, please open a new issue, referencing this one where needed.