HCL to YAML

Paste HCL (HashiCorp Configuration Language), block_type "label" { attr = value } syntax like Terraform resources or Nomad job specs, and get back the equivalent nested YAML, with blocks becoming objects keyed by their labels. A free online tool from Staaarter, right in your browser.

Runs locallyUpdated 2026-07-26

Overview

Introduction

HCL is the syntax behind Terraform, Nomad, Packer, and other HashiCorp tooling, readable block-and-attribute config that's great for infrastructure-as-code but not always what a downstream tool expects. This tool parses a practical subset of HCL and re-expresses it as nested YAML.

It's a hand-written recursive-descent parser covering the structural features most real HCL config actually uses, blocks, labels, and typed attributes, deliberately leaving HCL's expression language (functions, interpolation, for expressions) out of scope in favor of predictable, honest results.

What Is HCL to YAML?

An HCL-to-YAML converter tokenizes HCL source (identifiers, quoted strings, numbers, booleans, and punctuation) and then parses it recursively: an identifier followed by = is an attribute assignment, while an identifier followed by zero or more quoted labels and a { starts a nested block.

Blocks become nested JavaScript objects keyed by their type and then each label in turn, and that object tree is serialized as YAML with the same stringifier every other tool in this category uses.

How HCL to YAML Works

Attributes are simple: key = value, where value is a string, number, boolean, or [ ]-delimited array of those. Blocks are structural: the block type and any quoted labels form a path (resource, aws_instance, web), and the { ... } body is parsed recursively (allowing attributes and further nested blocks) into an object placed at that path.

If two blocks resolve to the same path (the same type and labels appearing twice), their bodies are shallow-merged rather than one replacing the other. # and // both start a line comment and are skipped during tokenizing, before parsing sees them.

When To Use HCL to YAML

Use it to preview a Terraform resource block, a Nomad job spec, or a Packer template's structure as YAML, useful when writing documentation or feeding the structure into a YAML-based tool.

It's also a quick way to sanity-check how HCL's label-based nesting would flatten into an object tree, without spinning up an actual Terraform parser.

Features

Advantages

  • Correctly reconstructs labeled blocks into the nested object shape their labels imply, matching how Terraform itself interprets them.
  • Handles zero-label blocks, multi-label blocks, arrays, and nested blocks-within-blocks.
  • Reports clear, line-numbered syntax errors rather than silently producing a wrong structure for unsupported syntax.
  • Runs entirely client-side with no added dependency, so infrastructure config never leaves your browser.

Limitations

  • HCL's expression language, function calls, for expressions, conditionals, and references between resources, is entirely out of scope; a ${...} interpolation sequence is kept as a literal string rather than evaluated.
  • Heredoc strings (<<EOT ... EOT) and maps used as attribute values (as opposed to blocks) are not supported and produce a parse error.
  • Numbers are parsed with a simple digits-and-decimal-point pattern; HCL's more exotic numeric literals (hex, exponents) aren't recognized.

Examples

A labeled resource block

Input

resource "aws_instance" "web" {
  ami = "ami-123"
  count = 2
}

Output

resource:
  aws_instance:
    web:
      ami: ami-123
      count: 2

The block type resource is the outer key; each label ("aws_instance", "web") nests one level deeper; the attributes form the innermost object.

${...} interpolation is kept literal

Input

name = "${var.name}"

Output

name: "${var.name}"

Since expression evaluation is out of scope, the interpolation sequence is preserved exactly as written rather than resolved.

Best Practices & Notes

Best Practices

  • Expect ${...} interpolation to appear unresolved in the YAML output, treat it as a reminder of where a real value would need to be substituted, not an error.
  • Check the error message's line number directly against your HCL source when parsing fails, it names the exact unsupported construct.
  • Run the result through the YAML Formatter afterward if you want to double-check indentation consistency.

Developer Notes

The implementation is a small tokenizer (producing ident/string/number/bool/punctuation tokens with line/column tracking) feeding a recursive-descent parser with two entry points: parseItem, which distinguishes an attribute (identifier followed by =) from a block (identifier followed by zero or more string labels and {), and parseValue, which handles scalars and [ ]-delimited arrays. Repeated blocks at the same path are shallow-merged via object spread.

HCL to YAML Use Cases

  • Previewing a Terraform resource or module block's structure as YAML for documentation
  • Converting a Nomad job spec or Packer template into YAML for a tool that expects that format
  • Sanity-checking how HCL's label-based block nesting translates into a plain object tree
  • Generating YAML fixtures for tests from an existing HCL source file

Common Mistakes

  • Expecting ${...} interpolation to be evaluated to an actual value, it's passed through as a literal string instead.
  • Using a heredoc string or a map as an attribute value and expecting it to parse, neither is supported in this subset.
  • Forgetting that repeated blocks at the same type-and-labels path merge their attributes rather than each becoming a separate list entry.

Tips

  • If parsing fails, check whether the offending construct is a heredoc, a map-as-value, or an expression, features called out as unsupported.
  • Use YAML Sorter afterward if you want the converted mapping's keys in alphabetical order rather than source-file order.
  • For deeply nested HCL (blocks within blocks), converting to YAML often makes the overall structure easier to scan than HCL's brace-based nesting.

References

Frequently Asked Questions