Terraform continually introduces more functionality and improvements as the product progresses. While newer versions enable better ways of performing tasks, documentation and practices don’t always catch up right away. One Terraform practice is to use jsonencode() over “heredoc” strings. In this article, you’ll learn what a heredoc string is and why you should use jsonencode() instead when working with JSON.

Check out more of my Terraform content here!

What is a heredoc string?

A “here document” or “heredoc” string is a way to represent a multi-line string more clearly. The heredoc string is inspired by Unix languages. In Unix languages, you can use heredoc strings to pass a code block to a command or create a multi-line comment in a BASH script.

A heredoc string consists of:

  • An opening heredoc marker, typically two less than signs (<<)
    • Optionally, you can use a hyphen for indented heredoc string (<<-)
  • A delimiter word of your choosing
  • A line break
  • The string spanning one or more lines
  • The delimiter word you chose on a separate line, ending the heredoc string

Here is an example of a heredoc string using << as the marker and EOT as the delimiter word.

<<EOT
Here is a multi-string,
spanning three lines
enclosed by my heredoc marker and delimiter word.
EOT

If your string requires indentation or spaces at the beginning of the line, use <<- as the opening marker. Insert indentation as needed.

<<-EOT
Here is the first line at the left margin.
  Here is line two slightly indented.
    Here is the third line even more indented!
EOT

A heredoc string’s purpose is to insert a text string exactly as written into a value. This value can exist in code or represent a JSON-formatted string. Here is an example of representing JSON using a heredoc string.

<<-EOT
{
    "CompanyName": "Jeff Brown Tech",
    "Employees": [
        {
            "Name": "Jeff Brown",
            "Title": "CEO"
        },
        {
            "Name": "Maggie Smith",
            "Title": "COO"
        }
    ]
}
EOT

Using heredoc strings in Terraform

A typical example of using heredoc strings in Terraform is with Azure Policy definitions. You write these definitions using JSON syntax that contains information such as name, description, mode, and a policy rule.

For example, here is a policy definition written in Terraform using a heredoc string to represent the definition’s policy_rule property. A common practice is to use the property name as the heredoc delimiter word; in this case, POLICY_RULE.

resource "azurerm_policy_definition" "audit-allowed-locations" {
  name         = "audit-allowed-locations"
  display_name = "Audit Allowed Locations"
  policy_type  = "Custom"
  mode         = "All"
  policy_rule = <<POLICY_RULE
{
    "if": {
        "field": "location",
        "notin": ["East US", "West US"]
    },
    "then": {
        "effect": "audit"
    }
}
POLICY_RULE
}

Using the jsonencode Terraform Function

While the heredoc can work to represent a JSON, HashiCorp does not recommend using heredoc strings to generate JSON. Instead, use the jsonencode() function wrapped around the JSON string. Using the jsonencode() function enables Terraform to validate the JSON syntax.

Here is the previous Azure Policy definition example using jsonencode() instead of a heredoc string to represent the policy_rule Using. By using jsonencode(), Terraform can catch errors within the policy before you attempt to apply the configuration.

resource "azurerm_policy_definition" "audit-allowed-locations" {
  name         = "audit-allowed-locations"
  display_name = "Audit Allowed Locations"
  policy_type  = "Custom"
  mode         = "All"
  policy_rule = jsonencode(
    {
      "if" : {
        "field" : "location",
        "notin" : ["East US", "West US"]
      },
      "then" : {
        "effect" : "audit"
      }
    }
  )
}

While HashiCorp recommends using jsonencode() over a heredoc string to represent JSON, their documentation routinely demonstrates using heredoc. For example, here is the documentation page for azurerm_policy_definition showing using heredoc strings. This discrepancy is likely due to heredoc strings being the required style in Terraform v0.11 and earlier. An excellent example of “do as I say, not as I do.”

Summary

While most Terraform examples you see might use a heredoc string to represent JSON, you should follow best practices and use the jsonencode() function instead. The jsonencode() function allows Terraform to validate the JSON syntax before applying your configuration, saving deployment time.