diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9061a982179526101b373e5b68583bebb8471e4e..bc2bc321e523d1f946ef2a3f66d62453b3d7920a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,7 +21,7 @@ pre-commit-try-repo:
       /etc/apt/sources.list.d/hashicorp.list
     # yamllint enable rule:line-length
     - apt-get update
-    - apt-get install -y terraform
+    - apt-get install -y terraform packer
   script:
     - pre-commit try-repo --all-files ./
   cache:
diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml
index 8deb7d41f449f6ab82acfcbaa4a5de90aeede148..6b2ad4edac3db51ae99e06b4b010599c1f6fa744 100644
--- a/.pre-commit-hooks.yaml
+++ b/.pre-commit-hooks.yaml
@@ -37,6 +37,27 @@
   types: [terraform]
   entry: terraform-validate
 
+- id: packer-fix
+  name: Fix Packer templates
+  description: Fix known backwards incompatibilities in Packer templates
+  language: python
+  entry: packer-fix
+  types: [json, hcl]
+
+- id: packer-fmt
+  name: Format Packer templates
+  description: Rewrites all Packer configuration files to a canonical format
+  language: python
+  entry: packer-fmt
+  types: [hcl]
+
+- id: packer-validate
+  name: Validate Packer templates
+  description: Checks that the Packer template is valid
+  language: python
+  entry: packer-validate
+  types: [json, hcl]
+
 - id: poetry-check
   name: poetry check
   description: Validate pyproject.toml files using Poetry
diff --git a/README.md b/README.md
index 0cf0a2e8a2409034a2bbc44ab112a20acd77aa05..143df4f5e8bbb29c123ce25cd0830480c2982c43 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,9 @@ A collection of [pre-commit](https://pre-commit.com/) hooks.
     - id: docker-compose
     - id: terraform-fmt
     - id: terraform-validate
+    - id: packer-fmt
+    - id: packer-fix
+    - id: packer-validate
     - id: poetry-check
     - id: branch-merge-conflict
 ```
@@ -50,6 +53,18 @@ Requires an installed `terraform`.
 Validate Terraform modules using `terraform validate`.
 Requires an installed `terraform`.
 
+### `packer-fix`
+
+Fix known backwards incompatibilities in Packer templates.
+
+### `packer-fmt`
+
+Rewrites all Packer configuration files to a canonical format (just HCL files).
+
+### `packer-validate`
+
+Checks that the Packer template is valid.
+
 ### `poetry-check`
 
 Validate `pyproject.toml` files using Poetry.
diff --git a/hooks/packer_fix.py b/hooks/packer_fix.py
new file mode 100644
index 0000000000000000000000000000000000000000..c8d8aee53890457df11878ede2006b3aa7cf4963
--- /dev/null
+++ b/hooks/packer_fix.py
@@ -0,0 +1,38 @@
+"""Fix known backwards incompatibilities in Packer templates."""
+
+import argparse
+import locale
+import pathlib
+import sys
+import hooks.utils
+
+
+def packer_fix(file):
+    """Runs packer fix.
+
+    If the invocation succeeds, overwrite the file with the fixed output from
+    packer. If it fails, fail.
+    """
+    proc = hooks.utils.run(["packer", "fix", file])
+    if proc.returncode > 0:
+        return 1
+    # pylint: disable=invalid-name
+    with open(file, "w", encoding=locale.getpreferredencoding()) as fh:
+        fh.write(proc.stdout)
+    return 0
+
+
+def main():
+    """Main entrypoint."""
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("file", nargs="+", type=pathlib.Path)
+    args = parser.parse_args()
+    hooks.utils.check_executable("packer")
+    return hooks.utils.bulk_check(
+        packer_fix,
+        args.file,
+    )
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/hooks/packer_fmt.py b/hooks/packer_fmt.py
new file mode 100644
index 0000000000000000000000000000000000000000..a9e2a214baf99433b59ec5964de4a6817f9d7311
--- /dev/null
+++ b/hooks/packer_fmt.py
@@ -0,0 +1,22 @@
+"""Rewrites all Packer configuration files to a canonical format."""
+
+import argparse
+import pathlib
+import sys
+import hooks.utils
+
+
+def main():
+    """Main entrypoint."""
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("file", nargs="+", type=pathlib.Path)
+    args = parser.parse_args()
+    hooks.utils.check_executable("packer")
+    return hooks.utils.bulk_check(
+        lambda x: hooks.utils.check_file(["packer", "fmt", "-diff", x]),
+        args.file,
+    )
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/hooks/packer_validate.py b/hooks/packer_validate.py
new file mode 100644
index 0000000000000000000000000000000000000000..7df19eb0fc6950ca736d0d0bb1a7f1feeba18db2
--- /dev/null
+++ b/hooks/packer_validate.py
@@ -0,0 +1,22 @@
+"""Checks that the Packer template is valid."""
+
+import argparse
+import pathlib
+import sys
+import hooks.utils
+
+
+def main():
+    """Main entrypoint."""
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("file", nargs="+", type=pathlib.Path)
+    args = parser.parse_args()
+    hooks.utils.check_executable("packer")
+    return hooks.utils.bulk_check(
+        lambda x: hooks.utils.check_file(["packer", "validate", x]),
+        args.file,
+    )
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/setup.py b/setup.py
index 1b48a0a584e6b66b7a0945e153e48033853d6f7a..de3c388eb1b39210cefc725e71fc002ddc4933e6 100644
--- a/setup.py
+++ b/setup.py
@@ -19,6 +19,9 @@ setup(
             "docker-compose-validate=hooks.docker_compose_validate:main",
             "terraform-validate=hooks.terraform_validate:main",
             "terraform-fmt=hooks.terraform_fmt:main",
+            "packer-fix=hooks.packer_fix:main",
+            "packer-fmt=hooks.packer_fmt:main",
+            "packer-validate=hooks.packer_validate:main",
             "poetry-check=hooks.poetry_check:main",
         ]
     },
diff --git a/test_files/packer.json b/test_files/packer.json
new file mode 100644
index 0000000000000000000000000000000000000000..e26c6e1c5b18fb042ab7546b07d544df09bb5364
--- /dev/null
+++ b/test_files/packer.json
@@ -0,0 +1,16 @@
+{
+  "variables": {
+    "access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
+    "secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}"
+  },
+  "builders": [{
+    "type": "amazon-ebs",
+    "access_key": "{{user `access_key`}}",
+    "secret_key": "{{user `access_key`}}",
+    "region": "us-east-1",
+    "source_ami": "ami-de0d9eb7",
+    "instance_type": "t1.micro",
+    "ssh_username": "ubuntu",
+    "ami_name": "packer-example {{timestamp}}"
+  }]
+}
diff --git a/test_files/template.pkr.hcl b/test_files/template.pkr.hcl
new file mode 100644
index 0000000000000000000000000000000000000000..06f4a867cafc014ef5006f39a5faaf7006de937c
--- /dev/null
+++ b/test_files/template.pkr.hcl
@@ -0,0 +1,42 @@
+
+build {
+  name = "alpine"
+  description = <<EOF
+This build creates alpine images for versions :
+* v3.12
+For the following builders :
+* virtualbox-iso
+EOF
+
+  // the common fields of the source blocks are defined in the
+  // source.builder-type.pkr.hcl files, here we only set the fields specific to
+  // the different versions of ubuntu.
+  source "source.virtualbox-iso.base-alpine-amd64" {
+    name                    = "3.12"
+    iso_url                 = local.iso_url_alpine_312
+    iso_checksum            = "file:${local.iso_checksum_url_alpine_312}"
+    output_directory        = "virtualbox_iso_alpine_312_amd64"
+    boot_command            = local.alpine_312_floppy_boot_command
+    boot_wait               = "10s"
+  }
+
+  source "source.vsphere-iso.base-alpine-amd64" {
+    name                    = "3.12"
+    vm_name                 = "alpine-3.12"
+    iso_url                 = local.iso_url_alpine_312
+    iso_checksum            = "file:${local.iso_checksum_url_alpine_312}"
+    boot_command            = local.alpine_312_floppy_boot_command_vsphere
+    boot_wait               = "10s"
+  }
+
+  source "source.vmware-iso.esxi-base-alpine-amd64" {
+    name                    = "3.12-from-esxi"
+    iso_url                 = local.iso_url_alpine_312
+    iso_checksum            = "file:${local.iso_checksum_url_alpine_312}"
+    boot_command            = local.alpine_312_floppy_boot_command_vsphere
+  }
+
+  provisioner "shell" {
+    inline = ["echo hi"]
+  }
+}