Skip to content

etsy/terraform-demux

Repository files navigation

terraform-demux

A drop-in terraform that picks the right Terraform version for every project — automatically.

CI Go Report Card Latest release Go Reference License: Apache-2.0

demo of running terraform-demux with different required_version constraints

Why terraform-demux?

Switching Terraform versions between projects is annoying. tfenv install, tfswitch, asdf shims — they all need maintenance, and you still forget to run them. terraform-demux reads each project's required_version constraint, downloads the matching Terraform release, verifies its SHA-256 against HashiCorp's published SHA256SUMS, caches it, and execs it — so a single terraform command Just Works in every project.

No per-version installs. No shims. No surprises.

Features

  • Zero-config — reads required_version from your terraform { ... } block, walking parent directories until it finds one
  • Drop-in — installs a terraform symlink, so existing scripts, CI, and IDE plugins keep working
  • Verified downloads — every archive is checked against HashiCorp's published SHA256SUMS before it's cached or executed
  • Concurrency-safe cache — per-binary flock keeps parallel invocations from re-downloading the same release
  • Apple Silicon friendlyTF_DEMUX_ARCH=amd64 lets you run older Terraform releases that have no arm64 build
  • Safer state operations — refuses terraform import, state mv, and state rm on Terraform versions that have config-block alternatives (import, moved, removed); opt back in with one env var when you need to
  • Cross-platform — Linux, macOS, and Windows on amd64 and arm64 (no windows/arm64)

Quick start

Homebrew (recommended)

brew tap etsy/terraform-demux https://github.com/etsy/terraform-demux
brew install terraform-demux

The formula installs the binary as terraform-demux and creates a terraform symlink, so you can keep typing terraform everywhere.

terraform -version

Manual

  1. Grab the binary for your platform from the latest release.
  2. Drop it into a directory on your $PATH. Either keep it as terraform-demux, or rename/symlink it to terraform if you want it to be invoked as the default.

How does it compare?

reads required_version directly drop-in terraform (no shim) auto-installs new versions on first use
terraform-demux
tfenv partial (via tfenv use min-required) shim wrapper ❌ (tfenv install <ver> first)
tfswitch ❌ (manages a symlink you switch) ✅ (interactive)

terraform-demux's niche: you never run a demux subcommand or remember to install a version. You just run terraform.

Configuration

Env var Purpose
TF_DEMUX_LOG Any non-empty value enables verbose logging to stderr. By default logs are buffered and only printed if something goes wrong.
TF_DEMUX_ARCH Override the architecture used to pick a Terraform binary. Common case: TF_DEMUX_ARCH=amd64 on Apple Silicon for older Terraform releases that have no native arm64 build.
TF_DEMUX_ALLOW_STATE_COMMANDS 1, true, or yes bypasses the state-command guard described below.
TF_DEMUX_CACHE_HOME Override the cache directory (otherwise os.UserCacheDir()/terraform-demux/). Useful for sandboxes and CI; also handy on macOS, where XDG_CACHE_HOME is ignored by os.UserCacheDir.

Suggested shell alias for amd64 invocations on Apple Silicon:

alias terraform-amd64="TF_DEMUX_ARCH=amd64 terraform"

TF_DEMUX_* variables are stripped from the environment passed to the child Terraform process, so wrapper-internal config can't accidentally leak into providers or nested invocations.

State-command guard

Native Terraform refactoring blocks are safer and reviewable in code, so terraform-demux refuses these CLI commands by default on the Terraform versions where a block alternative exists:

  • terraform import — refused on Terraform >= 1.5.0 (use the import block).
  • terraform state mv — refused on Terraform >= 1.1.0 (use the moved block).
  • terraform state rm — refused on Terraform >= 1.7.0 (use the removed block).

When you really do need to run one of them, set the override:

TF_DEMUX_ALLOW_STATE_COMMANDS=true terraform state mv ...

The guard matches positional Terraform subcommands only, so flag values like -var=action=import won't trigger it.

Cache directory

terraform-demux caches HashiCorp's release index and downloaded Terraform binaries under os.UserCacheDir()/terraform-demux/ (e.g. ~/Library/Caches/terraform-demux/ on macOS), split into http/ and bin/ subdirectories. Each binary is checksum-verified before it lands in bin/.

Set TF_DEMUX_CACHE_HOME to point the cache somewhere else.

Contributing

PRs and issues welcome. Run go test ./... for the unit tests; ./test.sh exercises the end-to-end flow against the fixtures in testdata/. CI runs on Linux, macOS, and Windows on every push.

License

Apache-2.0 — see LICENSE.

About

A user-friendly launcher (à la bazelisk) for Terraform.

Topics

Resources

License

Code of conduct

Stars

13 stars

Watchers

5 watching

Forks

Packages

 
 
 

Contributors