---
description: |
  Learn about the differences between JSON and HCL2 templates and how converting templates to HCL affects plugins.
page_title: Differences between HCL2 and JSON templates
---

# Differences between HCL2 and JSON templates

This document explains the differences between JSON to HCL2 templates, including how converting JSON templates to HCL2 affects builder, provisioner, and post-processor behavior.

## Introduction

Packer uses JSON or HCL2 templates to orchestrate builds for one or more artifacts.

In [legacy JSON templates](/packer/docs/templates/legacy_json_templates), declare a series of builders, provisioners, and post-processors to build images.

In [HCL2 templates](/packer/docs/templates/hcl_templates), you can specify builders through sources and collect them into build blocks.

You can use the `packer hcl2_upgrade` command when converting your existing JSON templates to HCL2. Refer to the [hcl2_upgrade](/packer/docs/commands/hcl2_upgrade) page for more information on its usage.


## Builders

Builders are components that are specialized for a platform.
Their role is to set the stage for the steps that will let you build the final image from your configuration template.

### JSON

In JSON, the `builders` attribute declares how a builder will be invoked to build an image.

```json
{
  "builders": [
    {
      "type": "null",
      "name": "test",
      "communicator": "none"
    }
  ]
}
```

### HCL2

In HCL2, builders are declared through a `source` block. Sources on their own are not enough, and must be invoked in a `build` block.
The `build` block serves as a container for all the steps (i.e. `source`, `provisioners` and `post-processors`) to go from a blank image to a finished one.

```hcl
source "null" "test" {
  communicator = "none"
}

build {
  sources = ["null.test"]
}
```

## Provisioners

Provisioners are components that modify the state of the machine image you are building.
They can be used for installing packages, writing data to the remote file system, or executing remote commands.
They can be defined in both JSON or HCL templates.

### JSON

In a JSON template, provisioners are declared at the top-level of the template as an array of sequentially
invoked components to provision the image you are building. They apply to all the builders defined in the template.

```json
{
  "builders": [
    {
      "type": "null",
      "name": "test",
      "communicator": "none"
    }
  ],
  "provisioners": [
    {
      "type": "shell-local",
      "inline": ["echo test"]
    }
  ]
}
```

### HCL2

In HCL2, provisioners are declared in the `build` block, and apply only to the sources invoked as
part of this build block.
This lets you declare multiple build blocks, each with its own set of provisioners, which will
result in multiple different builds executed in parallel.

```hcl
source "null" "test" {
  communicator = "none"
}

build {
  sources = ["null.test"]

  provisioner "shell-local" {
    inline = ["echo test"]
  }
}
```

## Post-processors

Post-processors are components that are invoked after the image was produced. Their
role is to consume an artifact produced by a builder, or another post-processor, and
build a new artifact from that.

### JSON

As with provisioners, in JSON templates, post-processors are sequentially invoked after a
build finished provisioning, and are declared in the template under "post-processors".

```json
{
  "builders": [
    {
      "type": "null",
      "name": "test",
      "communicator": "none"
    }
  ],
  "provisioners": [
    {
      "type": "shell-local",
      "inline": ["echo test"]
    }
  ],
  "post-processors": [
    {
      "type": "manifest"
    }
  ]
}
```

### HCL2

In HCL2, much like provisioners, they are also part of the `build` block, and similar rules
apply as with provisioners.

```hcl
source "null" "test" {
  communicator = "none"
}

build {
  sources = ["null.test"]

  provisioner "shell-local" {
    inline = ["echo test"]
  }

  post-processor "manifest" {}
}
```
