How to JSON Stringify in Terraform Using the `jsonencode` Function
Use the jsonencode function to convert Terraform values into properly formatted JSON strings for IAM policies, API payloads, and resource configurations.
When working with the hashicorp/terraform repository, converting native values into JSON format is a common requirement for configuring IAM policies, cloud-init scripts, and API request bodies. Unlike general-purpose programming languages that offer generic "stringify" methods, Terraform provides the purpose-built jsonencode function as the standard mechanism for JSON stringification. This function ensures deterministic, type-safe encoding without manual string manipulation.
Why jsonencode Is the Canonical Method for JSON Stringification in Terraform
The jsonencode function is the definitive approach for converting Terraform values into JSON strings, offering several critical advantages over manual construction:
- Deterministic output: The function follows the JSON specification exactly, guaranteeing consistent ordering of object keys (alphabetical) and proper escaping of special characters.
- Type preservation:
jsonencodeautomatically handles complex nested structures—including maps, lists, objects, strings, numbers, booleans, andnull—without requiring explicit type casting. - Safety: Because Terraform handles all quoting and escaping internally, you avoid common bugs such as missing quotes, stray commas, or invalid Unicode escape sequences.
- Idempotency: When the underlying data does not change, the resulting JSON string is identical across plan and apply runs, keeping resource diffs clean and predictable.
- Interoperability: Major Terraform providers (AWS, Azure, Google Cloud) expect JSON payloads for IAM policies, role trust documents, and custom API calls;
jsonencodeproduces exactly the format those APIs require.
How jsonencode Works Under the Hood
The implementation resides in the Terraform core source file [internal/lang/functions/jsonencode.go](https://github.com/hashicorp/terraform/blob/main/internal/lang/functions/jsonencode.go). The function receives a single argument of type cty.Value (the internal representation of Terraform values) and delegates to the cty library’s JSON marshaler located in [internal/cty/json/encode.go](https://github.com/hashicorp/terraform/blob/main/internal/cty/json/encode.go).
The encoding process performs the following operations:
- Validation: Accepts any
cty.Value, includingnull, which is encoded as the literal JSON valuenull. - Key sorting: For objects and maps, keys are sorted alphabetically to guarantee deterministic output across runs.
- Unicode escaping: Automatically escapes characters that require it according to JSON specification.
- Number formatting: Enforces JSON number rules, rejecting leading zeros and non-finite values like
NaNorInfinity.
Because the logic lives in the core language package, jsonencode is available in all Terraform configurations without requiring any provider or external plugin.
Practical Examples of jsonencode in Terraform Configurations
Encoding Simple Maps for Resource Tags
Use jsonencode to convert a Terraform map into a JSON string for resources that require JSON-formatted metadata:
variable "tags" {
type = map(string)
default = {
Environment = "dev"
Owner = "alice"
}
}
resource "aws_s3_bucket" "example" {
bucket = "my-example-bucket"
# The bucket tags must be a JSON string
tags = jsonencode(var.tags)
}
Generating IAM Policy Documents
Many AWS resources require JSON policy documents. While aws_iam_policy_document data sources provide native HCL syntax, jsonencode ensures proper formatting when passing the result to resources:
data "aws_iam_policy_document" "lambda" {
statement {
actions = ["lambda:InvokeFunction"]
resources = ["*"]
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
}
}
# Some providers (e.g., aws_iam_policy) accept a raw JSON string
resource "aws_iam_policy" "lambda_invocation" {
name = "AllowAPIGatewayInvoke"
policy = jsonencode(data.aws_iam_policy_document.lambda.json)
}
Building Complex Nested Payloads for API Calls
When constructing request bodies for HTTP data sources or external APIs, jsonencode handles nested lists and objects automatically:
locals {
payload = [
{
name = "frontend"
ports = [80, 443]
active = true
},
{
name = "backend"
ports = [8080]
active = false
}
]
}
resource "null_resource" "api_call" {
triggers = {
# The API expects a JSON array of objects
request_body = jsonencode(local.payload)
}
provisioner "local-exec" {
command = "curl -X POST -d '${self.triggers.request_body}' https://api.example.com/deploy"
}
}
Handling Null Values Gracefully
The function properly encodes optional configurations that may be unset, avoiding interpolation errors:
variable "optional_config" {
type = any
default = null
}
output "config_json" {
value = jsonencode(var.optional_config)
# Result will be the literal string "null"
}
Summary
jsonencodeis the native Terraform function for converting values to JSON strings, located ininternal/lang/functions/jsonencode.go.- The function provides deterministic, alphabetical key sorting and proper Unicode escaping through the
ctylibrary. - It handles all Terraform primitive types—including maps, lists, objects, and
null—without manual formatting. - Common use cases include IAM policies, cloud-init scripts, API request bodies, and resource tags.
- Because it is part of the core language,
jsonencoderequires no external providers and produces idempotent results across Terraform runs.
Frequently Asked Questions
What is the difference between jsonencode and jsondecode in Terraform?
jsonencode converts Terraform values (maps, lists, objects) into a JSON-formatted string, while jsondecode performs the inverse operation, parsing a JSON string into Terraform values. Use jsonencode when sending data to external systems, and jsondecode when processing JSON responses from APIs or data sources.
Can I use jsonencode inside heredoc strings?
While technically possible, embedding jsonencode inside heredocs is unnecessary and can lead to formatting issues. Instead, assign the jsonencode result directly to an attribute or local value. If you must use it in a heredoc, reference the encoded value via interpolation: ${local.encoded_json} rather than nesting the function call inside the heredoc block.
Does jsonencode preserve the order of object keys in maps?
No, jsonencode intentionally sorts object keys alphabetically to ensure deterministic output. According to the implementation in internal/cty/json/encode.go, this alphabetical sorting guarantees that identical input values always produce byte-for-byte identical JSON strings, preventing unnecessary resource updates in Terraform plans.
How does jsonencode handle special characters and Unicode strings?
The function automatically escapes special characters (such as quotes, backslashes, and control characters) according to JSON specification. For Unicode, it uses valid JSON escape sequences when necessary. This automatic escaping prevents syntax errors in generated JSON and ensures compatibility with downstream APIs that consume the encoded strings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s https://instagit.com/install.md