Back to Blog
Terragrunt
Cli

Terragrunt: A tour of the streamlined CLI

Yousif Akbar
Yousif
Akbar
,
Principal Software Engineer
July 8, 2026

Terragrunt 1.0 made the CLI a stable interface. Every flag, command, and piece of serialized output is covered by a backwards compatibility guarantee that holds for the whole 1.x series: what works today keeps working. That kind of promise only makes sense on a CLI design worth keeping for years, so in the releases leading up to 1.0, we gave it a comprehensive redesign.

If you've been using Terragrunt for years, you may still be typing run-all and --terragrunt-non-interactive out of muscle memory, and your CI scripts may still export TERRAGRUNT_ prefixed environment variables. This post is a tour of the CLI as it exists today: what changed, why it changed, and what the modern invocations look like.

Why the CLI was redesigned

The old CLI grew organically around one design decision: Terragrunt wrapped OpenTofu/Terraform, and any command it didn't recognize got forwarded to the wrapped binary, and unfortunately, that decision had consequences.

Every Terragrunt flag needed a terragrunt- prefix to avoid colliding with OpenTofu/Terraform flags, which made every flag eleven characters longer than it needed to be. Every flag had to be globally available, because Terragrunt couldn't know in advance which command it was wrapping, so terragrunt --help printed a wall of options regardless of what you were doing. Misspell a command and the error came from tofu, telling you OpenTofu had no command by that name, which was technically true and completely unhelpful. On any run, Terragrunt would attempt to provision state buckets and lock tables if they didn't exist, whether or not you asked it to, and throw errors at you if it failed.

RFC #3445 laid all of this out and proposed a CLI that follows a predictable set of rules instead. The timing was deliberate. We wanted to get the surface closer to one we could sustainably manage, then commit to it with the 1.0 guarantee.

run

The core of the redesign is the run command. When you want Terragrunt to orchestrate an OpenTofu/Terraform command, you say so:

terragrunt run plan

Shortcuts still exist for the commands you use constantly, so terragrunt plan and terragrunt apply work exactly as they always did. What changed is the default for everything else: unknown commands are no longer forwarded to OpenTofu/Terraform. If you want to run a command that doesn't have a shortcut, you ask for it explicitly:

terragrunt run -- workspace ls

The -- separates Terragrunt's flags from the arguments destined for the wrapped binary. The same pattern works for passing OpenTofu/Terraform flags through:

terragrunt run -- apply -auto-approve

run also absorbed two commands you may know well. run-all is now a flag:

# Before
terragrunt run-all plan

# After
terragrunt run --all plan

And the old graph command, which never graphed anything (it ran commands across the dependency graph), is now run --graph. One verb, with flags that modify its scope, instead of three commands with overlapping behavior.

exec

run always invokes OpenTofu/Terraform. exec invokes whatever you want, with the same configuration context a run would have: resolved dependencies, fetched source, and inputs exposed as TF_VAR_ environment variables.

Say a unit defines this:

# terragrunt.hcl
inputs = {
  bucket_name = "my-bucket"
}

You can lean on that configuration from any command:

terragrunt exec -- bash -c 'aws s3 ls s3://$TF_VAR_bucket_name'

This turns Terragrunt into a general-purpose context loader for operational tasks. Anything that needs to know what a unit knows (its inputs, its resolved source, its dependency outputs) can get that context without reimplementing HCL parsing. The --in-download-dir flag runs the command inside the unit's fetched module code, which is handy for inspecting what Terragrunt actually downloaded:

terragrunt exec --in-download-dir -- cat main.tf

backend bootstrap

Before the redesign, Terragrunt provisioned backend resources implicitly. Run terragrunt plan against a unit with a remote_state block, and if the S3 bucket didn't exist, Terragrunt created it. Convenient the first time, surprising every time after, especially for teams who wanted tight control over what gets created in their accounts.

Provisioning is now its own command:

terragrunt backend bootstrap

It reads the remote_state block and ensures the resources it describes exist. For an S3 backend, that means the state bucket (with encryption, versioning, and TLS enforcement), the DynamoDB lock table (if configured), and the access logging bucket if you configured one. The command is idempotent: resources that already exist and match the configuration are left alone, and resources that drifted are updated when it's safe to do so.

If you preferred the old automatic behavior, it's still available as an explicit opt-in, either with the --backend-bootstrap flag on a run or the TG_BACKEND_BOOTSTRAP environment variable in your CI environment.

bootstrap has siblings under the same command: backend migrate moves state between backends, and backend delete removes backend state files. Both of these have safety checks to ensure that state is versioned, etc. to help mitigate the risk of state manipulation. Backend lifecycle management used to be a mix of implicit behavior and manual cloud console work; now it's a command group.

find and list

The redesign also added two commands for answering a question that gets harder to answer as a repository grows: what's actually in here?

list gives you the human-readable view:

$ terragrunt list
live/dev/db    live/dev/ec2   live/dev/vpc
live/prod/db   live/prod/ec2  live/prod/vpc

The tree format shows the hierarchy:

$ terragrunt list -T
.
╰── live
    ├── dev
    │   ├── db
    │   ├── ec2
    │   ╰── vpc
    ╰── prod
        ├── db
        ├── ec2
        ╰── vpc

And the long format with --dependencies shows how units relate:

$ terragrunt list -l --dependencies
Type  Path           Dependencies
unit  live/dev/db    live/dev/vpc
unit  live/dev/ec2   live/dev/db, live/dev/vpc
unit  live/dev/vpc

The list command is designed to be the most convenient way for humans to explore their infrastructure estates, with multiple different displays supported for visualization of the estate.

find is the programmatic counterpart. It emits structured output meant for scripts and tooling:

$ terragrunt find --dag --json --dependencies
[
  {
    "type": "unit",
    "path": "vpc"
  },
  {
    "type": "unit",
    "path": "db",
    "dependencies": ["vpc"]
  },
  {
    "type": "unit",
    "path": "ec2",
    "dependencies": ["vpc", "db"]
  }
]

The --dag flag sorts output in dependency order, and --queue-construct-as shows the order Terragrunt would walk the graph for a given command. The difference matters most for destroy, which walks the graph in reverse: dependents come down before the units they depend on.

$ terragrunt find --queue-construct-as=plan
vpc
db
ec2

$ terragrunt find --queue-construct-as=destroy
ec2
db
vpc

If you were using output-module-groups to drive CI logic, find --dag --json --dependencies is its replacement, and a better one: it hands you the actual dependency edges rather than precomputed groups, so your tooling can decide for itself when each unit is free to run.

Consistent flags and environment variables

Underneath the new commands sits a quieter change that probably affects more of your scripts than anything above: naming. The terragrunt- prefix is gone from every flag, and every environment variable moved from TERRAGRUNT_ to TG_ (with backwards compatibility for old environment variable names).

# Before
terragrunt plan --terragrunt-non-interactive
export TERRAGRUNT_NON_INTERACTIVE=true

# After
terragrunt plan --non-interactive
export TG_NON_INTERACTIVE=true

Most renames are mechanical, but a few flags were updated further for clarity along the way. --terragrunt-debug became --inputs-debug, because that's what it actually does (it writes a debug .tfvars file for root-causing input issues). The migration guide has the full table, including the handful of flags that were renamed more substantially or removed.

A few commands were reorganized at the same time, mostly into command groups that collect related behavior:

  • hclfmt is now hcl fmt, and hclvalidate is hcl validate
  • terragrunt-info is now info print
  • graph-dependencies is now dag graph
  • render-json is now render --json -w

The pattern is the same one backend follows: a noun for the domain, a verb for the action.

The 1.x stability guarantee

All of this matters because of what happened at 1.0. CLI flags, serialized output from commands like find, HCL configuration, and the way Terragrunt enriches OpenTofu/Terraform stdout and stderr are all stable for the entire 1.x series. Scripts you write against this CLI today will keep working across 1.x upgrades.

That guarantee is the reason the redesign happened when it did. You only get to make that promise once, and making it on the old CLI would have locked in a decade of quirks. The cleanup came first so that the stable surface would be one worth keeping.

It also makes the CLI a foundation other tooling can build on.

Gruntwork Pipelines drives Terragrunt in CI/CD, and that kind of automation depends on exactly the properties the redesign delivered: structured output that parses the same way every release, and commands whose behavior doesn't shift under you.

Wrapping up

The streamlined CLI comes down to a few ideas: one orchestration verb (run) with flags instead of command variants, arbitrary command execution with unit context (exec), explicit backend management (backend bootstrap), real discovery tooling (find and list), and naming that follows rules you can guess. The CLI reference documents the full surface, and the migration guide covers every rename if you're moving older scripts forward.

A good way to start: run terragrunt list --tree at the root of a repository you work in and see what it tells you. Or pick one CI script still using run-all and TERRAGRUNT_ variables and migrate it; the rename table makes it a short job.

Improvements like the CLI redesign come out of the work we do supporting large Terragrunt estates. If that's the problem you're solving, check out Terragrunt Scale.

There's even a free tier where you can try it out today.