How to use templatefile() as the input of jsonencode() to create an IAM policy?

Dear all,

I was trying to figure that out by myself for few hrs. now but couldn’t come up with anything suitable, hence here :slight_smile:

I have a bunch of HCL formatted template files, like this:

##ExtraReadOnlyAccess.tftpl
{
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "account:GetAccountInformation",
          "ce:GetCostForecast",
        ]
        Effect   = "Allow"
        Resource = "*"
        Sid      = ${str_sid}
      }
    ]
}

which I need to turn into a IAM policies.
And then:

resource "aws_iam_policy" "aip" {
  for_each = var.service_enabled ? toset(var.extra_policies): []
  name = each.value
  policy = jsonencode(templatefile(
    "${path.module}/extra_policies/${each.value}.tftpl",
    { str_sid = each.value },
  ))
}

Which returning this error:

Error: “policy” contains an invalid JSON policy: contains a JSON-encoded string, not a JSON-encoded object

I think, I know why I’m getting this error: As templatefile() returns the output as heredoc with <<EOT ... EOT that making the entire outout as a sting, as oppose to an object, I suppose; hence jsonencode() is complaining?

Does anyone know how to solve this issue if I have to use jsonencode()?

Hi, does anyone have any idea at all?
Very hard to think I’m the only one trying to do this. Most of the AWS managed policies are quite open for our need and I don’t like to create 20 individual aws_iam_policy_document data resource or modify core-code every time need changes in the policy documents.

@dsantanu The error is caused by a mix-up of data types. The templatefile function returns a string, while the jsonencode function expects a Terraform object as input. Since the policy argument for the aws_iam_policy resource expects a string, you don’t need the jsonencode function call in the policy argument value.

However, the content of your template file ExtraReadOnlyAccess.tftpl is not a valid JSON string since it is in the Terraform object syntax. So this is another data type mix-up in your example, but inside the template.

As a fix, you’d either need to update the template with the vanilla JSON format (basically doing what jsonencode would have done), or you can be fancy and just call jsonencode within the template as per the " Generating JSON or YAML from a template" example.

Personally I would keep things simple and just update the template with vanilla JSON format, but if you want to try the 2nd option, here’s an updated ExtraReadOnlyAccess.tftpl for your reference:

${jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "account:GetAccountInformation",
          "ce:GetCostForecast",
        ]
        Effect   = "Allow"
        Resource = "*"
        Sid      = "${str_sid}"
      }
    ]
})}

What I found though is that if you don’t put quotes around ${str_sid}, the template is not considered valid although it is technically valid Terraform syntax.

1 Like

thanks @acwwat for your reply and for the 2nd option.
I didn’t want to handle the JSON file by myself, hence the use of jsonencode but sort of missed the option #2 in the document that you have pointed out. I’ll give it a try and report here back soon.

Update:
Just to confirm, it worked exactly the way I wanted; so all good now. ty! :+1:

1 Like