๐ v1.1.0 Release Candidate
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-display
Future 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.
๐ Changes since rc1
๐ Bug Fixes
mark_glob_as_readconstrains 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-boundaryargument to set the boundary explicitly. (#6351)- Git-based filters select units reading added or deleted glob files. Filters like
--filter '[HEAD^1...HEAD]'now select units that read an added or deleted file throughmark_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_casis rejected when CAS is disabled.terragrunt stack generate --no-casnow fails when a generated unit'sterraformblock setsupdate_source_with_cas = true, instead of silently emitting an unresolvable relativesource. This matches the existing behavior for the same attribute onunitandstackblocks. (#6363)
- Git-based filters select units reading added or deleted glob files. Filters like
โ๏ธ Process Updates
- Immutable releases. Starting with v1.1.0-rc1, Terragrunt releases are published as immutable releases on GitHub. Once published, a release's tag and assets can no longer be modified or deleted. (#6337)
- Install script verifies release attestations. The install script now checks downloaded assets against the release attestation that ships with immutable releases. When an authenticated GitHub CLI (v2.81.0 or later) is available, it verifies the checksums file and binary before installing, and aborts on a mismatch. Use
--no-verify-attestationto opt out. (#6344)
โจ New Features
Stack dependencies
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:
includeblocks now work interragrunt.stack.hclfiles, so shared stack configuration can live in a parent folder.dependencyblocks 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.
Content Addressable Store (CAS)
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_casmakes a generated stack self-contained. Set it on aunit,stack, orterraformblock with a relativesource, andterragrunt stack generaterewrites that source into a content-addressedcas::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 }
-
mutablecontrols how the CAS places fetched content on disk. By default, the CAS hard links files from its shared store into.terragrunt-cacheand marks them read-only, which is fast and uses no extra space, but means the files can't be edited in place. Setmutable = trueon aterraformblock 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.
Redesigned terragrunt catalog
The 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.
Reading detection for local module sources
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.
Skip auth during discovery with --no-discovery-auth-provider-cmd
By 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.
Run queue displayed as a dependency tree
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.
๐ Bug Fixes
Fix permission denied when generated files overwrite CAS-materialized files
With the CAS enabled, Terragrunt fetches sources as read-only files. Writing a generated file over one of them no longer fails with permission denied:
- Files from
generateblocks withif_exists = "overwrite", when the module ships the target file (for example, its ownversions.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 duringinit -upgrade.
In each case, the read-only file is replaced with a writable one, and the shared CAS store is never modified.
Reject update_source_with_cas on a terraform block 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 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.
Select units reading added or deleted glob files in Git-based filters
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 boundary
mark_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}")
}Resolve interpolated object keys in autoinclude blocks
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.
Fix panic on non-string literal interpolation in autoinclude templates
terragrunt 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.
๐งช Experiments Updated
Six experiments completed
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-display
Each 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.
โ๏ธ Process Updates
Immutable releases
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.
Install script verifies release attestations
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.
Pull Requests
๐ Bug Fixes
- fix: autoincludes variables interpolation by @denis256 in #6318
- fix: generate overwrite of read-only CAS-materialized files by @denis256 in #6329
- fix: CAS integration with committed module lockfiles by @yhakbar in #6330
- fix: improved resolving of complex objects in keys by @denis256 in #6317
- fix: Fixing
update_source_with_casintegration with--no-casby @yhakbar in #6363 - fix: Adding support for adding/deleting files in Git diffs by @yhakbar in #6352
- fix: Adding
--terragrunt-boundarytomark_glob_as_readby @yhakbar in #6351
๐ Documentation
- docs: Documenting
--parallelismtweaking considerations better by @yhakbar in #6313 - docs: Adding TGS 'Terragrunt at scale' page by @yhakbar in #6307
- docs:
v1.1.0changelog polish by @yhakbar in #6333 - docs: Improving performance docs by @yhakbar in #6332
- docs: Adding immutable releases docs by @yhakbar in #6337
- docs: Cleaning up 1.1.0 docs by @yhakbar in #6339
- docs: Updating provider size claims for provider cache server docs by @yhakbar in #6341
- docs: Documenting Discovery as a term by @yhakbar in #6359
- docs: Cleaning up catalog tabs docs by @yhakbar in #6369
๐งน Chores
- chore: marking as completed stack dependencies experiment by @denis256 in #6249
- chore: Completing
mark-many-as-readexperiment by @yhakbar in #6310 - chore: Completing
casexperiment by @yhakbar in #6254 - chore: Addressing feedback from #6254 by @yhakbar in #6324
- chore: Completing
dag-queue-displayexperiment by @yhakbar in #6320 - chore: Completing
opt-out-authexperiment by @yhakbar in #6321 - chore: Dropping
go-gitby @yhakbar in #6325 - chore: Addressing PR #6325 feedback by @yhakbar in #6335
- chore: bump cicd to use opentofu 1.12.2 by @denis256 in #6343
- chore: Signing GHA update by @denis256 in #6340
- chore: multiple dependencies update by @denis256 in #6356
- chore: Updating CI w/ Terragrunt guide to have more accurate screenshots by @yhakbar in #6357
- chore: drop usage of github.com/NYTimes/gziphandler by @denis256 in #6364
- chore(deps): bump astro from 6.3.2 to 6.4.6 in /docs by @dependabot[bot] in #6366
- chore: Completing
catalog-redesignexperiment by @yhakbar in #6271 - chore: Bumping JS dependencies by @yhakbar in #6368
- chore: Adding release attestation verification to install script by @yhakbar in #6344
- chore: Addressing weekly test stats by @yhakbar in #6354