HCL / Terraform Converter + Formatter
Convert HCL to JSON and format Terraform files with the real HashiCorp HCL library — compiled to WebAssembly, running locally.
One-way: JSON back to HCL is not offered. Expressions are not evaluated — Simplify is off, the hcl2json CLI default.
Press ? for shortcuts · The engine is fetched from this site once; your configuration never leaves the page.
Do it offline:$ hcl2json main.tf
How to use it
- Pick a mode: Convert turns HCL into its JSON representation; Format rewrites the file in canonical HCL style.
- Paste HCL or Terraform configuration into the input pane, or open a .tf, .hcl or .tfvars file — up to 1 MiB.
- Press the action button — the first run fetches the 1.9 MB engine once; every later run reuses it from this tab.
- Read the result in the output pane. A parse error shows the engine's own diagnostic with its input.hcl line,column position.
- Copy the result or download it as .json or .tf, and share the mode with a link — the link never carries your configuration.
Paste HCL and get its JSON representation, or run the canonical formatter over a Terraform file — both powered by the genuine HashiCorp HCL library (v2.24.0) and the hcl2json converter (v0.6.9), compiled to WebAssembly and executed in a worker in this tab. Convert output is byte-identical to the hcl2json CLI, diagnostics arrive word for word, and your configuration is never uploaded.
How it works
The engine is not a re-implementation. The genuine hashicorp/hcl v2.24.0 library and the tmccombs/hcl2json v0.6.9 converter are compiled with Go's js/wasm target into one 7.4 MB WebAssembly artifact (1.9 MB compressed), which runs inside a Web Worker in this tab. The engine itself arrives in exactly two same-origin requests — the Go runtime shim and the wasm binary, both this site's own static assets — and after that the parser works entirely on this device. Against the native hcl2json binary, the executed test corpus converts 8 of 8 fixtures byte-identically, so what this page prints is what the CLI prints.
Convert emits the HCL JSON representation, the alternate input syntax every HCL tool accepts. Block types and labels become nested object keys — a resource "aws_s3_bucket" "logs" block lands under resource → aws_s3_bucket → logs — and repeated blocks of one type collect into arrays. Argument expressions are not evaluated: interpolations such as "${var.name}", function calls and for-expressions are carried as strings, exactly as the JSON syntax expects them. The upstream Simplify option, which would fold constant expressions, stays off because it is the hcl2json CLI's own default.
Format runs hclwrite.Format, the library terraform fmt uses, from the same pinned hcl v2.24.0. It works on tokens, not meaning: indentation collapses to two-space steps, runs of = signs inside a body align, spacing around operators normalizes — and nothing is renamed, reordered, validated or dropped. Heredoc bodies pass through untouched, comments stay where they were, and formatting an already-canonical file returns it unchanged, which the test corpus proves by running the formatter twice over every fixture.
Examples
resource "aws_s3_bucket" "logs" {
bucket = "my-app-logs"
force_destroy = true
tags = {
Environment = "production"
}
}
{
"resource": {
"aws_s3_bucket": {
"logs": [
{
"bucket": "my-app-logs",
"force_destroy": true,
"tags": {
"Environment": "production"
}
}
]
}
}
}Convert — the resource's two labels become nested keys and the block body lands in an array, because HCL allows repeated blocks of one type. The 4-space JSON with no trailing newline is byte-identical to what hcl2json prints.
resource "aws_iam_policy" "readonly" {
name = "readonly"
policy = <<EOT
{
"Version": "2012-10-17"
}
EOT
}
resource "aws_iam_policy" "readonly" {
name = "readonly"
policy = <<EOT
{
"Version": "2012-10-17"
}
EOT
}
Format — the attributes gain canonical indentation and their = signs align, while the heredoc body between <<EOT and EOT is preserved byte for byte: formatter rules stop at a heredoc's edge.
variable "region" {
type = string
default = "eu-central-1"
}
variable "region" {
type = string
default = "eu-central-1"
}
Format — mixed 6- and 3-space indentation collapses to the two-space standard and the = run stays aligned. Running Format again returns the same bytes: the formatter is idempotent.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Format is hclwrite.Format — the terraform fmt library, not the binary
The Format tab runs hclwrite.Format from hcl v2.24.0 — the library terraform fmt uses for its rewriting. This page does not claim byte-parity with the terraform binary itself: that comparison is recorded as an open check, and until it lands the honest statement is same library, pinned version, idempotent output — proven against the native library on all nine formatting fixtures.
One-way: JSON back to HCL is not offered
The JSON representation loses details native syntax carries — comment placement, heredoc choice, expression layout — so a faithful reverse conversion does not exist in general. This tool converts HCL to JSON only; keep the .tf file as the source of truth and treat the JSON as a machine-readable projection.
Expressions stay strings — nothing is evaluated
Interpolations like "${var.name}", function calls and ternaries come out as JSON strings, because that is how HCL's JSON syntax encodes expressions. No variable is resolved and no function runs. The upstream Simplify flag, which would fold constants, is pinned off here to match the hcl2json CLI's default output.
Format never raises an error
hclwrite.Format is token-based: input it cannot fully parse passes through with whatever spacing rules still apply, and nothing is reported. Syntax checking lives in Convert, where the parser reads the whole configuration and its diagnostics appear word for word — run Convert first when a file looks suspect.
Repeated blocks become arrays — even a single one
In the JSON representation each block type maps to an array of bodies, so one resource still nests inside [ ]. Tools reading the JSON must index into that array; it is not an artifact of this page but the shape hcl2json and Terraform's own JSON syntax agree on.
Every cap is named, never silent
Input caps at 1 MiB of text, results at 8,388,608 characters, and a run is stopped after 10 seconds. Each limit refuses with its own banner before or after the run — never a spinner that hangs, never a truncated result presented as complete.
Common errors
The input is empty — paste HCL or Terraform configuration first.- Both modes need text first. Load the sample to see the shape the parser expects, or drop a .tf file straight onto the input pane.
parse config: [input.hcl:1,33-34: Unclosed configuration block; There is no closing brace for this block before the end of the file. This may be caused by incorrect brace nesting elsewhere in this file.]- The engine's own text, word for word — input.hcl is this page's name for the pasted text, and 1,33 reads line 1, column 33. A missing closing brace is usually the LAST brace of the file, but the parser points at the block it could not close.
parse config: [input.hcl:2,3-7: Argument or block definition required; An argument or block definition is required here. To set an argument, use the equals sign "=" to introduce the argument value.]- HCL native syntax separates arguments with =, not the colon JSON uses — name: "x" inside a block is the classic YAML/JSON habit this diagnostic catches. Write name = "x" instead.
parse config: [input.hcl:2,9-3,1: Invalid expression; Expected the start of an expression, but found an invalid expression token.]- An = with nothing after it: the parser reached the line break still waiting for a value. The 2,9-3,1 span shows exactly where the expression should have started and where the parser gave up.
Input is larger than the 1 MiB limit for this tool. For bigger files use the CLI: hcl2json main.tf- The cap keeps a worst-case parse inside the 10-second kill window. Terraform configurations are normally far smaller; a bigger corpus belongs to the CLI on your machine, which has no such limit.
The run took longer than 10 seconds and was stopped — split the configuration and try again.- A WebAssembly call cannot be paused, so a runaway parse is ended by terminating the worker after 10 seconds. The next run reloads the engine from the browser cache and your input stays put.
FAQ
Is my Terraform configuration uploaded anywhere?
No. Fetching the engine takes exactly two requests, both for this site's own static files — the Go runtime shim and the wasm engine — and no request this page makes ever carries your text. Your configuration goes into a worker in this tab and nowhere else — and share links carry only the selected mode, never the text, because infrastructure code routinely embeds secrets.
Why is the engine a 1.9 MB download?
Because it is the real parser, not an approximation: hashicorp/hcl v2.24.0 plus the hcl2json converter compiled from Go to WebAssembly, with Go's runtime inside — 7.4 MB of wasm, 1.9 MB compressed on the wire. It downloads once per session behind your first action, then stays cached in the tab.
Which HCL version does this page run?
hashicorp/hcl v2.24.0 and tmccombs/hcl2json v0.6.9, both pinned and named in the citations below. Pinning matters: diagnostics, formatting decisions and JSON layout can shift between library releases, and this page tells you exactly which behavior you are getting.
Is Format the same as terraform fmt?
It runs hclwrite.Format, the library terraform fmt uses, at pinned hcl v2.24.0 — and its output is proven identical to that native library and idempotent across all nine test fixtures. Byte-parity against the terraform binary itself has not been executed yet, so this page deliberately does not claim it.
Can I convert JSON back to HCL here?
No — this tool is one-way. The JSON representation drops surface details such as comments and heredoc styles, so a general JSON-to-HCL conversion cannot restore the original file. If you need native syntax, keep the .tf source and convert forward only.
What would the Simplify option do, and why is it off?
hcl2json can evaluate constant expressions while converting — Simplify would turn upper("a") into "A". This page pins it off because that is the hcl2json CLI's default, and because byte-identical CLI output is a promise this page tests. What you paste is represented, never computed.
Is there a CLI equivalent for scripts and CI?
Yes: hcl2json main.tf prints the same JSON this page shows — the line requires hcl2json installed (v0.6.9, the exact release this engine embeds; its repository is cited below). For formatting in CI, terraform fmt covers .tf files with the same underlying library.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Convert or format now, per the selected mode.
- ⌘/Ctrl + S
- Download the result as .json or .tf.
- ⌘/Ctrl + C
- Copy the result when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the result even with a selection active.
- /
- Focus the HCL input.
- t
- Cycle the color theme.
- ? or ⌘/Ctrl + /
- Show the shortcut overlay.
- Esc
- Cancel a run, or dismiss a banner.