Json syntax iam role policy

So terraform supports json syntax for config, which is fine.
I prefer it as it’s easier to programmatically read it and modify it.

However, I’m a bit stump when it comes to resources that allow you add a policy that’s in JSON format.

For example:

resource "aws_iam_role" "test_role" {
  name = "test_role"

  # Terraform's "jsonencode" function converts a
  # Terraform expression result to valid JSON syntax.
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Sid    = ""
        Principal = {
          Service = "ec2.amazonaws.com"

  tags = {
    tag-key = "tag-value"

I keep getting syntax errors and am not sure of the way to do the above assume role policy in json syntax

Also if i can be greedy

resource "aws_cloudwatch_log_resource_policy" "example" {
  policy_name = "example"
  policy_document                 = <<CONFIG
  "Version": "2012-10-17",
  "Statement": [
      "Effect": "Allow",
      "Principal": {
        "Service": "es.amazonaws.com"

is there a way to do this as well with json syntax? or is jsonencode the only way?

I’m aware you can use templatefile() function but that requires you to create a file.
are there better ways to include a policy json in a json syntax terraform config?

Hi @dilaodilao,

I’m afraid I’m not sure what exactly you are asking.

Are you asking about how to write an expression that uses the jsonencode function when you are writing a .tf.json file?

yes, after looking over the question the 2nd question is probably not relevant.
As that’s terraform’s HCL language and dont think the JSON syntax supports that.

So yes, how do you json encode a very simple JSON in JSON syntax in terraform.

ie {“a”: “AAA”}

When you are using the JSON variant of the Terraform language, there are various different mapping rules for how Terraform will interpret JSON in difference parts of the configuration, due to JSON having fewer distinct syntax constructs than the native syntax does.

When you are defining an argument in a resource configuration that would normally use the name = expression syntax, the expression mapping is the rule that applies to the “expression” part.

The documentation says that you can write a JSON string, in which case:

Parsed as a string template and then evaluated as described below.

(The rule “described below” that text is only relevant when producing a result that isn’t a string. Since policy_document is a string argument, those details are not relevant.)

Therefore I think you have two main options for expressing this as JSON:

  1. Write a JSON string containing a literal JSON document, with all of the necessary escaping to make the outer document valid. That’s too long to write out here in full, but it would start like this:

      "policy_document": "{\"Version\":\"2021-10-17\",...}"
  2. Write an inline string template that has an interpolation sequence which calls jsonencode. Again, too long to write out in full, but here’s the start:

      "policy_document": "${jsonencode({\"Version\" = \"2021\", /*...*/})}"

If you are asking for a way to write the policy document directly as a JSON object, rather than as a string containing JSON syntax, then I’m afraid there is no way to do that. The provider declares that this argument expects a string and so Terraform requires you to write something that produces a string. The fact that the string happens to include more JSON is not something Terraform is aware of.

The JSON variant of the Terraform language is available primarily so that it can be generated by other software and fed directly to Terraform, rather than written by and maintained by humans. Therefore it’s not optimized for hand-writing or hand-maintenance by humans. If I were writing a program to generate a JSON-based Terraform configuration file containing a resource of this type then I expect I would use the JSON encoder of the language where I wrote the generator. For example, in JavaScript:

  "resource": {
    "aws_iam_role": {
      "test_role": {
        // ...
        "assume_role_policy": JSON.stringify({
          "Version": "2012-10-17",
          // ...

This would be sufficient as long as the policy document is entirely static. If the policy document needs to include values that come from other resources in the same module then there’d be no way to avoid using Terraform’s own jsonencode in a template interpolation as I showed earlier, because if the JSON result contains dynamic data from Terraform then Terraform must be the one to actually create the JSON string.