NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Go modules · #367 by repository stars
Last release today
09 Oct 2026
Ships on a steady schedule
a new release about every 8 days
Rarely documented
notes for 10 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
10 years old
1758 releases · first in 2016
Nothing published for this version
Nothing published for this version
Nothing published for this version
One column per quarter.
This is the third release candidate for Terragrunt v1.1.
This is the third release candidate for Terragrunt v1.1.
It carries the same six completed experiments as v1.1.0-rc2, plus bug fixes for those experiments and improvements to how releases are published and verified.
This release completes the following experiments:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayFuture release candidates for v1.1.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.
Please try out this release candidate in lower environments and share your feedback in the associated GitHub discussion.
Dependency output resolution: Resolving a dependency block's outputs now applies the env_vars from the unit's extra_arguments blocks whose commands include output (#6396).
Stack autoinclude: Transitive autoinclude dependencies that point at a stack directory now resolve correctly (#6389).
Scaffold variable detection: terragrunt scaffold now reads input variables from the module directory only, so variable blocks in nested modules or examples no longer leak into the scaffolded inputs (#6381).
| type=stack filter generates a stack but leaves its nested stacks ungenerated, terragrunt stack generate now prints a tip showing how to generate them too (#6387).A stack generates a tree of units from a single terragrunt.stack.hcl file. Wiring one of those units to another used to mean defining dependency blocks in your catalog and threading dependency paths through values. Stack dependencies let you declare those relationships up front instead.
Add an autoinclude block inside a unit or stack block, and Terragrunt generates a partial configuration (a terragrunt.autoinclude.hcl file) next to the generated terragrunt.hcl or terragrunt.stack.hcl that's automatically merged into the unit or stack definition. The new unit.<name>.path and stack.<name>.path references resolve to generated paths, so you don't have to hardcode them:
# terragrunt.stack.hcl
unit "vpc" {
source = "github.com/acme/catalog//units/vpc"
path = "vpc"
}
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
dependency "vpc" {
config_path = unit.vpc.path
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
}
}
}Anything that's valid in a unit configuration is valid in its autoinclude block, so you can also patch catalog units with configuration they don't ship with, like retry rules:
# terragrunt.stack.hcl
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
errors {
retry "transient_errors" {
retryable_errors = [".*Error: transient network issue.*"]
max_attempts = 3
sleep_interval_sec = 5
}
}
}
}The same works for nested stacks: an autoinclude block inside a stack block patches the generated terragrunt.stack.hcl, so you can, for example, add an extra unit to one environment without forking the stack in your catalog.
Stack configurations also gained two capabilities along the way:
include blocks now work in terragrunt.stack.hcl files, so shared stack configuration can live in a parent folder.dependency blocks can target stack directories, and the run queue expands them to the units inside. Note that this relationship only goes one way: units can depend on stacks, but stacks cannot depend on stacks or units.See the stacks documentation for the full reference. Previously gated behind the stack-dependencies experiment, all of this is now enabled by default.
The Content Addressable Store (CAS) deduplicates source downloads across configurations. It addresses repositories and modules by their content, stores them locally, and serves later requests from that local store instead of repeating the fetch. This speeds up catalog cloning, OpenTofu/Terraform source fetching, and stack generation, and identical files occupy disk space once regardless of how many configurations use them.
The CAS is no longer limited to Git. It also deduplicates HTTP, Amazon S3, Google Cloud Storage, Mercurial, and SMB sources, along with OpenTofu/Terraform registry sources fetched via tfr://. See supported sources for how each one resolves and deduplicates content.
CAS is enabled by default. Use the --no-cas flag (or TG_NO_CAS=true) to opt out of it for a run:
terragrunt run --all --no-cas -- planTwo new attributes give you finer control, and both default to off:
update_source_with_cas makes a generated stack self-contained. Set it on a unit, stack, or terraform block with a relative source, and terragrunt stack generate rewrites that source into a content-addressed cas:: reference, so the generated tree no longer depends on the surrounding repository layout. Catalog authors can keep relative paths in their sources and still ship a portable, reproducible stack:
# stacks/networking/terragrunt.stack.hcl
unit "vpc" {
source = "../..//units/vpc"
path = "vpc"
update_source_with_cas = true
}After terragrunt stack generate, the relative path is replaced by a reference to the exact tree the CAS stored:
# Generated output
unit "vpc" {
source = "cas::sha1:f39ea0ebf891c9954c89d07b73b487ff938ef08b"
path = "vpc"
update_source_with_cas = true
}mutable controls how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into .terragrunt-cache and marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Set mutable = true on a terraform block to copy the content instead, making the working tree safe to edit at the cost of extra I/O and disk space:
# units/vpc/terragrunt.hcl
terraform {
source = "github.com/acme/catalog//modules/vpc"
mutable = true
}Previously gated behind the cas experiment, the CAS no longer requires --experiment cas.
terragrunt catalogThe catalog command has been redesigned. It now starts without any configuration, discovers components across your catalog repositories in the background, and streams them into the TUI as they're found.
Discovery is no longer limited to a modules/ directory; components can live anywhere in a catalog repository. To control what gets discovered, add a .terragrunt-catalog-ignore file with .gitignore-style globs for the paths you want filtered out.
Components in the TUI now carry metadata to help you navigate a large catalog: each one shows a kind label (template, stack, unit, or module) and optional tags defined in the front-matter of its README.md. From the component list, press s to open a new screen that interactively collects the values used to scaffold the component into your repository.
Previously gated behind the catalog-redesign experiment, the redesigned catalog is now the default terragrunt catalog experience.
Terragrunt can select units by the files they read, which is the basis of change-based runs in CI. Previously, pointing a unit's terraform block at a local directory didn't mark the files inside that directory as read, so a change to the module wouldn't select the unit.
When a unit's source is a local module, Terragrunt now records the module's *.tf, *.tf.json, *.hcl, *.tofu, and *.tofu.json files as read by that unit, so --filter 'reading=<path>' and --queue-include-units-reading select the unit when a module file changes:
terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- planFor files that reading detection doesn't track on its own, the new mark_glob_as_read() HCL function expands a glob and marks every matching file as read in one call:
locals {
configs = mark_glob_as_read("${get_terragrunt_dir()}/config/{*.yaml,**/*.yaml}")
}Existing pipelines built on --queue-include-units-reading or reading= filters may select more units than before, because changes to local module files now count as reads. Previously gated behind the mark-many-as-read experiment, these behaviors no longer require --experiment mark-many-as-read.
--no-discovery-auth-provider-cmdBy default, Terragrunt runs your --auth-provider-cmd once for every unit it discovers, so HCL functions that need credentials resolve correctly during parsing. In a large repository, that can mean hundreds of invocations before any unit runs, which can dominate wall-clock time on change-based runs.
The --no-discovery-auth-provider-cmd flag (env: TG_NO_DISCOVERY_AUTH_PROVIDER_CMD) skips those invocations during the discovery phase, leaving auth to run only for the units that actually execute:
terragrunt run --all \
--no-discovery-auth-provider-cmd \
--queue-include-units-reading=./changed-file.txt \
-- planWarning
Use this only when you know parsing resolves without credentials. Units whose configuration depends on values from --auth-provider-cmd during discovery (for example, via get_aws_account_id()) will fail to parse when the flag is set.
Previously gated behind the opt-out-auth experiment, the flag now works without --experiment opt-out-auth.
Before a run --all, Terragrunt lists the units it's about to run. That list now renders as a dependency tree by default instead of a flat list, with units nested under their dependencies, so the run order and the relationships between units are visible before anything executes:
The following units will be run, starting with dependencies and then their dependents:
.
├── monitoring
╰── vpc
╰── database
╰── backend-app
The header adapts to direction: dependencies come before dependents on apply, and the order reverses on destroy.
Previously gated behind the dag-queue-display experiment, the tree display no longer requires --experiment dag-queue-display.
terragrunt stack generate --filter './my-stack | type=stack' generates only the selected
stack, not the nested stacks it contains, which can be surprising for a stack of stacks.
When a non-glob | type=stack filter leaves a stack's nested stacks ungenerated, Terragrunt
now prints a tip showing how to generate them too, for example
--filter './my-stack | type=stack' --filter './my-stack/** | type=stack'.
permission denied when generated files overwrite CAS-materialized filesWith the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with permission denied:
generate blocks with if_exists = "overwrite", when the module ships the target file (for example, its own versions.tf).terragrunt.values.hcl, when the unit or stack source already contains one.terragrunt.autoinclude.hcl, when the unit or stack source already contains one..terraform.lock.hcl, when the provider cache server updates a committed lock file during init -upgrade.In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
update_source_with_cas on a terraform block when CAS is disabledterragrunt stack generate --no-cas now fails when a generated unit's terraform block sets update_source_with_cas = true, instead of silently emitting the unit with its relative source unchanged. The relative source has no meaning once CAS is disabled, so the generated unit could not resolve its module. This matches the existing behavior for the same attribute on unit and stack blocks, and for a run invoked with --no-cas.
extra_arguments env vars when resolving dependency outputsResolving a dependency block's outputs now applies the env_vars from the unit's terraform extra_arguments blocks whose commands include output.
Git-based filters (for example terragrunt run --all --filter '[HEAD^1...HEAD]' -- plan) now select units
that read an added or deleted file through mark_glob_as_read, even when that file lives outside the unit's
own directory. Previously only modified files outside a unit reached those units; adding or deleting a file
the glob matched left the reading unit out of the run, so its real config change was skipped. Added files are
matched against the newer reference, and deleted files against the older one where the file still exists.
mark_glob_as_read constrains its walk to a boundarymark_glob_as_read now confines glob expansion to a boundary directory. By default the boundary is the enclosing Git repository root; outside a Git repository it is unset. A pattern whose walk would begin outside the boundary returns an error instead of expanding.
This bounds patterns that resolve higher than intended. For example, "${local.dir}/{*.yaml}" becomes /{*.yaml} when local.dir is empty, which previously walked the entire filesystem. A ? : conditional does not prevent this, because HCL evaluates both branches of a conditional before selecting one. Wrapping the call in try lets the error fall back to a default:
locals {
files = sort(try(mark_glob_as_read("${local.dir}/{*.yaml,*.yml,*.json}"), []))
}Pass a leading --terragrunt-boundary argument to set the boundary explicitly, for example to scope the walk to a subdirectory or to widen it to the filesystem root:
locals {
scoped = mark_glob_as_read("--terragrunt-boundary=/etc/terragrunt", "/etc/terragrunt/{*.yaml}")
all = mark_glob_as_read("--terragrunt-boundary=/", "/{*.yaml}")
}terragrunt scaffold now reads input variables from the module directory itself, matching what OpenTofu and Terraform load for a root module. Previously it scanned subdirectories too, so variable blocks defined in nested modules or examples leaked into the scaffolded inputs even though the module never exposes them.
terragrunt stack generate now resolves interpolated object keys in autoinclude blocks (for example
{ "${local.prefix}_key" = ... }), even when the value references dependency.*. Previously the generated
unit kept the key verbatim, leaking a stack-only reference that is not valid in the unit scope.
autoinclude templatesterragrunt stack generate no longer panics when an autoinclude template interpolates a non-string literal (for example "${0}" or "${true}") alongside a dependency.* reference. The interpolated literal is now rendered to its string form (${0} becomes 0) and the dependency reference is preserved for the unit.
autoinclude dependencies on a stack directoryrun --all no longer fails with "does not contain a terragrunt.hcl file" when an autoinclude dependency points at a stack directory (one holding terragrunt.stack.hcl) and the unit is reached transitively through another unit. The dependency cycle check now skips a target with no unit config, matching the direct dependency case.
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayEach feature is described in the New Features section above.
The corresponding --experiment flags (and TG_EXPERIMENT values) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.
Thank you to everyone who ran these experiments early and filed the feedback that got them here.
Starting with this release, Terragrunt releases are published as immutable releases on GitHub. Once a release is published, its tag and assets can no longer be modified or deleted, so the binary you download is guaranteed to be the same binary that was uploaded when the release was published.
See the releases process documentation for details, and Verifying releases with the GitHub CLI for how to check a download against the release attestation.
The install script now checks downloaded release assets against the release attestation that ships with immutable releases. For releases starting with v1.1.0, when an authenticated GitHub CLI (v2.81.0 or later) is available, the script verifies the checksums file and the binary against the attestation before installing, and aborts if either does not match the published release. The check is skipped with a warning when gh is unavailable, too old, or unauthenticated. Use --no-verify-attestation to opt out.
update_source_with_cas integration with --no-cas by @yhakbar in #6363--terragrunt-boundary to mark_glob_as_read by @yhakbar in #6351--parallelism tweaking considerations better by @yhakbar in #6313v1.1.0 changelog polish by @yhakbar in #6333mark-many-as-read experiment by @yhakbar in #6310cas experiment by @yhakbar in #6254dag-queue-display experiment by @yhakbar in #6320opt-out-auth experiment by @yhakbar in #6321go-git by @yhakbar in #6325catalog-redesign experiment by @yhakbar in #6271lll by @yhakbar in #6377TestWindowsTflintIsInvoked by @yhakbar in #6382lll #2 by @yhakbar in #6385go fix ./... by @yhakbar in #6398Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
This is the second release candidate for Terragrunt v1.1.
This is the second release candidate for Terragrunt v1.1.
It carries the same six completed experiments as v1.1.0-rc1, plus bug fixes for those experiments and improvements to how releases are published and verified.
This release completes the following experiments:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayFuture release candidates for v1.1.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.
Please try out this release candidate in lower environments and share your feedback in the associated GitHub discussion.
mark_glob_as_read constrains its walk to a boundary. Glob expansion is now confined to a boundary directory, defaulting to the enclosing Git repository root. A pattern whose walk would begin outside the boundary returns an error instead of expanding, so a pattern like "${local.dir}/{*.yaml}" that collapses to /{*.yaml} no longer walks the entire filesystem. Pass a leading --terragrunt-boundary argument to set the boundary explicitly. (#6351)
--filter '[HEAD^1...HEAD]' now select units that read an added or deleted file through mark_glob_as_read, even when that file lives outside the unit's own directory. Previously only modified files outside a unit reached those units. (#6352)update_source_with_cas is rejected when CAS is disabled. terragrunt stack generate --no-cas now fails when a generated unit's terraform block sets update_source_with_cas = true, instead of silently emitting an unresolvable relative source. This matches the existing behavior for the same attribute on unit and stack blocks. (#6363)--no-verify-attestation to opt out. (#6344)A stack generates a tree of units from a single terragrunt.stack.hcl file. Wiring one of those units to another used to mean defining dependency blocks in your catalog and threading dependency paths through values. Stack dependencies let you declare those relationships up front instead.
Add an autoinclude block inside a unit or stack block, and Terragrunt generates a partial configuration (a terragrunt.autoinclude.hcl file) next to the generated terragrunt.hcl or terragrunt.stack.hcl that's automatically merged into the unit or stack definition. The new unit.<name>.path and stack.<name>.path references resolve to generated paths, so you don't have to hardcode them:
# terragrunt.stack.hcl
unit "vpc" {
source = "github.com/acme/catalog//units/vpc"
path = "vpc"
}
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
dependency "vpc" {
config_path = unit.vpc.path
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
}
}
}Anything that's valid in a unit configuration is valid in its autoinclude block, so you can also patch catalog units with configuration they don't ship with, like retry rules:
# terragrunt.stack.hcl
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
errors {
retry "transient_errors" {
retryable_errors = [".*Error: transient network issue.*"]
max_attempts = 3
sleep_interval_sec = 5
}
}
}
}The same works for nested stacks: an autoinclude block inside a stack block patches the generated terragrunt.stack.hcl, so you can, for example, add an extra unit to one environment without forking the stack in your catalog.
Stack configurations also gained two capabilities along the way:
include blocks now work in terragrunt.stack.hcl files, so shared stack configuration can live in a parent folder.dependency blocks can target stack directories, and the run queue expands them to the units inside. Note that this relationship only goes one way: units can depend on stacks, but stacks cannot depend on stacks or units.See the stacks documentation for the full reference. Previously gated behind the stack-dependencies experiment, all of this is now enabled by default.
The Content Addressable Store (CAS) deduplicates source downloads across configurations. It addresses repositories and modules by their content, stores them locally, and serves later requests from that local store instead of repeating the fetch. This speeds up catalog cloning, OpenTofu/Terraform source fetching, and stack generation, and identical files occupy disk space once regardless of how many configurations use them.
The CAS is no longer limited to Git. It also deduplicates HTTP, Amazon S3, Google Cloud Storage, Mercurial, and SMB sources, along with OpenTofu/Terraform registry sources fetched via tfr://. See supported sources for how each one resolves and deduplicates content.
CAS is enabled by default. Use the --no-cas flag (or TG_NO_CAS=true) to opt out of it for a run:
terragrunt run --all --no-cas -- planTwo new attributes give you finer control, and both default to off:
update_source_with_cas makes a generated stack self-contained. Set it on a unit, stack, or terraform block with a relative source, and terragrunt stack generate rewrites that source into a content-addressed cas:: reference, so the generated tree no longer depends on the surrounding repository layout. Catalog authors can keep relative paths in their sources and still ship a portable, reproducible stack:
# stacks/networking/terragrunt.stack.hcl
unit "vpc" {
source = "../..//units/vpc"
path = "vpc"
update_source_with_cas = true
}After terragrunt stack generate, the relative path is replaced by a reference to the exact tree the CAS stored:
# Generated output
unit "vpc" {
source = "cas::sha1:f39ea0ebf891c9954c89d07b73b487ff938ef08b"
path = "vpc"
update_source_with_cas = true
}mutable controls how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into .terragrunt-cache and marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Set mutable = true on a terraform block to copy the content instead, making the working tree safe to edit at the cost of extra I/O and disk space:
# units/vpc/terragrunt.hcl
terraform {
source = "github.com/acme/catalog//modules/vpc"
mutable = true
}Previously gated behind the cas experiment, the CAS no longer requires --experiment cas.
terragrunt catalogThe catalog command has been redesigned. It now starts without any configuration, discovers components across your catalog repositories in the background, and streams them into the TUI as they're found.
Discovery is no longer limited to a modules/ directory; components can live anywhere in a catalog repository. To control what gets discovered, add a .terragrunt-catalog-ignore file with .gitignore-style globs for the paths you want filtered out.
Components in the TUI now carry metadata to help you navigate a large catalog: each one shows a kind label (template, stack, unit, or module) and optional tags defined in the front-matter of its README.md. From the component list, press s to open a new screen that interactively collects the values used to scaffold the component into your repository.
Previously gated behind the catalog-redesign experiment, the redesigned catalog is now the default terragrunt catalog experience.
Terragrunt can select units by the files they read, which is the basis of change-based runs in CI. Previously, pointing a unit's terraform block at a local directory didn't mark the files inside that directory as read, so a change to the module wouldn't select the unit.
When a unit's source is a local module, Terragrunt now records the module's *.tf, *.tf.json, *.hcl, *.tofu, and *.tofu.json files as read by that unit, so --filter 'reading=<path>' and --queue-include-units-reading select the unit when a module file changes:
terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- planFor files that reading detection doesn't track on its own, the new mark_glob_as_read() HCL function expands a glob and marks every matching file as read in one call:
locals {
configs = mark_glob_as_read("${get_terragrunt_dir()}/config/{*.yaml,**/*.yaml}")
}Existing pipelines built on --queue-include-units-reading or reading= filters may select more units than before, because changes to local module files now count as reads. Previously gated behind the mark-many-as-read experiment, these behaviors no longer require --experiment mark-many-as-read.
--no-discovery-auth-provider-cmdBy default, Terragrunt runs your --auth-provider-cmd once for every unit it discovers, so HCL functions that need credentials resolve correctly during parsing. In a large repository, that can mean hundreds of invocations before any unit runs, which can dominate wall-clock time on change-based runs.
The --no-discovery-auth-provider-cmd flag (env: TG_NO_DISCOVERY_AUTH_PROVIDER_CMD) skips those invocations during the discovery phase, leaving auth to run only for the units that actually execute:
terragrunt run --all \
--no-discovery-auth-provider-cmd \
--queue-include-units-reading=./changed-file.txt \
-- planWarning
Use this only when you know parsing resolves without credentials. Units whose configuration depends on values from --auth-provider-cmd during discovery (for example, via get_aws_account_id()) will fail to parse when the flag is set.
Previously gated behind the opt-out-auth experiment, the flag now works without --experiment opt-out-auth.
Before a run --all, Terragrunt lists the units it's about to run. That list now renders as a dependency tree by default instead of a flat list, with units nested under their dependencies, so the run order and the relationships between units are visible before anything executes:
The following units will be run, starting with dependencies and then their dependents:
.
├── monitoring
╰── vpc
╰── database
╰── backend-app
The header adapts to direction: dependencies come before dependents on apply, and the order reverses on destroy.
Previously gated behind the dag-queue-display experiment, the tree display no longer requires --experiment dag-queue-display.
permission denied when generated files overwrite CAS-materialized filesWith the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with permission denied:
generate blocks with if_exists = "overwrite", when the module ships the target file (for example, its own versions.tf).terragrunt.values.hcl, when the unit or stack source already contains one.terragrunt.autoinclude.hcl, when the unit or stack source already contains one..terraform.lock.hcl, when the provider cache server updates a committed lock file during init -upgrade.In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
update_source_with_cas on a terraform block when CAS is disabledterragrunt stack generate --no-cas now fails when a generated unit's terraform block sets update_source_with_cas = true, instead of silently emitting the unit with its relative source unchanged. The relative source has no meaning once CAS is disabled, so the generated unit could not resolve its module. This matches the existing behavior for the same attribute on unit and stack blocks, and for a run invoked with --no-cas.
Git-based filters (for example terragrunt run --all --filter '[HEAD^1...HEAD]' -- plan) now select units
that read an added or deleted file through mark_glob_as_read, even when that file lives outside the unit's
own directory. Previously only modified files outside a unit reached those units; adding or deleting a file
the glob matched left the reading unit out of the run, so its real config change was skipped. Added files are
matched against the newer reference, and deleted files against the older one where the file still exists.
mark_glob_as_read constrains its walk to a boundarymark_glob_as_read now confines glob expansion to a boundary directory. By default the boundary is the enclosing Git repository root; outside a Git repository it is unset. A pattern whose walk would begin outside the boundary returns an error instead of expanding.
This bounds patterns that resolve higher than intended. For example, "${local.dir}/{*.yaml}" becomes /{*.yaml} when local.dir is empty, which previously walked the entire filesystem. A ? : conditional does not prevent this, because HCL evaluates both branches of a conditional before selecting one. Wrapping the call in try lets the error fall back to a default:
locals {
files = sort(try(mark_glob_as_read("${local.dir}/{*.yaml,*.yml,*.json}"), []))
}Pass a leading --terragrunt-boundary argument to set the boundary explicitly, for example to scope the walk to a subdirectory or to widen it to the filesystem root:
locals {
scoped = mark_glob_as_read("--terragrunt-boundary=/etc/terragrunt", "/etc/terragrunt/{*.yaml}")
all = mark_glob_as_read("--terragrunt-boundary=/", "/{*.yaml}")
}terragrunt stack generate now resolves interpolated object keys in autoinclude blocks (for example
{ "${local.prefix}_key" = ... }), even when the value references dependency.*. Previously the generated
unit kept the key verbatim, leaking a stack-only reference that is not valid in the unit scope.
autoinclude templatesterragrunt stack generate no longer panics when an autoinclude template interpolates a non-string literal (for example "${0}" or "${true}") alongside a dependency.* reference. The interpolated literal is now rendered to its string form (${0} becomes 0) and the dependency reference is preserved for the unit.
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayEach feature is described in the New Features section above.
The corresponding --experiment flags (and TG_EXPERIMENT values) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.
Thank you to everyone who ran these experiments early and filed the feedback that got them here.
Starting with this release, Terragrunt releases are published as immutable releases on GitHub. Once a release is published, its tag and assets can no longer be modified or deleted, so the binary you download is guaranteed to be the same binary that was uploaded when the release was published.
See the releases process documentation for details, and Verifying releases with the GitHub CLI for how to check a download against the release attestation.
The install script now checks downloaded release assets against the release attestation that ships with immutable releases. For releases starting with v1.1.0, when an authenticated GitHub CLI (v2.81.0 or later) is available, the script verifies the checksums file and the binary against the attestation before installing, and aborts if either does not match the published release. The check is skipped with a warning when gh is unavailable, too old, or unauthenticated. Use --no-verify-attestation to opt out.
update_source_with_cas integration with --no-cas by @yhakbar in #6363--terragrunt-boundary to mark_glob_as_read by @yhakbar in #6351--parallelism tweaking considerations better by @yhakbar in #6313v1.1.0 changelog polish by @yhakbar in #6333mark-many-as-read experiment by @yhakbar in #6310cas experiment by @yhakbar in #6254dag-queue-display experiment by @yhakbar in #6320opt-out-auth experiment by @yhakbar in #6321go-git by @yhakbar in #6325catalog-redesign experiment by @yhakbar in #6271Nothing published for this version
Nothing published for this version
This is the first release candidate for Terragrunt v1.1.
This is the first release candidate for Terragrunt v1.1.
This release completes the following experiments:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayFuture release candidates for v1.1.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.
Please try out this release candidate in lower environments and share your feedback in the Associated GitHub discussion.
A stack generates a tree of units from a single terragrunt.stack.hcl file. Wiring one of those units to another used to mean defining dependency blocks in your catalog and threading dependency paths through values. Stack dependencies let you declare those relationships up front instead.
Add an autoinclude block inside a unit or stack block, and Terragrunt generates a partial configuration (a terragrunt.autoinclude.hcl file) next to the generated terragrunt.hcl or terragrunt.stack.hcl that's automatically merged into the unit or stack definition. The new unit.<name>.path and stack.<name>.path references resolve to generated paths, so you don't have to hardcode them:
# terragrunt.stack.hcl
unit "vpc" {
source = "github.com/acme/catalog//units/vpc"
path = "vpc"
}
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
dependency "vpc" {
config_path = unit.vpc.path
}
inputs = {
vpc_id = dependency.vpc.outputs.vpc_id
}
}
}Anything that's valid in a unit configuration is valid in its autoinclude block, so you can also patch catalog units with configuration they don't ship with, like retry rules:
# terragrunt.stack.hcl
unit "app" {
source = "github.com/acme/catalog//units/app"
path = "app"
autoinclude {
errors {
retry "transient_errors" {
retryable_errors = [".*Error: transient network issue.*"]
max_attempts = 3
sleep_interval_sec = 5
}
}
}
}The same works for nested stacks: an autoinclude block inside a stack block patches the generated terragrunt.stack.hcl, so you can, for example, add an extra unit to one environment without forking the stack in your catalog.
Stack configurations also gained two capabilities along the way:
include blocks now work in terragrunt.stack.hcl files, so shared stack configuration can live in a parent folder.dependency blocks can target stack directories, and the run queue expands them to the units inside. Note that this relationship only goes one way: units can depend on stacks, but stacks cannot depend on stacks or units.See the stacks documentation for the full reference. Previously gated behind the stack-dependencies experiment, all of this is now enabled by default.
The Content Addressable Store (CAS) deduplicates source downloads across configurations. It addresses repositories and modules by their content, stores them locally, and serves later requests from that local store instead of repeating the fetch. This speeds up catalog cloning, OpenTofu/Terraform source fetching, and stack generation, and identical files occupy disk space once regardless of how many configurations use them.
The CAS is no longer limited to Git. It also deduplicates HTTP, Amazon S3, Google Cloud Storage, Mercurial, and SMB sources, along with OpenTofu/Terraform registry sources fetched via tfr://. See supported sources for how each one resolves and deduplicates content.
CAS is enabled by default. Use the --no-cas flag (or TG_NO_CAS=true) to opt out of it for a run:
terragrunt run --all --no-cas -- planTwo new attributes give you finer control, and both default to off:
update_source_with_cas makes a generated stack self-contained. Set it on a unit, stack, or terraform block with a relative source, and terragrunt stack generate rewrites that source into a content-addressed cas:: reference, so the generated tree no longer depends on the surrounding repository layout. Catalog authors can keep relative paths in their sources and still ship a portable, reproducible stack:
# stacks/networking/terragrunt.stack.hcl
unit "vpc" {
source = "../..//units/vpc"
path = "vpc"
update_source_with_cas = true
}After terragrunt stack generate, the relative path is replaced by a reference to the exact tree the CAS stored:
# Generated output
unit "vpc" {
source = "cas::sha1:f39ea0ebf891c9954c89d07b73b487ff938ef08b"
path = "vpc"
update_source_with_cas = true
}mutable controls how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into .terragrunt-cache and marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Set mutable = true on a terraform block to copy the content instead, making the working tree safe to edit at the cost of extra I/O and disk space:
# units/vpc/terragrunt.hcl
terraform {
source = "github.com/acme/catalog//modules/vpc"
mutable = true
}Previously gated behind the cas experiment, the CAS no longer requires --experiment cas.
terragrunt catalogThe catalog command has been redesigned. It now starts without any configuration, discovers components across your catalog repositories in the background, and streams them into the TUI as they're found.
Discovery is no longer limited to a modules/ directory; components can live anywhere in a catalog repository. To control what gets discovered, add a .terragrunt-catalog-ignore file with .gitignore-style globs for the paths you want filtered out.
Components in the TUI now carry metadata to help you navigate a large catalog: each one shows a kind label (template, stack, unit, or module) and optional tags defined in the front-matter of its README.md. From the component list, press s to open a new screen that interactively collects the values used to scaffold the component into your repository.
Previously gated behind the catalog-redesign experiment, the redesigned catalog is now the default terragrunt catalog experience.
Terragrunt can select units by the files they read, which is the basis of change-based runs in CI. Previously, pointing a unit's terraform block at a local directory didn't mark the files inside that directory as read, so a change to the module wouldn't select the unit.
When a unit's source is a local module, Terragrunt now records the module's *.tf, *.tf.json, *.hcl, *.tofu, and *.tofu.json files as read by that unit, so --filter 'reading=<path>' and --queue-include-units-reading select the unit when a module file changes:
terragrunt run --all --filter 'reading=./modules/vpc/main.tf' -- planFor files that reading detection doesn't track on its own, the new mark_glob_as_read() HCL function expands a glob and marks every matching file as read in one call:
locals {
configs = mark_glob_as_read("${get_terragrunt_dir()}/config/{*.yaml,**/*.yaml}")
}Existing pipelines built on --queue-include-units-reading or reading= filters may select more units than before, because changes to local module files now count as reads. Previously gated behind the mark-many-as-read experiment, these behaviors no longer require --experiment mark-many-as-read.
--no-discovery-auth-provider-cmdBy default, Terragrunt runs your --auth-provider-cmd once for every unit it discovers, so HCL functions that need credentials resolve correctly during parsing. In a large repository, that can mean hundreds of invocations before any unit runs, which can dominate wall-clock time on change-based runs.
The --no-discovery-auth-provider-cmd flag (env: TG_NO_DISCOVERY_AUTH_PROVIDER_CMD) skips those invocations during the discovery phase, leaving auth to run only for the units that actually execute:
terragrunt run --all \
--no-discovery-auth-provider-cmd \
--queue-include-units-reading=./changed-file.txt \
-- planWarning
Use this only when you know parsing resolves without credentials. Units whose configuration depends on values from --auth-provider-cmd during discovery (for example, via get_aws_account_id()) will fail to parse when the flag is set.
Previously gated behind the opt-out-auth experiment, the flag now works without --experiment opt-out-auth.
Before a run --all, Terragrunt lists the units it's about to run. That list now renders as a dependency tree by default instead of a flat list, with units nested under their dependencies, so the run order and the relationships between units are visible before anything executes:
The following units will be run, starting with dependencies and then their dependents:
.
├── monitoring
╰── vpc
╰── database
╰── backend-app
The header adapts to direction: dependencies come before dependents on apply, and the order reverses on destroy.
Previously gated behind the dag-queue-display experiment, the tree display no longer requires --experiment dag-queue-display.
permission denied when generated files overwrite CAS-materialized filesWith the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with permission denied:
generate blocks with if_exists = "overwrite", when the module ships the target file (for example, its own versions.tf).terragrunt.values.hcl, when the unit or stack source already contains one.terragrunt.autoinclude.hcl, when the unit or stack source already contains one..terraform.lock.hcl, when the provider cache server updates a committed lock file during init -upgrade.In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
terragrunt stack generate now resolves interpolated object keys in autoinclude blocks (for example
{ "${local.prefix}_key" = ... }), even when the value references dependency.*. Previously the generated
unit kept the key verbatim, leaking a stack-only reference that is not valid in the unit scope.
autoinclude templatesterragrunt stack generate no longer panics when an autoinclude template interpolates a non-string literal (for example "${0}" or "${true}") alongside a dependency.* reference. The interpolated literal is now rendered to its string form (${0} becomes 0) and the dependency reference is preserved for the unit.
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
stack-dependenciescascatalog-redesignmark-many-as-readopt-out-authdag-queue-displayEach feature is described in the New Features section above.
The corresponding --experiment flags (and TG_EXPERIMENT values) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.
Thank you to everyone who ran these experiments early and filed the feedback that got them here.
--parallelism tweaking considerations better by @yhakbar in #6313v1.1.0 changelog polish by @yhakbar in #6333mark-many-as-read experiment by @yhakbar in #6310cas experiment by @yhakbar in #6254dag-queue-display experiment by @yhakbar in #6320opt-out-auth experiment by @yhakbar in #6321go-git by @yhakbar in #6325Nothing published for this version
Faster read-file tracking with the mark-many-as-read experiment
mark-many-as-read experimentWith the mark-many-as-read experiment enabled, Terragrunt records every module file it marks as read during parsing. The bookkeeping for that record scaled quadratically: each new path was checked against every path recorded so far, which got expensive for units with large local module sources, and monorepos paid that cost again for every unit and every command.
Recording a path now takes constant time no matter how many paths came before it, and re-marking already-recorded files is cheaper still. The reading lists reported by find and list are unchanged.
assume_role: preserve commas inside list expressionsTerragrunt previously failed to correctly parse assume_role attributes containing list values such as transitive_tag_keys or policy_arns. Commas inside nested list expressions were incorrectly treated as top-level separators, causing generated configurations to fail with parsing errors.
assume_role = {
role_arn = "arn:aws:iam::123456789012:role/test-role"
transitive_tag_keys = ["Project", "Projects"]
}This resulted in errors similar to:
Missing item separator; Expected a comma to mark the beginning of the next item.
Terragrunt now preserves commas inside nested list and object expressions when parsing assume_role blocks, allowing configurations containing array attributes to be processed correctly.
Thanks to @Rahul-Kumar-prog for contributing this fix!
Features gated behind a completed experiment were treated as disabled instead of permanently enabled, so functionality that graduated out of experiment status could silently stop working.
The one affected code path was hcl validate --inputs with a git filter expression such as --filter '[HEAD~1...HEAD]': after the filter-flag experiment completed, the command stopped preparing git worktrees for the filter. Git filter expressions now work with hcl validate --inputs again, matching find, list, and the other commands that accept filters.
When resolving an include block with expose = true, Terragrunt surfaced low-level parsing or conversion errors with no indication of which include block, file, or field was at fault. This was especially hard to debug for errors that carry no source location, such as:
unsuitable value: a bool is required
The error is now annotated with the include block name, the included (parent) file path, and a single dotted locator for the failing field — the top-level config field (dependency, inputs, locals, or feature) plus the attribute path within it when go-cty can determine one:
exposed include "root" (/path/to/root.hcl): dependency.outputs["enabled"]: unsuitable value: a bool is required
When go-cty cannot resolve a precise attribute path, the locator degrades to just the field name:
exposed include "root" (/path/to/root.hcl): dependency: unsuitable value: a bool is required
Errors that originate in HCL parsing already carry a source range (file:line:column) and are preserved unchanged. This narrows the search from the entire configuration tree to a specific file and field.
A graph traversal combined with an intersected filter dropped the components reached in discovery.
e.g., ...a-dependent | type=unit (the dependents of a-dependent, intersected with type of units) returned only a-dependent itself instead of its dependents, and git-change traversals such as ...[HEAD~1...HEAD] | type=unit lost the dependents of the changed units.
A component that matched both a graph expression target and a positive filesystem or git filter was classified as discovered before the graph traversal ran, so the traversal never expanded from it. Terragrunt now checks graph expression targets first, so intersecting a traversal with another filter keeps the dependencies and dependents it reaches.
generate blocks now honor hcl_fmtTerragrunt now accepts hcl_fmt on generate blocks and preserves the setting when configurations are parsed, written, and parsed again. This lets generated .tf, .hcl, and .tofu files opt out of automatic HCL formatting by setting hcl_fmt = false, matching the existing generate = { ... } attribute-map behavior.
OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTESTerragrunt previously hardcoded the service.name resource attribute to terragrunt for every emitted trace and metric, ignoring the standard OpenTelemetry environment variables. Multiple Terragrunt invocations could not be distinguished in an OpenTelemetry backend without an intermediate collector to rewrite the attribute.
The resource is now composed via resource.New with WithFromEnv() placed after Terragrunt's defaults, so OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES are honored on every span and metric. Per the OpenTelemetry specification, OTEL_SERVICE_NAME takes precedence over a service.name entry in OTEL_RESOURCE_ATTRIBUTES. The default service.name remains terragrunt when neither variable is set.
s3:: sources: support virtual-hosted-style URLss3:: source URLs using the virtual-hosted-style S3 endpoint format were rejected:
terraform {
source = "s3::https://my-bucket.s3.us-west-2.amazonaws.com/terraform/modules/myapp.zip"
}This resulted in errors like:
ERROR downloading source url s3::https://my-bucket.s3.us-west-2.amazonaws.com/...
* URL is not a valid S3 URL
Terragrunt now accepts every AWS S3 endpoint form, including virtual-hosted-style URLs (<bucket>.s3.<region>.amazonaws.com) and modern path-style URLs (s3.<region>.amazonaws.com).
On Windows, running a Terragrunt command from Nushell could leave the shell unable to read input afterward, with keystrokes such as the arrow keys appearing as raw escape sequences instead of being interpreted.
While it runs, Terragrunt reconfigures the console it shares with the parent shell so that terminal escape sequences are processed, but it did not put the original mode back when it exited. PowerShell reapplies its own console settings on every prompt and recovers on its own, so the problem surfaces only in shells that keep the inherited mode, such as Nushell. Terragrunt now records the console mode at startup and restores it on exit, returning the shell to the state it was in beforehand.
Reported in #6245.
<url>.mdEvery docs page is now served as clean Markdown at the same URL with .md appended. For example, /getting-started/install is also available at /getting-started/install.md.
curl https://docs.terragrunt.com/getting-started/install.mdThe .md version contains the page content without the site navigation or other surrounding HTML, which makes it well suited as context for LLMs and AI tooling: it is smaller and carries only the documentation itself. Coverage includes every page, including the CLI command reference and the changelog.
This complements the existing llms.txt and llms-full.txt files by providing a per-page Markdown source.
optional-hooks — Add experimental --no-hooks flag support for terragrunt runThe terragrunt run command now supports an experimental --no-hooks flag for disabling hook execution during command runs.
The feature is gated behind the optional-hooks experiment and skips execution of before_hook, after_hook, and error_hook blocks when enabled.
TG_EXPERIMENT=optional-hooks terragrunt run --no-hooks planThis feature is currently experimental because disabling hooks changes Terragrunt execution semantics and may evolve in future releases.
Using --no-hooks without enabling the optional-hooks experiment will return an error.
hook-context-env experiment exposes additional TG_CTX_* env vars to hooksEnable the new hook-context-env experiment to surface three additional environment variables to every before_hook, after_hook, and error_hook:
TG_CTX_HOOK_TYPE — before_hook, after_hook, or error_hook, identifying which lifecycle phase invoked the hook.TG_CTX_SOURCE — the resolved terraform source URL (CLI --source override, else evaluated terraform.source with source-map applied, else .).TG_CTX_TERRAGRUNT_DIR — the directory of the current Terragrunt config.terragrunt run --all --experiment hook-context-env -- applyThese variables make it easier to share a single hook script across lifecycle phases and to access the unit's source and config directory without threading them through hook arguments.
cas: fallbacks now emit telemetryWhen the cas experiment is enabled and a CAS operation cannot complete, Terragrunt falls back to a slower path (the standard download client, or a temporary clone when the shared git store is unavailable) and keeps going. Until now the only record of a fallback was a warning in the logs, which made it impractical to measure how often CAS degrades across a fleet.
Each fallback now also emits a cas_fallback telemetry event whose reason attribute identifies the cause: init_error, getter_error, git_store_unavailable, probe_failure, or stack_generation_error. Operators collecting OpenTelemetry traces or metrics from Terragrunt can count and alert on these events to judge CAS health before relying on it by default.
catalog commandThe catalog command now accepts the --no-cas and --cas-clone-depth flags, which were already available on run, stack generate, and stack run. When --no-cas is set, catalog repositories are cloned with plain Git even if the cas experiment is enabled. --cas-clone-depth controls the git clone --depth value the CAS uses when cloning catalog repositories.
terragrunt catalog --experiment cas --cas-clone-depth=-1cas — update_source_with_cas requires a literal source stringWhen a catalog unit, stack, or terraform block set update_source_with_cas = true with a source that was not a literal string, rewriting silently produced a wrong source. Interpolation such as "../units/${local.name}" had the interpolated portion dropped, leaving a bare prefix; a reference such as local.foo resolved to the directory containing the block itself. In both cases stack generation packaged the wrong directory without any error.
Stack generation now fails with an error explaining that update_source_with_cas requires a literal source string. Non-literal expressions, including interpolation, function calls, and references like local.foo, are rejected.
cas — Malformed cas:: references fail with a clear errorA cas:: source with a malformed hash, such as cas::sha1:a, used to fail with an opaque internal error while looking the hash up in the store.
CAS references are now validated up front: the hash must be lowercase hexadecimal with exactly 40 characters for sha1 or 64 for sha256. References that don't match are rejected with an error identifying the bad reference.
cas — Repositories with submodules now clone correctlyCloning a repository that contains git submodules through the Content Addressable Store failed while ingesting the repository:
git_cat_file: fatal: Not a valid object name <hash>
A submodule appears in the repository tree as a pointer to a commit in another repository, so the object behind it cannot be read from the repository being cloned.
The CAS now fetches each submodule from the URL registered in .gitmodules at its pinned commit and materializes its contents in place, including nested submodules. Relative submodule URLs (such as ../sibling.git) are resolved against the parent repository URL, matching git's behavior. Submodule contents are stored and deduplicated like any other content, so repeated clones reuse the cache.
catalog-redesign — Failures now exit nonzero and name the sources that failedThe redesigned catalog exited with code 0 even when it failed: a session that ended on an unreachable repository, a failed scaffold, or a failed copy reported success in its exit code. Repositories that failed to load during discovery were dropped too: the warning logged for each one was drawn over by the full-screen interface, so a run where every source failed showed the same "No catalog sources were discovered" screen as a run that genuinely found nothing.
The catalog now exits nonzero when the session ends on a failure: a discovery failure that leaves nothing to browse, a failed scaffold, or a failed copy. Quitting a working session still exits 0. When some sources fail to load while others succeed, the catalog stays usable and a clean quit still exits 0; the component list shows how many sources failed, and the failed repositories are printed with their causes after the catalog closes. When every source fails, the error screen lists each failed repository instead of claiming nothing was found, and dismissing it exits nonzero.
Running terragrunt catalog without an interactive terminal, such as in CI, used to fail with a raw error from the underlying TUI library:
bubbletea: error opening TTY: bubbletea: could not open TTY: open /dev/tty: no such device or address
It now fails immediately with an error stating that the catalog command requires an interactive terminal.
catalog-redesign — Scaffolding a component no longer fails with a path-traversal errorScaffolding a component from the catalog (pressing s) could fail on macOS while downloading the source:
subdirectory component contain path traversal out of the repository
The catalog caches each repository under the system temporary directory, which macOS reports through a symlink (/var/folders/... pointing at /private/var/folders/...). The source location Terragrunt handed to the downloader was built against the unresolved path, so it pointed outside the cached repository and was rejected.
Terragrunt now resolves the temporary directory before discovering components, so the source stays inside the repository and scaffolding proceeds.
stack-dependencies: HCL tooling now handles autoincludeTwo tooling gaps around the experimental autoinclude block are closed:
hcl validate now validates autoinclude blocks. With the stack-dependencies experiment enabled, validating a terragrunt.stack.hcl that declares autoinclude runs the same strict checks as terragrunt stack generate. A malformed block (for example, a locals block inside autoinclude) is now reported at validation time instead of passing hcl validate and only failing later during generation. Without the experiment, validation behavior is unchanged.
read_terragrunt_config() can read stack-level autoinclude files. Reading a generated terragrunt.autoinclude.stack.hcl previously failed because the file was decoded as a unit configuration, which rejects its unit and stack blocks. With the experiment enabled, the file is now decoded as the stack-file fragment it is, returning its unit and stack blocks the same way reading a terragrunt.stack.hcl does. Unit-level terragrunt.autoinclude.hcl files already read correctly and continue to do so.
stack-dependencies: autoinclude merges like a regular includeA generated unit autoinclude (terragrunt.autoinclude.hcl) now merges into the unit's config using the same default merge as a regular include, which is a shallow merge, applied uniformly across generation, full parse, and discovery. Top-level keys from the unit and the autoinclude combine, and on a conflict the autoinclude wins and replaces the unit's value rather than deep-merging nested maps; locals stay local in scope.
A generated stack autoinclude (terragrunt.autoinclude.stack.hcl) injects unit and stack blocks into the generated terragrunt.stack.hcl. An injected block whose name matches an existing unit or stack now overrides that block wholesale, consistent with unit autoinclude override semantics, and an injected block with a new name is added. This applies uniformly across generation, full parse, and discovery, so a name match no longer produces a duplicate-name error. A stack autoinclude may not declare a top-level dependency block (stacks have no dependencies; declare the dependency inside the target unit's own autoinclude).
A dependency block injected through an autoinclude is now available before a unit's remote_state is evaluated, so referencing dependency.<name>.outputs.<key> there no longer fails. remote_state now behaves the same as generate blocks.
stack-dependencies: autoinclude blocks can reference values.*An autoinclude block may now reference the stack's values.*. Previously a values.* reference was rejected at stack generate time, except in a dependency config_path. It now resolves to a literal like local.*, unit.<name>.path, and stack.<name>.path, wherever it appears: inputs, generate, remote_state, mock_outputs, and config_path.
Function calls in an autoinclude now resolve at generate time too, in the terragrunt.stack.hcl context, instead of being kept verbatim and evaluated in the generated unit. Only a dependency.* reference (a dependency's outputs) stays verbatim and resolves inside the unit; in a mixed expression the stack-level parts resolve and only the dependency.* reference is kept.
Because functions now evaluate against the stack file rather than the unit, directory and include functions report the stack file's location: get_terragrunt_dir returns the stack file's directory, and path_relative_to_include returns ".". If you relied on these resolving in the unit, move them to the unit's own configuration, or derive a per-unit value such as a remote_state backend key from unit.<name>.path.
A locals block inside an autoinclude remains rejected; declare stack-level locals in terragrunt.stack.hcl instead.
stack-dependencies: stack dependencies resolve values.* in the target stack's localsExpanding a dependency that points at a generated stack directory no longer fails when that stack's terragrunt.stack.hcl reads values.* in its locals block. Previously, terragrunt stack generate succeeded but terragrunt run --all then failed with There is no variable named "values" while expanding the dependency into its units.
Dependency expansion now reads the generated terragrunt.values.hcl next to each terragrunt.stack.hcl it visits, including nested stacks, so each nesting level resolves values.* from its own values file, the same way a full stack parse does.
stack-dependencies: component path references in values no longer break next to autoinclude blocksA unit or stack block's values can reference unit.<name>.path and stack.<name>.path even when another block in the same terragrunt.stack.hcl declares an autoinclude. Previously, the presence of any autoinclude block made stack generate reject those references with Unknown variable; There is no variable named "unit", while the same file without an autoinclude generated fine.
OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES environment variables by @Tensho in #6256terragrunt.stack.hcl files by @yhakbar in #6291autoinclude in hcl validate and fix read_terragrunt_config() for configurations using autoinclude by @yhakbar in #6297completedSince by @yhakbar in #6252.md changelog files by @yhakbar in #6286autoinclude documentation by @yhakbar in #6299autoinclude more flexible, supporting values.* by @yhakbar in #6283--no-cas flag to the catalog command by @yhakbar in #6292Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
tfr:// source URLs accept an optional version
tfr:// source URLs accept an optional versionThe version query parameter on tfr:// source URLs is now optional. When omitted, Terragrunt queries the registry's list-versions endpoint and downloads the latest stable version, matching how OpenTofu and Terraform resolve a module reference that has no version constraint.
terraform {
source = "tfr:///terraform-aws-modules/vpc/aws"
}Prereleases are excluded from resolution, so a registry that only publishes 4.0.0-rc1 alongside 3.3.0 will pin to 3.3.0. Pin a version explicitly with ?version= when you need reproducible builds or want to opt into a prerelease.
Thanks to @raman1236 for contributing this feature!
update_source_with_cas: preserve //subdir on a unit's terraform.sourceWhen a unit's terraform.source used the // subdir convention (for example, source = "../..//modules/foo") and opted into update_source_with_cas, the rewritten source dropped the //subdir tail and the synthetic tree contained only the leaf module's files. A module that referenced a sibling via a relative path (source = "../bar") could not resolve that reference after materialization.
Rewrites now preserve the original //subdir (for example, cas::sha1:<hash>//modules/foo), and the synthetic tree is rooted at the path before //, so sibling files reachable via relative paths land in the materialized working directory.
Sources without // are unchanged: the tree stays scoped to the leaf module, and the rewritten reference has no //subdir tail.
--filter now detects affected units on WindowsOn Windows, terragrunt find --filter '[origin/main...HEAD]' (and its variants) returned no affected units even when git diff reported changed files. The source= and reading= filters were affected by the same problem.
Filter glob patterns are always written with forward slashes, but the affected-unit comparison was being made with Windows backslash separators, so nothing matched. Terragrunt now compares paths consistently with forward slashes on every platform, and the filter detects changed units on Windows as it already did on Linux and macOS.
Reported in #6214.
startswith, endswith, strcontains, and run_cmd no longer panic on malformed callsCalling startswith, endswith, or strcontains with the wrong number of arguments (for example a single argument instead of two) crashed Terragrunt instead of reporting a configuration error. Calling run_cmd with only option flags and no command (for example run_cmd("--terragrunt-quiet")) crashed the same way.
These calls now return a clear error: a wrong-number-of-parameters error for the string functions, and an empty-command error for run_cmd.
--parallelism flag no longer accepts non-positive numbersPreviously, terragrunt commands that accept the --parallelism flag (or equivalently the $TG_PARALLELISM environment variable) used to hang indefinitely when invoked with --parallelism=0.
Terragrunt now validates that the value is positive and exits with an error otherwise.
Reported in #6211
cas — content-addressing for non-git sourcesCAS now covers module sources beyond git: http(s), Amazon S3, Google Cloud Storage, and Mercurial. Repeat runs against an unchanged remote reuse the cached tree instead of downloading the bytes again.
Before fetching, CAS issues a cheap remote probe (an HTTP HEAD, an S3 object-attributes lookup, a GCS metadata read, or hg identify) to derive a cache key without pulling the source. On a hit, the cached tree is linked directly; on a miss, or when the remote exposes no usable signal, CAS downloads the source, ingests it, and keys the resulting tree by its content hash. A remote that publishes a new version under the same address pins to a new entry, so a stale cache cannot serve outdated bytes.
cas — OpenTofu/Terraform registry sourcesModule sources of the form tfr://... are now content-addressed in CAS. Repeat runs against the same pinned registry version reuse the cached module instead of re-downloading the archive from the registry.
CAS resolves a tfr:// source by asking the registry where the underlying archive lives and uses that resolved URL as the cache key. Two runs that pin the same version share one entry; a republish under the same version pins to a new entry, so a stale cache cannot serve outdated bytes.
stack-dependencies: unit.<name>.path and stack.<name>.path resolve in valuesThe stack-dependencies experiment now exposes unit.<name>.path and stack.<name>.path when evaluating the values attribute of a unit or stack block, not only inside autoinclude blocks. A parent stack can pass the generated path of a sibling component down into a child stack, so a unit nested in that child stack can depend on a unit that lives at a different level of the hierarchy.
unit "vpc" {
source = "../catalog/units/vpc"
path = "vpc"
}
stack "app" {
source = "../catalog/stacks/app"
path = "app"
values = {
vpc_path = unit.vpc.path
}
}A unit inside the app stack reads values.vpc_path and uses it as the config_path of an autoinclude dependency, wiring the cross-level relationship at generation time. Paths follow the same layout the generator produces, including no_dot_terragrunt_stack on the referenced block.
stack-dependencies: simplified unit.* / stack.* ref shapeThe stack-dependencies experiment no longer resolves stack.<name>.<unit_name>.path or stack.<name>.<nested_stack>.path. Only the top-level stack.<name>.path and unit.<name>.path forms remain. stack.<name>.name and unit.<name>.name are gone too; both only ever echoed the label that the reference already had to spell out.
Nested references required parsing every nested catalog up front and conflicted with the reserved name and path attributes on each ref: a nested unit named name or path could not be addressed.
To depend on a generated unit inside a stack, compute the path as ${stack.<name>.path}/<unit-relative-path> directly. The layout under a stack's generated directory follows no_dot_terragrunt_stack on the parent stack and on each unit, so hand-computed paths must mirror that resolution.
stack-dependencies: .terragrunt-stack-origin no longer writtenTerragrunt no longer writes the .terragrunt-stack-origin file when generating nested stacks. Set update_source_with_cas = true on your unit and stack blocks if you would like relative paths in your catalog to resolve correctly instead.
tfr:// module registry URLs by @yhakbar in #6112cas.WithFS by @yhakbar in #6195terragrunt.stack.hcl, including autoinclude by @yhakbar in #6166terraform.source URLs with // by @yhakbar in #6218// by @yhakbar in #6234--parallelism value is positive by @ccmtaylor in #6212fatih/structs dependency by @yhakbar in #6186go-homedir direct dependency by @yhakbar in #6184go-errors as a dependency by @yhakbar in #6182// path test for source with CAS by @yhakbar in #6239Nothing published for this version
…to terragrunt run . This flag might experience breaking changes based on community feedback for the duration of the experiment.
terragrunt no longer hangs when download_dir is a non-hidden subdirectory of the unitSetting download_dir (via the attribute, --download-dir, or TG_DOWNLOAD_DIR) to a subdirectory of the unit's working directory whose name did not start with a dot caused commands that prepare the OpenTofu or Terraform source (apply, plan, run, and similar) to hang.
For example:
# /infra/web/terragrunt.hcl
download_dir = "cache"
terraform {
source = "./mod"
}Here terragrunt apply would copy ./mod into cache/, see the new cache/ directory on the next read of the unit, and recurse into it. The default .terragrunt-cache was unaffected because Terragrunt's source-copy step skips any directory whose name starts with a dot.
These configurations now produce an immediate error identifying the source and destination paths.
mark-many-as-read experiment now triggers during discoveryWith the mark-many-as-read experiment enabled, a unit whose terraform { source = ... } pointed at a local module did not show up under --filter 'reading=' filters that referenced files inside that module. Discovery would parse the unit, but the module files were never recorded as read, so the reading filter attribute could not match and the queue came back empty.
The module walk now runs on the discovery code path as well, so changes to files in a local module source flow through to the units that depend on them.
terragrunt render no longer crashes on exclude or catalog blocks with certain attributesRendering a config crashed with a value has no attribute of that name panic before any output could be produced when:
exclude block set no_run, orcatalog block set default_template, no_shell, or no_hooks.These attributes are now carried through the render pipeline alongside the other fields on their respective blocks, so both blocks round-trip cleanly.
terragrunt render no longer crashes on multiple errors.ignore blocks with mismatched signalsRendering a config that defined more than one errors.ignore block crashed with an inconsistent list element types panic when the signals map was populated on one block and absent (or differently typed) on another. The same crash showed up in dependency-output evaluation, since both paths build the same rendered representation of the config.
Each ignore block is now rendered with a uniform shape. Indexed access (errors.ignore[0]), length, and iteration still work, and the signals map on each block is preserved as written.
Unknown variable: dependency errors during dependency resolutionterragrunt plan and apply no longer print ERROR Error: Unknown variable "dependency" lines when a unit pulls in a shared include (e.g. via find_in_parent_folders) that references dependency.* outputs. The plans completed correctly, but the error lines cluttered CI logs.
Resolves #6036.
terragrunt stack commands no longer crash on stacks with multiple unitsRunning terragrunt stack output (or any command that resolves concurrently parsing multiple configuration files) against a stack with several units could intermittently crash while the units were being parsed in parallel due to a race on internal bookkeeping of files read (used in the reading filter attribute).
Parallel unit parsing now coordinates safely when recording which source files were read, preventing crashes.
terraform_binary properly respected when both tofu and terraform are on PATHA regression in command execution caching resulted in over-caching the STDOUT result of tofu --version when both tofu and terraform were available on PATH and terraform_binary was set. Early on in the execution flow, Terragrunt checks if OpenTofu is installed what its version is to determine if it supports setting of the automatic provider cache directory. This resulted in the value of terraform_binary being ignored for later version checks to assess compliance with terraform_version_constraint.
The version-detection cache used per run is now scoped to the binary that produced each entry, so the version recorded against an early default-binary resolution no longer leaks into the later resolution that honors terraform_binary.
deep-merge experiment adds a deep_merge HCL functionEnable the new deep-merge experiment to use the deep_merge(map1, map2, ...) HCL function.
deep_merge recursively merges map and object values. Later arguments override earlier arguments for overlapping keys, nested maps are merged recursively, lists are appended, and null arguments are ignored.
This is useful when composing inputs from multiple decoded JSON, YAML, or HCL-derived maps:
locals {
config_json_files = sort(fileset(get_terragrunt_dir(), "*.json"))
config = deep_merge([
for file in local.config_json_files :
jsondecode(file("${get_terragrunt_dir()}/${file}"))
]...)
}
inputs = local.configCalling deep_merge without enabling the deep-merge experiment returns an error.
opt-out-auth — Opt out of --auth-provider-cmd during discoveryEnable the new opt-out-auth experiment to use --no-discovery-auth-provider-cmd (env: TG_NO_DISCOVERY_AUTH_PROVIDER_CMD), which disables the auth provider command during the discovery phase.
Without the flag, Terragrunt assumes that --auth-provider-cmd must be run per parsed component during the discovery phase so that it can reliably resolve HCL functions such as get_aws_account_id and run_cmd. On large repositories with run --all --filter='reading=', this dominates wall-clock time because the auth command runs for every discovered unit rather than only the subset that will run.
The --no-discovery-auth-provider-cmd flag turns off auth invocations during discovery. The auth provider command still runs normally when running units.
Units whose discovery-relevant blocks depend on credentials produced by --auth-provider-cmd will fail to parse with the flag set. Use it when you know that parsing will resolve successfully without any authentication done beforehand by Terragrunt.
While this flag is experimental, you must also opt-in to the opt-out-auth experiment by setting the TG_EXPERIMENT environment variable to opt-out-auth or by passing the --experiment=opt-out-auth flag to terragrunt run. This flag might experience breaking changes based on community feedback for the duration of the experiment.
e.g.
terragrunt run --all \
--experiment=opt-out-auth \
--no-discovery-auth-provider-cmd \
--queue-include-units-reading=./changed-file.txt \
plancatalog-redesign — Interactive scaffold form on sPressing s from the catalog list or detail view now opens an in-TUI form that prompts for every variable/value the selected component exposes. The form is modal: in navigate mode j and k (or the arrow keys) move between fields and enter interacts with the focused one. Required entries are flagged, and optional entries show their default in a muted style until the user opts in.
enter on a text or HCL field switches the form into edit mode. Typing edits the value in place; esc returns to navigate. Only fields the user actually changes get written to the generated file, and optional defaults stay implicit, so the result is leaner than the placeholder flow.
enter on a boolean field toggles between [x] true and [ ] false directly, without a separate edit mode.
x on an optional field marks it "use default" again, removing any in-progress value and leaving the source's default to apply.
Complex types (lists, maps, objects) accept raw HCL and are validated before the file is written, so a typo surfaces inline rather than producing a broken terragrunt.hcl or terragrunt.values.hcl file.
ctrl+d finishes the form. Required fields the user never set still write as # TODO: fill in value so the rest of the file is usable.
S (capital) keeps the previous placeholder-only flow, generating the same TODO-laden file as before for users who prefer to populate values by editing the generated file.
stack-dependencies: parser tolerates HCL expressions throughout terragrunt.stack.hclThe stack-dependencies experiment now defers evaluation of source, path, values, and include.path until each unit or stack block is parsed on its own. As a result, autoinclude resolution during stack generation and run --all discovery no longer fall over when other parts of a stack file use Terragrunt functions, local.*, or values.*. A few adjacent behaviors are tightened up at the same time.
Autoinclude resolves even when sibling units use expressions.
Before 1.0.6, if any unit in a stack file used a function call or a local.* / values.* reference in source, path, or values, generating an autoinclude on a different unit in the same file could fail. The parser now leaves those expressions alone until they're needed, so an unrelated unit can carry an autoinclude block without being blocked by its neighbors:
locals {
shared_region = "us-east-1"
}
unit "account" {
source = "${get_terragrunt_dir()}/../catalog/units/account"
path = "account"
values = {
account = values.account
region = local.shared_region
}
}
unit "roles" {
source = "${get_terragrunt_dir()}/../catalog/units/roles"
path = "roles"
autoinclude {
dependency "account" {
config_path = unit.account.path
}
}
}include blocks in terragrunt.stack.hcl accept computed paths.
The path attribute on an include block can be an HCL expression, not just a string literal. An autoinclude block in the included file is resolved normally after the include merges in:
include "shared" {
path = find_in_parent_folders("shared.stack.hcl")
}render WriteTo by @yhakbar in #6144setup-go from Windows signing by @yhakbar in #6158catalog scaffold by @yhakbar in #6162vfs to getter by @yhakbar in #6070--out-dir usage by @FernandoArteaga-telus in #6176mark-many-as-read for discovery by @yhakbar in #6174go-commons dependency by @yhakbar in #6180download_dir copy infinite recursion by @yhakbar in #6169Full Changelog: v1.0.5...v1.0.6
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →