Terraform AWS IAM Iterate Over Rendered JSON Policies

Viewed 1251

How can I iterate over the JSON rendered data.aws_iam_policy_document documents within an aws_iam_policy?

data "aws_iam_policy_document" "role_1" {

  statement {
    sid = "CloudFront1"

    actions = [
      "cloudfront:ListDistributions",
      "cloudfront:ListStreamingDistributions"
    ]
    resources = ["*"]
  }
}

data "aws_iam_policy_document" "role_2" {
  statement {
    sid = "CloudFront2"

    actions = [
      "cloudfront:CreateInvalidation",
      "cloudfront:GetDistribution",
      "cloudfront:GetInvalidation",
      "cloudfront:ListInvalidations"
    ]
    resources = ["*"]
  }
}

variable "role_policy_docs" {
  type        = list(string)
  description = "Policies associated with Role"
  default     = [
    "data.aws_iam_policy_document.role_1.json",
    "data.aws_iam_policy_document.role_2.json",
  ]
}

locals {
  role_policy_docs = { for s in var.role_policy_docs: index(var.role_policy_docs, s) => s}
}

resource "aws_iam_policy" "role" {
  for_each = local.role_policy_docs

  name        = format("RolePolicy-%02d", each.key)
  description = "Custom Policies for Role"

  policy = each.value
}

resource "aws_iam_role_policy_attachment" "role" {
  for_each   = { for p in aws_iam_policy.role : p.name => p.arn }
  role       = aws_iam_role.role.name
  policy_arn = each.value
}

This example has been reduced down to the very basics. The policy documents are dynamically generated with the source_json and override_json conventions. I cannot simply combine the statements into a single policy document.

Terraform Error:

Error: "policy" contains an invalid JSON policy

  on role.tf line 35, in resource "aws_iam_policy" "role":
  35:   policy = each.value
1 Answers

This:

variable "role_policy_docs" {
  type        = list(string)
  description = "Policies associated with Role"
  default     = [
    "data.aws_iam_policy_document.role_1.json",
    "data.aws_iam_policy_document.role_2.json",
  ]
}

Is literally defining those default values as strings, so what you're getting is this:

  + role_policy_docs = {
      + 0 = "data.aws_iam_policy_document.role_1.json"
      + 1 = "data.aws_iam_policy_document.role_2.json"
    }

If you tried removing the quotations around the data blocks, it will not be valid because you cannot use variables in default definitions. Instead, assign your policy documents to a new local, and use that local in your for loop instead:

locals {
  role_policies = [
    data.aws_iam_policy_document.role_1.json,
    data.aws_iam_policy_document.role_2.json,
  ]
  

  role_policy_docs = { 
    for s in local.role_policies : 
      index(local.role_policies, s) => s 
    }
}
Related