Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
1.1.4→1.1.5Release Notes
gruntwork-io/terragrunt (terragrunt)
v1.1.5Compare Source
✨ New Features
duplicate-dependency-labelsalso catches a sharedconfig_pathTwo
dependencyblocks with different labels can point at the sameconfig_path. Both parse, so the same unit is declared twice, and the two blocks drift apart as soon as one gains amock_outputsorskip_outputsthe other lacks:Terragrunt now warns when it finds this, alongside the existing warning for two blocks sharing a label. With the
duplicate-dependency-labelsstrict control enabled, the warning becomes an error naming both addresses and the path they share:🏎️ Performance Improvements
Fewer remote probes for sources shared across units
run --allasked the remote what a source resolved to once per unit, so a hundred units sharing one module made a hundred requests. Each of them then read the same commit out of the store for itself.Units that resolve the same source at the same time now share one probe, and units that need the same Git commit share the work of reading it into the CAS.
Measured over 100 units pointing at one Git module, counting the Git commands a run spawns:
git ls-remoteThe last row needs the
offline-casexperiment described below; the rest apply to every run. Against a local Git server the first run went from roughly 5 seconds to 0.3, and a later run from 1 second to 0.1. A real remote makes each avoidedls-remoteworth more, since it costs a network round trip rather than a local process.The new
offline-casexperiment goes a step further and has the CAS record each probe answer in the store, so a later run can skip the request. How long it trusts an answer depends on the source:The experiment also unlocks three flags that change how the recorded answers are used:
--cas-offlinenever contacts a remote. Sources come from the local store and the recorded answers, and anything missing is an error rather than a fetch.--cas-refreshignores the recorded answers for one run and asks every remote again.--cas-probe-ttltrusts a changeable source's answer for a duration you choose, such as10m.See Recorded probes and the
offline-casexperiment.Faster first-time source downloads
The first time Terragrunt stores a repository in the Content Addressable Store (CAS), it copies the content of every file out of the clone. It used to launch a separate
gitprocess for each one, and on repositories with many files those launches dominated the time.Terragrunt now reads a repository's content through a single long-lived
gitprocess, and stores several files at a time.In benchmarks on an Apple M3 Max:
The saving grows with the number of files.
This applies when the CAS does not already hold the content, such as the first use of a new module version or a run against an empty store. Downloads that the CAS can already serve skipped this work before and are unchanged.
CAS store improvements
The CAS no longer writes a lock file beside each object it stores. A store had one lock file for every file and every directory listing it cached, so
~/.cache/terragrunt/casheld roughly twice as many entries as the cached content needed. Lock files already written stay where they are; deleting the store while no Terragrunt process is running against it reclaims them, and the store rebuilds without them.Terragrunt preserves a couple of files from a repository's
.gitdirectory when it materializes a Git source, and which files those are depends on the command. Those files used to be folded into the stored entry for the commit, so the first command to fetch a commit decided what every later command received from it: a commit first cached bystack generate, which asks for none of those files, left a later run against the same commit without them. Each file is now recorded against the commit on its own, and a command receives exactly the files it asked for whether the commit was already cached or not.A source pinned to a full commit SHA now asks the remote for that commit alone, one commit deep, instead of fetching every branch and tag with full history. Remotes that will not serve a commit by name, such as an older or locked-down server, still get the full fetch, so pinning keeps working everywhere. Where the remote does serve it, the first fetch of a large repository transfers the pinned commit and nothing else.
The numbers below come from micro-benchmarks run against a git server on the same machine. The fixture is a 500-commit history whose pinned commit sits 100 commits behind the tip.
Against a real remote the pinned fetch saves more than the table shows, since the objects it no longer asks for would also have to cross the network.
Faster dependents filters
A filter with
...before its target, such as...vpc, finds dependents by walking the directory tree around the target and parsing each configuration it passes to see whether it depends on the target. That walk parsed every configuration from scratch, even one Terragrunt had earlier in the same command, and it runs again from each dependent it finds. On a large repository, one query could read and parse the same unrelated unit once per dependent it selected.Terragrunt now reuses a configuration it has already parsed, so each unit is read from disk about once per query.
In benchmarks on an Apple M3 Max, querying the dependents of a unit from its own directory, where every other unit depends on it:
From the repository root, where the walk only has to rule out the units that do not depend on the target, the same query over a 1,024-unit repository went from 250ms to 131ms.
Faster file work, especially on small CI runners
Terragrunt frequently does a lot of small file operations at once: copying a module into its working directory, storing a repository in the Content Addressable Store (CAS), and materializing one back out. How many it ran at once scaled with the number of vCPUs seen by the Terragrunt process or the
--parallelismflag if configured.Terragrunt now picks that number by probing the filesystem it is about to write to to guess how much throughput it can handle to improve performance.
The gain is largest where the filesystem is much faster or slower than Terragrunt would expect, just scaling off vCPUs.
Materializing a 3,000 file repository on a 2 vCPU runner:
On a 16 vCPU machine, storing that repository for the first time is 19% faster on ext4 and 20% faster on btrfs.
terragrunt hcl fmtnow formats at most 8 files at once by default, which measured about 14% faster than one worker per CPU on a 16 core machine.Runs with the
fast-copystrict control enabled also copy module directories faster on macOS, by around 60% in benchmarks on an Apple M3 Max.Faster worktrees for Git filters
A Git-based filter, such as
--filter '[main...HEAD]', generates a worktrees to be able to runtofuin states that aren't reflected in the current worktree (e.g. when a unit is deleted, Terragrunt has to run aplan -destroyorapply -destroyin themainworktree, not theHEADworktree in the earlier example).As a conditional optimization, Terragrunt now reads the Git diff first and generates worktrees only when on-disk worktrees are necessary downstream.
For commands like
find,listorbrowseworktree generation can be skipped more aggressively, and even more performance improvements were made there.On a repository with 15,000 tracked files,
terragrunt find --filter '[HEAD~1...HEAD]'went from 4.7s to 0.4s on an M3 Max machine.Lower memory use during
run --allWhen you set
--json-out-dir, Terragrunt saves a JSON plan for every unit it runs. It used to build each of those documents in memory in full before writing any of it to disk, so a unit with a 64 MB plan needed roughly 168 MB to save it, and every unit running in parallel needed its own. Terragrunt now writes the document as it arrives. That same plan needs about 300 KB, roughly 550x less, and saving it finishes about 18% faster.Two other places held on to more than they needed. During
run --all plan, Terragrunt kept every unit's error output until the run finished so it could check it for a single message at the end, and it now checks that as the output streams. Responses from a provider registry were read twice on the way in, and are now read once, which uses about 19% less memory per request.JSON plans are also replaced atomically now. A run that fails part way through leaves the previous file in place instead of truncating it.
mutable = truesources are cloned instead of copiedA source marked
mutable = trueneeds a file of its own, because a hard link would hand out the store's read-only copy. Terragrunt now asks the filesystem for a copy-on-write clone of the stored file and copies only where the filesystem has none to give. APFS, btrfs, and XFS volumes with reflink support have one.A cloned target shares the stored content until you write to it, so it occupies disk space only for the parts you change. On those volumes, marking a source
mutablein every unit costs disk space only for what each unit edits.These micro-benchmarks time materializing an editable tree on APFS on an M3 Max, once copied as in earlier releases and once cloned.
A clone takes about the same time for a file of any size, while a copy takes longer the bigger the file. A tree of small files takes about 10ms longer to materialize, and a tree with large files materializes about six times faster.
Terragrunt no longer records what units read unless something needs it
Every parse used to record the files it read. Part of that record is the content of each local module a unit sources, so Terragrunt walked those module directories once per unit, on every command, whether or not anything would look at the result.
Only four things consult the record: reading-based filter expressions, the
--queue-include-units-readingflag,find --reading, and the file tree interragrunt browse. Terragrunt now keeps it for those and skips the module walk everywhere else.Benchmarks on an Apple M3 Max, across 1,000 units that all source the same local module:
find --dependenciesrender --allrender --allperforms the same full parse of each unit thatrun --allperforms before it invokes OpenTofu, so a run over units with large local modules saves comparable time before the first plan starts.The saving grows with the size of the local modules a repository sources, and the new times hold steady as those modules grow. Commands that do ask about reads behave as they did before.
Finding the repository root no longer launches
gitget_repo_root(),get_path_from_repo_root(),get_path_to_repo_root(), the runner, and discovery all need the root of the enclosing repository. Terragrunt used to ask Git for it by runninggit rev-parse --show-toplevel, and starting that process cost far more than producing the answer did.Terragrunt now finds the root itself, by looking for a
.gitentry in the working directory and each directory above it. Linked worktrees and submodules resolve the way they did before.In benchmarks on an Apple M3 Max, resolving one root, where depth is how many directories separate the starting point from the root:
Because Terragrunt no longer asks Git, some of Git's own settings for locating a repository stop applying.
GIT_CEILING_DIRECTORIESstill stops the search where it did.GIT_DIR,GIT_WORK_TREEandcore.worktreeare ignored, and thesafe.directoryownership check is not applied, soget_repo_root()now answers in a repository owned by another user where Git refuses. A path inside a bare repository still reports that there is no repository. This is assumed to be more expected from the perspective of a Terragrunt user, and usage ofgit rev-parse --show-toplevelfrom arun_cmdis still available otherwise. If this impacts your workflows, please open a bug report, and maintainers are happy to work with you on this.🐛 Bug Fixes
More generated files are written atomically
Terragrunt used to generate most files by opening the destination and writing into it, so the file spent time on disk half-written, and a run that failed partway through left a truncated one behind.
These now go to a temporary file that replaces the destination once it is complete:
generateblocksrender --write--report-file--debug.terraform.lock.hclbackendcommands no longer fail on unapplied dependenciesbackend bootstrap,backend migrateandbackend deleteused to read the whole configuration of every unit they touched, which meant fetching the outputs of everydependencyblock. Declaring a dependency on a unit you had not applied yet was enough to stop them with the "detected no outputs" error, even when nothing inremote_stateread that dependency.These commands now read only the
remote_stateblock and theterraformblock'ssource. They never fetch dependency outputs. Aremote_statethat does read a dependency output still resolves it, and still reports missing outputs when the dependency has not been applied.base64gzip()returns the v1.1.3 bytes againTerragrunt v1.1.4 was built with Go 1.27, which changed the compressed bytes produced by
base64gzip(). The bytes decompress to the same content, but a resource that compares the encoded value, such as an EC2 instance withuser_data_base64anduser_data_replace_on_change = true, planned a replacement after the upgrade.base64gzip()now returns the bytes it returned in v1.1.3 and earlier, so upgrading plans no change. Terragrunt warns once per run that this is legacy behavior. If you already applied the v1.1.4 output, every plan shows the encoded value changing back until you apply it or enable the strict control below, and a resource that depends on stability ofbase64gzipbytes is replaced by that apply.Terragrunt 1.2 will switch
base64gzip()to the new encoder by default. The newbase64gzip_compat()function, behind thebase64gzip-compatexperiment, returns the v1.1.3 bytes permanently (assuming the experiment eventually stabilizes), so call it where the encoded value must stay stable across upgrades. This function may be removed in a future release.To keep the current Go encoder's output now and silence the warning, enable the new
legacy-base64gzipstrict control:Deleted files in the CAS are fetched again instead of failing the run
When something removes a file from the Content Addressable Store (CAS) that a cached source still needs, Terragrunt now downloads that source again and restores what is missing, then carries on.
Terragrunt used to treat a cached source as complete once it had been downloaded, so a file deleted from the store afterwards ended the run with a read failure naming a path inside the store. Recovering meant clearing the store by hand.
A source that no longer supplies the missing content still fails, and now says which object the store is missing. The same is true of a
cas::reference in a stack file, which names stored content directly and has no source behind it to download again, and of a run under--cas-offline, which forbids the download that would restore the store.catalogsanitizes the content it draws from a repositoryterragrunt catalogbrowses repositories you point it at, and draws their titles, descriptions, tags and READMEs to the terminal as it finds them. Thecatalogcommand did not appropriately sanitize content from repositories to ensure that the content rendered correctly in terminals.catalognow sanitizes everything it draws, the wayterragrunt browsealready sanitized the files it previews. Control characters become the Unicode replacement character, so that content draws as visible placeholders.--format jsonland--format mdkeep the text as the repository wrote it.Fixed a crash in
terragrunt catalogwhen a repository cannot be reachedterragrunt catalognow reports the underlying git error when it cannot reach a repository listed in thecatalogblock. Previously, this could cause a crash part-way through loading. This affected any repository Terragrunt could not clone, e.g. an SSH URL with no usable key, a private repository without credentials, or a remote that timed out.find --dependencieslists dependencies in a stable orderWhen a unit had more than one dependency,
terragrunt find --dependencies --jsoncould report them in a different order on each run, with no change to the configuration.The order is now fixed.
list,dag graph, andbrowsesorted before rendering already, so their output is unchanged.Support backend assume_role during direct dependency state reads
With
dependency-fetch-output-from-stateenabled, direct S3 state reads now correctly chain the backend'sassume_roleonto the dependency's execution role. Previously, cross-account dependency state reads failed with403 AccessDeniedwhen theremote_stateblock configured a separateassume_rolefor state access.Dependency state read failures fall back
With the
dependency-fetch-output-from-stateexperiment enabled, network, permissions, and parsing failures from a direct dependency state read could end a run that worked through native output retrieval.Outside
renderandrender-json, Terragrunt now retries failed direct reads withtofu outputorterraform output. If native output retrieval succeeds, the run continues and only the direct-read speedup is lost. Missing state and the two render commands retain their existing mock-output behavior.This fallback also covers OpenTofu client-side state encryption. Terragrunt recognizes the encrypted envelope and retries output retrieval through the configured binary instead of treating the dependency as having no outputs. If that binary can decrypt the state and native output retrieval succeeds, only the speedup is lost.
renderandrender-jsonstill require--no-dependency-fetch-output-from-statewhen they must resolve real outputs from encrypted state.Current flag names take precedence over deprecated ones
A setting given under both its current name and a deprecated one took the deprecated value whatever the source of each, so
TERRAGRUNT_LOG_LEVEL=debugin the environment overrodeTG_LOG_LEVEL=infoset beside it.A command-line argument now beats an environment variable under either name, and at the same level the current name beats the deprecated one. A
--terragrunt-*argument still overrides aTG_*variable from the environment, so a script mixing the two keeps working.execaccepts--source,--source-map, and--no-auto-initterragrunt execrejected--source,--source-map, and--no-auto-initas invalid flags, one message per flag:flag `--source-map` is not a valid flag for `exec`. It reads configuration and downloads source the same wayrundoes, so there was no way to pointexecat a local copy of a module, or to stop it from runninginit. All three flags are now registered onexec.terragrunt exec --source-map git::ssh://git@github.com/acme/modules.git=/local/modules -- tfmigrate planexectherefore also readsTG_SOURCE,TG_SOURCE_MAP, andTG_NO_AUTO_INIT, along with the deprecatedTERRAGRUNT_SOURCE,TERRAGRUNT_SOURCE_MAP, andTERRAGRUNT_AUTO_INIT, which it previously ignored. If you export any of those forrun,execstarts honoring them too.--no-auto-initreaches the unitexectargets only under--in-download-dir, sinceexecotherwise never runsinitfor it. It also reaches units named independencyblocks, with or without that flag, because Terragrunt initializes a dependency when resolving its outputs requires it.Direct GCS state reads work with Workload Identity Federation
With the
dependency-fetch-output-from-stateexperiment enabled, a GCS backend authenticated through Workload Identity Federation still rantofu outputorterraform outputfor every dependency, so the experiment made no difference.It affected any credentials file of type
external_account, which is whatgoogle-github-actions/authwrites and pointsGOOGLE_APPLICATION_CREDENTIALSat. Terragrunt read onlyservice_accountandauthorized_userfiles directly.Terragrunt now reads
external_accountcredentials files directly, including the service-account impersonation thatgoogle-github-actions/authconfigures when you give it a service account. A direct read requires the file'scredential_sourceto be one of:urlfilewith an absolute pathAny other
credential_sourcekeeps the previous behavior, and the dependency still runstofu outputorterraform output. Reading those directly would use Terragrunt's own process rather than the unit's environment to resolve the identity:executablewould run the command with Terragrunt's environment.environment_idsuch asaws1) would use Terragrunt's AWS credentials.filewith a relative path would resolve against Terragrunt's working directory.The
impersonate_service_accountbackend setting is a separate feature and is not affected. Backends that set it still runtofu outputorterraform output.Files from
generateblocks are created as0600A
generateblock writes files for Terragrunt and the processes it spawns, all of which run as the user who ran Terragrunt. Creating them as0644granted read access that nothing uses.They are now created as
0600. Under themutable-generateexperiment, a block withoutmutable = truegets a read-only link to a copy shared between working directories, and Terragrunt stores new content as0400rather than0444. Withmutable = true, the block keeps a writable0600file of its own.Content the CAS is already holding keeps the permissions it was stored with, since changing them would change every file linked to that copy. Those files stay
0444until the cache is cleared. Run with--log-level debugto see which ones.EC2 instance role credentials work again from inside a container
Since v1.1.4, Terragrunt running in a container on an EC2 instance could fail to use the instance's IAM role when the instance metadata service has a hop limit of 1. Runs failed with:
In that setup the IMDSv2 token request never gets an answer. Terragrunt v1.1.4 waited on it until the whole credential lookup timed out, so the IMDSv1 fallback that v1.1.3 and earlier relied on never ran.
Terragrunt now gives up on the IMDSv2 token request quickly and falls back to IMDSv1, as it did before v1.1.4. No configuration change is needed.
IAM role credentials are reused for
--json-out-dirplan exportAfter
v1.1.4,run --all planwith an IAM role and--json-out-dircould make a secondsts:AssumeRolerequest. That request used the role session itself and failed withAccessDeniedunless the role trusted itself.Terragrunt now caches the assumed session in-process until five minutes before it expires (default session length is one hour when
--iam-assume-role-durationis unset), keyed by role configuration and source identity, so the JSON export reuses the first assumption. Setting--iam-assume-role-durationwas already a working workaround and remains supported.If a session cannot be refreshed but has not yet expired, Terragrunt logs a warning and continues with the cached credentials rather than failing the run.
Provider Cache Server reads only the running implementation's CLI config files
In v1.1.4, the Provider Cache Server started reading OpenTofu's CLI config file locations (
~/.tofurcand$XDG_CONFIG_HOME/opentofu/tofurc) regardless of which binary Terragrunt was running. A machine with a stray~/.tofurc(for example, one declaring anetwork_mirror) could breakterragrunt initfor Terraform users with errors like:Terragrunt now detects whether the configured binary is OpenTofu or Terraform before starting the cache server and reads only that implementation's CLI config files:
~/.tofurc,~/.terraformrc,$XDG_CONFIG_HOME/opentofu/tofurc(on Windows:%APPDATA%\tofu.rc, then%APPDATA%\terraform.rc).~/.terraformrc(%APPDATA%\terraform.rcon Windows).*.tfrcand*.tfrc.jsonfragments from the CLI config directory:~/.terraform.d(%APPDATA%\terraform.don Windows), or$XDG_CONFIG_HOME/opentofufor OpenTofu when~/.terraform.ddoes not exist.TF_CLI_CONFIG_FILEcontinues to override the config file location for both implementations.The same selection applies to the credentials read for module registry downloads and version-constraint resolution, kept separately per implementation within a single run.
The implementation is detected from the binary Terragrunt is configured to run at startup (
--tf-path,TG_TF_PATH, or the first oftofu/terraformfound onPATH); aterraform_binarysetting inside a unit's configuration does not change which files the cache server reads. When a run's implementation differs from the one the cache server was configured for, and the two implementations would read different CLI config files on that machine, that run skips the provider cache and uses its own CLI configuration, and Terragrunt prints a warning when such a run initializes providers (initorproviders lock). Both implementations resolve to the same files when none of the implementation-specific files above exist, or whenTF_CLI_CONFIG_FILEnames the file. Every run then uses the cache whichever binary it runs. If detection fails, Terragrunt falls back to OpenTofu's file locations.Run report includes cause for units that fail before OpenTofu/Terraform
Units that failed during config evaluation or dependency output resolution were reported as a run error with an empty cause. The report now records the underlying error text in
Cause.Thanks to @Tensho for contributing this fix!
Fixed S3-compatible source downloads with environment credentials
Downloading unit sources from S3-compatible services (
s3::https://minio.example.com/...) now works when credentials are supplied via environment variables (AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) or IAM roles rather than embedded in the URL query string. Previously, the custom endpoint was only pinned when credentials were present in the URL, causing the AWS SDK to redirect requests toamazonaws.comand fail withInvalidAccessKeyId.Stack dependencies include outputs from nested stacks
A
dependencypointing at a directory that holds aterragrunt.stack.hclnow reads the outputs of units generated by that stack's nestedstackblocks. The run queue already waited for those units, but their outputs were missing from the dependency.Each nested stack adds a level named after it, the same address
terragrunt stack outputgives those units:mock_outputsentries for a nested stack's units nest under the stack's name the same way.A unit and a nested stack with the same name in one stack file share an address, so a dependency on that stack now errors once both have outputs to read.
terragrunt stack outputalready rejects the same configuration. Rename one of the two blocks to give each its own address.🧪 Experiments Added
base64gzip-compatexperiment adds abase64gzip_compatHCL functionEnable the new
base64gzip-compatexperiment to use thebase64gzip_compat(str)HCL function.base64gzip_compatreturns the valuebase64gzipreturned in Terragrunt v1.1.3 and earlier, and keeps returning it after Terragrunt 1.2 switchesbase64gzip()to the current Go encoder. Use it where the encoded value must stay stable across upgrades:Calling
base64gzip_compatwithout enabling thebase64gzip-compatexperiment returns an error. The name may still change to match OpenTofu.offline-casgates the CAS probe cacheThe
offline-casexperiment has been added as the gate for the probe cache the CAS keeps, in which it records what each source resolved to so a later run can skip asking the remote.Enabling the experiment turns the cache on and unlocks three flags that change how its answers are used:
--cas-offline,--cas-refresh, and--cas-probe-ttl. Setting one of them without the experiment returns an error naming the flag.Without the experiment nothing is recorded or served, and every run probes every source, as before.
See the experiment documentation for what each flag does and what has to land before it stabilizes.
tg-loginreserved for signing in to the Gruntwork Developer PortalThe
tg-loginexperiment has been added as the gate forterragrunt login, a command for signing in to the Gruntwork Developer Portal. Once it lands, signing in letsterragrunt catalogread the repositories your organization selected in the portal rather than acatalogblock you maintain yourself.In this release the flag is reserved only. Enabling it has no effect, and no command reads it.
See the experiment documentation for what is planned and what has to land before it stabilizes.
🧪 Experiments Updated
azure-backendcan assign the blob data role during bootstrapCreating an Azure storage account grants no access to the blobs inside it, so an identity using
use_azuread_authcould bootstrap the backend and then fail to read state as unauthorized until someone granted the data-plane role by hand.With the
azure-backendexperiment enabled,assign_blob_data_role = truenow has bootstrap grant Storage Blob Data Contributor on the storage account:The role goes to the identity Terragrunt authenticated as, resolved from the access token it already holds rather than from a directory lookup, so it works for identities that cannot read Microsoft Entra. Set
principal_idto grant the role to a different user, group, or service principal.Existing assignments are detected and left alone, so reruns need only read permission on role assignments.
The setting is opt-in: creating a role assignment requires
Microsoft.Authorization/roleAssignments/write, which Contributor does not include. Leaving it unset preserves the previous behavior of assigning nothing.expansionblocks now iteratedependency,unit, andstackblocksWith the
block-iterationexperiment enabled, adependency,unit, orstackblock can have anexpansionblock declaring acountor afor_each. Terragrunt reads the block once per element, producing one dependency, unit, or stack for each:You address each element by its key. An expanded dependency is read as
dependency.aurora["web"].outputs.id, andterragrunt stack output 'aurora["web"].role'reaches one element of an expanded unit.Adding an expansion to a block that did not have one therefore changes its address, and shrinking a
for_eachor lowering acountremoves addresses. Terragrunt has nomovedequivalent, so nothing records the rename for you: references andstack outputscripts need updating by hand, and state left behind at an address that no longer exists has to be destroyed deliberately.The experiment also enables an
enabledattribute onunitandstackblocks. Setting it tofalsedrops the component from stack generation and fromterragrunt stack output, and leaves every other address alone.dependencyblocks acceptenabledwithout the experiment.See the
expansionblock reference for the rules, the addressing scheme, and how to clean up state left behind when an expansion shrinks.symlinksexperiment:include_in_copycopies the contents of symlinked directories againIn v1.1.4, files behind a symlinked directory named in
include_in_copywere not copied into the OpenTofu/Terraform working directory, so they were missing from.terragrunt-cache.exclude_from_copypatterns reaching through a symlinked directory also excluded nothing.With the
symlinksexperiment enabled (--experiment symlinksorTG_EXPERIMENT=symlinks), patterns rooted at a symlinked directory expand through the link again, for bothinclude_in_copyandexclude_from_copy, as in v1.1.3 and earlier. Without the experiment, the v1.1.4 behavior is unchanged.A link that points back at a directory already being copied, or at a parent of one, such as a link to the unit directory itself, is skipped. Terragrunt logs a warning naming the link when that happens.
renderpreviews an expandeddependencyblock written in JSONWith the
block-iterationexperiment enabled, a configuration written in JSON now renders the same way an HCL one does. It has no HCL to quote, so Terragrunt writes the block as the HCL that means the same thing and previews the elements underneath it:$ cat terragrunt.hcl.json {"dependency": {"shard": { "expansion": {"count": 2}, "config_path": "../shard-${count.index}" }}} $ terragrunt render --experiment block-iteration dependency "shard" { expansion { count = 2 } config_path = "../shard-${count.index}" } # Expands to: # # dependency "shard" { # config_path = "../shard-0" # } # # dependency "shard" { # config_path = "../shard-1" # }Previously the elements rendered as ordinary blocks, which repeated one label. Terragrunt warns about that and rejects it under the
duplicate-dependency-labelsstrict control, so the rendered file did not read back.--format jsonno longer drops the elements either. Itsdependencymap is keyed by label, which every element shares, so it kept whichever element came last. JSON has no comment to preview the elements in, so it now emits the block as it was written, references and all:$ terragrunt render --format json --experiment block-iteration { "dependency": { "shard": { "expansion": { "count": 2 }, "config_path": "../shard-${count.index}", "skip_outputs": true } } }Whichever syntax you write and whichever format you ask for, rendering the output again returns it unchanged.
Expanded units keep their own outputs when a whole stack is a dependency
With the
block-iterationexperiment enabled, adependencypointing at a directory that holds aterragrunt.stack.hclcollected the outputs of an expandedunitunder the block's bare label. Every element wrote to that one label, so only the last one survived, and reading it returned another element's outputs.Each element is now reachable under its own key, matching the address
terragrunt stack outputalready gives it:A unit that declares no
expansionis still read asdependency.networking.outputs.vpc.id.Pull Requests
✨ Features
tg-loginexperiment by @yhakbar in #6759🐛 Bug Fixes
render --jsonexpansion logic by @yhakbar in #6763🏎️ Performance
generatescript by @yhakbar in #6780git cat-file --batchinstead of multiplegit cat-filecalls per blob by @yhakbar in #6838git rev-parse --show-toplevelas a Go func by @yhakbar in #6867sync.Poolfor hot buffers by @yhakbar in #6769📖 Documentation
dag graphby @yhakbar in #6788block-iterationdocumentation by @yhakbar in #6844sign-commitsto the use ofpeter-evans/create-pull-requestfor docs clean-up PRs by @yhakbar in #6891🧹 Chores
go fix ./...on all tags by @yhakbar in #6766TestUnitPathsFromStackDir_DepthCapReturnsErrortest by @yhakbar in #6781find --dependencies --jsonby @yhakbar in #6800for_eachin the expansion engine tests by @yhakbar in #6829nolintlintby @yhakbar in #6803Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Renovate Bot.